diff options
Diffstat (limited to 'src/main/java/gregtech/api')
517 files changed, 105502 insertions, 0 deletions
diff --git a/src/main/java/gregtech/api/GregTech_API.java b/src/main/java/gregtech/api/GregTech_API.java new file mode 100644 index 0000000000..e80bc7345b --- /dev/null +++ b/src/main/java/gregtech/api/GregTech_API.java @@ -0,0 +1,1082 @@ +package gregtech.api; + +import static gregtech.api.enums.GT_Values.B; +import static gregtech.api.enums.GT_Values.L; +import static gregtech.api.enums.GT_Values.M; +import static gregtech.api.enums.Mods.IndustrialCraft2; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.BiFunction; +import java.util.function.IntFunction; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +import javax.annotation.Nonnull; + +import net.minecraft.block.Block; +import net.minecraft.client.renderer.texture.IIconRegister; +import net.minecraft.creativetab.CreativeTabs; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.world.World; + +import com.google.common.collect.Multimap; +import com.google.common.collect.Multimaps; +import com.google.common.collect.SetMultimap; + +import cpw.mods.fml.common.registry.GameRegistry; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import gregtech.GT_Mod; +import gregtech.api.enums.GT_Values; +import gregtech.api.enums.Materials; +import gregtech.api.enums.SoundResource; +import gregtech.api.enums.Textures; +import gregtech.api.interfaces.IDamagableItem; +import gregtech.api.interfaces.ITexture; +import gregtech.api.interfaces.internal.IGT_RecipeAdder; +import gregtech.api.interfaces.internal.IThaumcraftCompat; +import gregtech.api.interfaces.metatileentity.IMetaTileEntity; +import gregtech.api.interfaces.tileentity.IMachineBlockUpdateable; +import gregtech.api.items.GT_CoolantCellIC_Item; +import gregtech.api.items.GT_CoolantCell_Item; +import gregtech.api.items.GT_Tool_Item; +import gregtech.api.metatileentity.BaseMetaTileEntity; +import gregtech.api.objects.GT_Cover_Default; +import gregtech.api.objects.GT_Cover_None; +import gregtech.api.objects.GT_HashSet; +import gregtech.api.objects.GT_ItemStack; +import gregtech.api.threads.GT_Runnable_Cable_Update; +import gregtech.api.threads.GT_Runnable_MachineBlockUpdate; +import gregtech.api.util.GT_CircuitryBehavior; +import gregtech.api.util.GT_Config; +import gregtech.api.util.GT_CoverBehavior; +import gregtech.api.util.GT_CoverBehaviorBase; +import gregtech.api.util.GT_CreativeTab; +import gregtech.api.util.GT_Log; +import gregtech.api.util.GT_ModHandler; +import gregtech.api.util.GT_OreDictUnificator; +import gregtech.api.util.GT_Utility; +import gregtech.api.util.item.ItemHolder; +import gregtech.api.world.GT_Worldgen; +import gregtech.common.GT_DummyWorld; +import gregtech.common.items.GT_IntegratedCircuit_Item; + +/** + * Please do not include this File in your Mod-download as it ruins compatibility, like with the IC2-API You may just + * copy those Functions into your Code, or better call them via reflection. + * <p/> + * The whole API is the basic construct of my Mod. Everything is dependent on it. I change things quite often so please + * don't include any File inside your Mod, even if it is an Interface. Since some Authors were stupid enough to break + * this simple Rule, I added Version checks to enforce it. + * <p/> + * In these Folders are many useful Functions. You can use them via reflection if you want. I know not everything is + * compilable due to APIs of other Mods, but these are easy to fix in your Setup. + * <p/> + * You can use this to learn about Modding, but I would recommend simpler Mods. You may even copy-paste Code from these + * API-Files into your Mod, as I have nothing against that, but you should look exactly at what you are copying. + * + * @author Gregorius Techneticies + */ +@SuppressWarnings("unused") // API class has legitimately unused methods and members +public class GregTech_API { + + /** + * @deprecated Use {@link GT_Values#M} + */ + @Deprecated + public static final long MATERIAL_UNIT = M; + /** + * @deprecated Use {@link GT_Values#L} + */ + @Deprecated + public static final long FLUID_MATERIAL_UNIT = L; + /** + * Fixes the HashMap Mappings for ItemStacks once the Server started + * <br> + * <br> + * NOTE: We use wildcards generics for the key because it could be for example {@link ItemStack} or + * {@link GT_ItemStack} + */ + public static final Collection<Map<?, ?>> sItemStackMappings = new ArrayList<>(); + public static final Collection<SetMultimap<? extends ItemHolder, ?>> itemStackMultiMaps = new ArrayList<>(); + + /** + * The MetaTileEntity-ID-List-Length + */ + public static final short MAXIMUM_METATILE_IDS = Short.MAX_VALUE - 1; + /** + * My Creative Tab + */ + public static final CreativeTabs TAB_GREGTECH = new GT_CreativeTab("Main", "Main"), + TAB_GREGTECH_MATERIALS = new GT_CreativeTab("Materials", "Materials"), + TAB_GREGTECH_ORES = new GT_CreativeTab("Ores", "Ores"); + /** + * A List of all registered MetaTileEntities + * <p/> + * 0 - 749 are used by GregTech. + * 750 - 999 are reserved for Alkalus. + * 1000 - 2047 are used by GregTech. + * 2048 - 2559 are reserved for OvermindDL. + * 2560 - 3071 are reserved for Immibis. + * 3072 - 3583 are reserved for LinusPhoenix. + * 3584 - 4095 are reserved for BloodyAsp. + * 4096 - 5095 are used for GregTech Frames. + * 5096 - 6099 are used for GregTech Pipes. + * 6100 - 8191 are used for GregTech Decoration Blocks. + * 8192 - 8703 are reserved for ZL123. + * 8704 - 9215 are reserved for Mr10Movie. + * 9216 - 9727 are used for GregTech Automation Machines. + * 9728 - 10239 are reserved for 28Smiles. + * 10240 - 10751 are reserved for VirMan. + * 10752 - 11263 are reserved for Briareos81. + * 11264 - 12000 are reserved for Quantum64. + * 12001 - 12500 are reserved for RedMage17. + * 12501 - 13000 are reserved for bartimaeusnek. + * 13001 - 13100 are reserved for Techlone. + * 13101 - 13500 are reserved for kekzdealer. + * 13501 - 14000 are reserved for glee8e. + * 14001 - 14100 are reserved for glowredman. + * 14101 - 14200 are reserved for MuXiu1997. + * 14201 - 14300 are reserved for kuba6000. + * 14301 - 14999 are currently free. + * 15000 - 16999 are reserved for TecTech. + * 17000 - 29999 are currently free. + * 30000 - 31999 are reserved for Alkalus. + * 32001 - 32766 are reserved for Glod. + * <p/> + * Contact me if you need a free ID-Range, which doesn't conflict with other Addons. You could make an ID-Config, + * but we all know what "stupid" customers think about conflicting ID's + */ + public static final IMetaTileEntity[] METATILEENTITIES = new IMetaTileEntity[MAXIMUM_METATILE_IDS]; + /** + * The Icon List for Covers + */ + public static final Map<GT_ItemStack, ITexture> sCovers = new ConcurrentHashMap<>(); + /** + * The List of Cover Behaviors for the Covers + */ + public static final Map<GT_ItemStack, GT_CoverBehaviorBase<?>> sCoverBehaviors = new ConcurrentHashMap<>(); + /** + * The List of Circuit Behaviors for the Redstone Circuit Block + */ + public static final Map<Integer, GT_CircuitryBehavior> sCircuitryBehaviors = new ConcurrentHashMap<>(); + /** + * The List of Blocks, which can conduct Machine Block Updates + */ + public static final Map<Block, Integer> sMachineIDs = new ConcurrentHashMap<>(); + /** + * The Redstone Frequencies + */ + public static final Map<Integer, Byte> sWirelessRedstone = new ConcurrentHashMap<>(); + /** + * The Advanced Redstone Frequencies + */ + public static final Map<String, Map<Integer, Map<Long, Byte>>> sAdvancedWirelessRedstone = new ConcurrentHashMap<>(); + + /** + * The IDSU Frequencies + */ + public static final Map<Integer, Integer> sIDSUList = new ConcurrentHashMap<>(); + /** + * A List of all Books, which were created using @GT_Utility.getWrittenBook the original Title is the Key Value + */ + public static final Map<String, ItemStack> sBookList = new ConcurrentHashMap<>(); + /** + * The List of all Sounds used in GT, indices are in the static Block at the bottom + * + * @deprecated Use {@link SoundResource} + */ + @Deprecated + public static final Map<Integer, String> sSoundList = SoundResource.asSoundList(); + /** + * The List of Tools, which can be used. Accepts regular damageable Items and Electric Items + */ + public static final GT_HashSet<GT_ItemStack> sToolList = new GT_HashSet<>(), sCrowbarList = new GT_HashSet<>(), + sScrewdriverList = new GT_HashSet<>(), sWrenchList = new GT_HashSet<>(), sSoftHammerList = new GT_HashSet<>(), + sHardHammerList = new GT_HashSet<>(), sWireCutterList = new GT_HashSet<>(), + sSolderingToolList = new GT_HashSet<>(), sSolderingMetalList = new GT_HashSet<>(), + sJackhammerList = new GT_HashSet<>(); + /** + * The List of Hazmat Armors + */ + public static final GT_HashSet<GT_ItemStack> sGasHazmatList = new GT_HashSet<>(), + sBioHazmatList = new GT_HashSet<>(), sFrostHazmatList = new GT_HashSet<>(), + sHeatHazmatList = new GT_HashSet<>(), sRadioHazmatList = new GT_HashSet<>(), + sElectroHazmatList = new GT_HashSet<>(); + + private static final Multimap<Integer, ItemStack> sRealConfigurationList = Multimaps + .newListMultimap(new TreeMap<>(), ArrayList::new); + private static final Map<Integer, List<ItemStack>> sConfigurationLists = new ConcurrentHashMap<>(); + private static final Map<Predicate<ItemStack>, BiFunction<ItemStack, EntityPlayerMP, ItemStack>> sRealCircuitProgrammerList = new LinkedHashMap<>(); + public static final Map<Predicate<ItemStack>, BiFunction<ItemStack, EntityPlayerMP, ItemStack>> sCircuitProgrammerList = Collections + .unmodifiableMap(sRealCircuitProgrammerList); + + /** + * The List of Dimensions, which are Whitelisted for the Teleporter. This list should not contain other Planets. + * Mystcraft Dimensions and other Dimensional Things should be allowed. Mystcraft and Twilight Forest are + * automatically considered a Dimension, without being in this List. + */ + public static final Collection<Integer> sDimensionalList = new HashSet<>(); + /** + * Lists of all the active World generation Features, these are getting Initialized in Postload! + */ + public static final List<GT_Worldgen> sWorldgenList = new ArrayList<>(); + /** + * A List containing all the Materials, which are somehow in use by GT and therefor receive a specific Set of Items. + */ + public static final Materials[] sGeneratedMaterials = new Materials[1000]; + /** + * This is the generic Cover behavior. Used for the default Covers, which have no Behavior. + */ + public static final GT_CoverBehavior sDefaultBehavior = new GT_Cover_Default(), sNoBehavior = new GT_Cover_None(); + /** + * For the API Version check + */ + public static volatile int VERSION = 509; + + /** + * @deprecated Use {@link GT_Values#RA} + */ + @SuppressWarnings("DeprecatedIsStillUsed") // Still need be initialized for backward compat + @Deprecated + public static IGT_RecipeAdder sRecipeAdder; + /** + * Registers Aspects to Thaumcraft. This Object might be {@code null} if Thaumcraft isn't installed. + */ + public static IThaumcraftCompat sThaumcraftCompat; + /** + * The Lists below are executed at their respective timings. Useful to do things at a particular moment in time. + * The Lists are not Threaded - a native Java interface is used for their execution. + * Add your "commands" in the constructor or in the static-code-block of your mod's Main class. + * Implement the method {@code run()}, and everything should work. + */ + public static List<Runnable> sBeforeGTPreload = new ArrayList<>(), sAfterGTPreload = new ArrayList<>(), + sBeforeGTLoad = new ArrayList<>(), sAfterGTLoad = new ArrayList<>(), sBeforeGTPostload = new ArrayList<>(), + sAfterGTPostload = new ArrayList<>(), sFirstWorldTick = new ArrayList<>(), + sBeforeGTServerstart = new ArrayList<>(), sAfterGTServerstart = new ArrayList<>(), + sBeforeGTServerstop = new ArrayList<>(), sAfterGTServerstop = new ArrayList<>(), + sGTBlockIconload = new ArrayList<>(), sGTItemIconload = new ArrayList<>(), sGTCompleteLoad = new ArrayList<>(); + /** + * The Icon Registers from Blocks and Items. They will get set right before the corresponding Icon Load Phase as + * executed in the Runnable List above. + */ + @SideOnly(Side.CLIENT) + public static IIconRegister sBlockIcons, sItemIcons; + /** + * The Configuration Objects + */ + public static GT_Config sMachineFile = null, sWorldgenFile = null, sMaterialProperties = null, sUnification = null, + sSpecialFile = null, sClientDataFile, sOPStuff = null; + + public static int TICKS_FOR_LAG_AVERAGING = 25, MILLISECOND_THRESHOLD_UNTIL_LAG_WARNING = 100; + /** + * Initialized by the Block creation. + */ + public static Block sBlockMachines; + + public static Block sBlockOres1, sBlockOresUb1, sBlockOresUb2, sBlockOresUb3, + /* sBlockGem, */ + sBlockMetal1, sBlockMetal2, sBlockMetal3, sBlockMetal4, sBlockMetal5, sBlockMetal6, sBlockMetal7, sBlockMetal8, + sBlockMetal9, sBlockGem1, sBlockGem2, sBlockGem3, sBlockReinforced; + public static Block sBlockGranites, sBlockConcretes, sBlockStones; + public static Block sBlockCasings1, sBlockCasings2, sBlockCasings3, sBlockCasings4, sBlockCasings5, sBlockCasings6, + sBlockCasings8, sBlockCasings9, sSolenoidCoilCasings; + public static Block sBlockLongDistancePipes; + public static Block sDroneRender; + /** + * Getting assigned by the Config + */ + public static boolean sTimber = true, sDrinksAlwaysDrinkable = false, sMultiThreadedSounds = false, + sDoShowAllItemsInCreative = false, sColoredGUI = true, sMachineMetalGUI = false, sConstantEnergy = true, + sMachineExplosions = true, sMachineFlammable = true, sMachineNonWrenchExplosions = true, + sMachineRainExplosions = true, sMachineThunderExplosions = true, sMachineFireExplosions = true, + sMachineWireFire = true, mOutputRF = false, mInputRF = false, meIOLoaded = false, mRFExplosions = false, + mServerStarted = false; + + @Deprecated + public static boolean mIC2Classic = false, mMagneticraft = false, mImmersiveEngineering = false, + mGTPlusPlus = false, mTranslocator = false, mTConstruct = false, mGalacticraft = false, mHodgepodge = false, + mAvaritia = false; + /** + * This is always set to true + */ + @Deprecated + public boolean mAE2 = true; + + public static int mEUtoRF = 360, mRFtoEU = 20; + + /** + * Option to not use MACHINE_METAL mixing into colors + */ + public static boolean sUseMachineMetal = false; + + public static boolean mUseOnlyGoodSolderingMaterials = false; + + private static final String aTextIC2Lower = IndustrialCraft2.ID.toLowerCase(Locale.ENGLISH); + /** + * Getting assigned by the Mod loading + */ + public static boolean sUnificationEntriesRegistered = false, sPreloadStarted = false, sPreloadFinished = false, + sLoadStarted = false, sLoadFinished = false, sPostloadStarted = false, sPostloadFinished = false; + + private static Class<BaseMetaTileEntity> sBaseMetaTileEntityClass = null; + + @SuppressWarnings("unchecked") + private static final IntFunction<TileEntity>[] teCreators = new IntFunction[16]; + + private static final Set<Class<?>> dummyWorlds = new HashSet<>(); + + static { + sItemStackMappings.add(sCovers); + sItemStackMappings.add(sCoverBehaviors); + + dummyWorlds.add(GT_DummyWorld.class); + tryAddDummyWorld("blockrenderer6343.client.world.DummyWorld"); + } + + private static void tryAddDummyWorld(String className) { + ClassLoader cl = GregTech_API.class.getClassLoader(); + Class<?> clazz; + try { + clazz = Class.forName(className, false, cl); + } catch (ReflectiveOperationException ex) { + return; + } + dummyWorlds.add(clazz); + } + + public static void addDummyWorld(Class<?> clazz) { + dummyWorlds.add(clazz); + } + + public static boolean isDummyWorld(@Nonnull World w) { + return dummyWorlds.contains(w.getClass()); + } + + /** + * You want OreDict-Unification for YOUR Mod/Addon, when GregTech is installed? This Function is especially for YOU. + * Call this Function after the load-Phase, as I register the most of the Unification at that Phase (Redpowers + * Storageblocks are registered at postload). A recommended use of this Function is inside your Recipe-System itself + * (if you have one), as the unification then makes 100% sure, that every added non-unificated Output gets + * automatically unificated. + * <p/> + * I will personally make sure, that only common prefixes of Ores get registered at the Unificator, as of now there + * are: pulp, dust, dustSmall, ingot, nugget, gem, ore and block If another Mod-Author messes these up, then it's + * not my fault, and it's especially not your fault. As these are commonly used prefixes. + * <p/> + * This Unificator-API-Function uses the same Functions I use, for unificating Items. So if there is something + * messed up (very unlikely), then everything is messed up. + * <p/> + * You shouldn't use this to unificate the Inputs of your Recipes, this is only meant for the Outputs. + * + * @param aOreStack the Stack you want to get unificated. It is stackSize Sensitive. + * @return Either an unificated Stack or the stack you toss in, but it should never be null, unless you throw a + * Null-Pointer into it. + */ + public static ItemStack getUnificatedOreDictStack(ItemStack aOreStack) { + if (!GregTech_API.sPreloadFinished) GT_Log.err.println( + "GregTech_API ERROR: " + aOreStack.getItem() + + "." + + aOreStack.getItemDamage() + + " - OreDict Unification Entries are not registered now, please call it in the postload phase."); + return GT_OreDictUnificator.get(true, aOreStack); + } + + /** + * Causes a Machineblock Update This update will cause surrounding MultiBlock Machines to update their + * Configuration. You should call this Function in @Block.breakBlock and in @Block.onBlockAdded of your Machine. + * + * @param aWorld is being the World + * @param aX is the X-Coord of the update causing Block + * @param aY is the Y-Coord of the update causing Block + * @param aZ is the Z-Coord of the update causing Block + */ + public static boolean causeMachineUpdate(World aWorld, int aX, int aY, int aZ) { + if (aWorld != null && !aWorld.isRemote && !isDummyWorld(aWorld)) { // World might be null during World-gen + GT_Runnable_MachineBlockUpdate.setMachineUpdateValues(aWorld, aX, aY, aZ); + return true; + } + return false; + } + + @SuppressWarnings("UnusedReturnValue") // Retains API method signature + public static boolean causeCableUpdate(World aWorld, int aX, int aY, int aZ) { + if (aWorld == null || aWorld.isRemote || isDummyWorld(aWorld)) { + return false; + } // World might be null during World-gen + GT_Runnable_Cable_Update.setCableUpdateValues(aWorld, aX, aY, aZ); + return true; + } + + /** + * Adds a Multi-Machine Block, like my Machine Casings for example. You should call @causeMachineUpdate + * in @Block.breakBlock and in {@link Block#onBlockAdded} of your registered Block. You don't need to register + * TileEntities which implement {@link IMachineBlockUpdateable} + * + * @param aBlock the Block + * @param aMeta the Metadata of the Blocks as Bitmask! -1 or ~0 for all Meta-values + */ + @SuppressWarnings("UnusedReturnValue") // Retains API method signature + public static boolean registerMachineBlock(Block aBlock, int aMeta) { + if (aBlock == null) return false; + if (GregTech_API.sThaumcraftCompat != null) + GregTech_API.sThaumcraftCompat.registerPortholeBlacklistedBlock(aBlock); + sMachineIDs.put(aBlock, aMeta); + return true; + } + + /** + * Like above but with boolean Parameters instead of a BitMask + */ + public static boolean registerMachineBlock(Block aBlock, boolean... aMeta) { + if (aBlock == null || aMeta == null || aMeta.length == 0) return false; + if (GregTech_API.sThaumcraftCompat != null) + GregTech_API.sThaumcraftCompat.registerPortholeBlacklistedBlock(aBlock); + int rMeta = 0; + for (byte i = 0; i < aMeta.length && i < 16; i++) if (aMeta[i]) rMeta |= B[i]; + sMachineIDs.put(aBlock, rMeta); + return true; + } + + /** + * if this Block is a Machine Update Conducting Block + */ + public static boolean isMachineBlock(Block aBlock, int aMeta) { + if (aBlock != null) { + Integer id = sMachineIDs.get(aBlock); + return id != null && (id & B[aMeta]) != 0; + } + return false; + } + + /** + * Creates a new Coolant Cell Item for your Nuclear Reactor + */ + public static Item constructCoolantCellItem(String aUnlocalized, String aEnglish, int aMaxStore) { + try { + return new GT_CoolantCellIC_Item(aUnlocalized, aEnglish, aMaxStore); + } catch (Throwable e) { + /* Do nothing */ + } + try { + return new GT_CoolantCell_Item(aUnlocalized, aEnglish, aMaxStore); + } catch (Throwable e) { + /* Do nothing */ + } + return new gregtech.api.items.GT_Generic_Item( + aUnlocalized, + aEnglish, + "Doesn't work as intended, this is a Bug"); + } + + /** + * Creates a new Energy Armor Item + */ + public static Item constructElectricArmorItem(String aUnlocalized, String aEnglish, int aCharge, int aTransfer, + int aTier, int aDamageEnergyCost, int aSpecials, double aArmorAbsorbtionPercentage, boolean aChargeProvider, + int aType, int aArmorIndex) { + try { + return (Item) Class.forName("gregtechmod.api.items.GT_EnergyArmorIC_Item") + .getConstructors()[0].newInstance( + aUnlocalized, + aEnglish, + aCharge, + aTransfer, + aTier, + aDamageEnergyCost, + aSpecials, + aArmorAbsorbtionPercentage, + aChargeProvider, + aType, + aArmorIndex); + } catch (Throwable e) { + /* Do nothing */ + } + try { + return (Item) Class.forName("gregtechmod.api.items.GT_EnergyArmor_Item") + .getConstructors()[0].newInstance( + aUnlocalized, + aEnglish, + aCharge, + aTransfer, + aTier, + aDamageEnergyCost, + aSpecials, + aArmorAbsorbtionPercentage, + aChargeProvider, + aType, + aArmorIndex); + } catch (Throwable e) { + /* Do nothing */ + } + return new gregtech.api.items.GT_Generic_Item( + aUnlocalized, + aEnglish, + "Doesn't work as intended, this is a Bug"); + } + + /** + * Creates a new Energy Battery Item + */ + public static Item constructElectricEnergyStorageItem(String aUnlocalized, String aEnglish, int aCharge, + int aTransfer, int aTier, int aEmptyID, int aFullID) { + try { + return (Item) Class.forName("gregtechmod.api.items.GT_EnergyStoreIC_Item") + .getConstructors()[0].newInstance(aUnlocalized, aEnglish, aCharge, aTransfer, aTier, aEmptyID, aFullID); + } catch (Throwable e) { + /* Do nothing */ + } + try { + return (Item) Class.forName("gregtechmod.api.items.GT_EnergyStore_Item") + .getConstructors()[0].newInstance(aUnlocalized, aEnglish, aCharge, aTransfer, aTier, aEmptyID, aFullID); + } catch (Throwable e) { + /* Do nothing */ + } + return new gregtech.api.items.GT_Generic_Item( + aUnlocalized, + aEnglish, + "Doesn't work as intended, this is a Bug"); + } + + /** + * Creates a new Hard Hammer Item + */ + public static GT_Tool_Item constructHardHammerItem(String aUnlocalized, String aEnglish, int aMaxDamage, + int aEntityDamage) { + try { + return (GT_Tool_Item) Class.forName("gregtechmod.api.items.GT_HardHammer_Item") + .getConstructors()[0].newInstance(aUnlocalized, aEnglish, aMaxDamage, aEntityDamage); + } catch (Throwable e) { + /* Do nothing */ + } + return new gregtech.api.items.GT_Tool_Item( + aUnlocalized, + aEnglish, + "Doesn't work as intended, this is a Bug", + aMaxDamage, + aEntityDamage, + false); + } + + /** + * Creates a new Crowbar Item + */ + public static GT_Tool_Item constructCrowbarItem(String aUnlocalized, String aEnglish, int aMaxDamage, + int aEntityDamage) { + try { + return (GT_Tool_Item) Class.forName("gregtechmod.api.items.GT_CrowbarRC_Item") + .getConstructors()[0].newInstance(aUnlocalized, aEnglish, aMaxDamage, aEntityDamage); + } catch (Throwable e) { + /* Do nothing */ + } + try { + return (GT_Tool_Item) Class.forName("gregtechmod.api.items.GT_Crowbar_Item") + .getConstructors()[0].newInstance(aUnlocalized, aEnglish, aMaxDamage, aEntityDamage); + } catch (Throwable e) { + /* Do nothing */ + } + return new gregtech.api.items.GT_Tool_Item( + aUnlocalized, + aEnglish, + "Doesn't work as intended, this is a Bug", + aMaxDamage, + aEntityDamage, + false); + } + + /** + * Creates a new Wrench Item + */ + public static GT_Tool_Item constructWrenchItem(String aUnlocalized, String aEnglish, int aMaxDamage, + int aEntityDamage, int aDisChargedGTID) { + try { + return (GT_Tool_Item) Class.forName("gregtechmod.api.items.GT_Wrench_Item") + .getConstructors()[0].newInstance(aUnlocalized, aEnglish, aMaxDamage, aEntityDamage, aDisChargedGTID); + } catch (Throwable e) { + /* Do nothing */ + } + return new gregtech.api.items.GT_Tool_Item( + aUnlocalized, + aEnglish, + "Doesn't work as intended, this is a Bug", + aMaxDamage, + aEntityDamage, + false); + } + + /** + * Creates a new electric Screwdriver Item + */ + public static GT_Tool_Item constructElectricScrewdriverItem(String aUnlocalized, String aEnglish, int aMaxDamage, + int aEntityDamage, int aDisChargedGTID) { + try { + return (GT_Tool_Item) Class.forName("gregtechmod.api.items.GT_ScrewdriverIC_Item") + .getConstructors()[0].newInstance(aUnlocalized, aEnglish, aMaxDamage, aEntityDamage, aDisChargedGTID); + } catch (Throwable e) { + /* Do nothing */ + } + return new gregtech.api.items.GT_Tool_Item( + aUnlocalized, + aEnglish, + "Doesn't work as intended, this is a Bug", + aMaxDamage, + aEntityDamage, + false); + } + + /** + * Creates a new electric Wrench Item + */ + public static GT_Tool_Item constructElectricWrenchItem(String aUnlocalized, String aEnglish, int aMaxDamage, + int aEntityDamage, int aDisChargedGTID) { + try { + return (GT_Tool_Item) Class.forName("gregtechmod.api.items.GT_WrenchIC_Item") + .getConstructors()[0].newInstance(aUnlocalized, aEnglish, aMaxDamage, aEntityDamage, aDisChargedGTID); + } catch (Throwable e) { + /* Do nothing */ + } + return new gregtech.api.items.GT_Tool_Item( + aUnlocalized, + aEnglish, + "Doesn't work as intended, this is a Bug", + aMaxDamage, + aEntityDamage, + false); + } + + /** + * Creates a new electric Saw Item + */ + public static GT_Tool_Item constructElectricSawItem(String aUnlocalized, String aEnglish, int aMaxDamage, + int aEntityDamage, int aToolQuality, float aToolStrength, int aEnergyConsumptionPerBlockBreak, + int aDisChargedGTID) { + try { + return (GT_Tool_Item) Class.forName("gregtechmod.api.items.GT_SawIC_Item") + .getConstructors()[0].newInstance( + aUnlocalized, + aEnglish, + aMaxDamage, + aEntityDamage, + aToolQuality, + aToolStrength, + aEnergyConsumptionPerBlockBreak, + aDisChargedGTID); + } catch (Throwable e) { + /* Do nothing */ + } + return new gregtech.api.items.GT_Tool_Item( + aUnlocalized, + aEnglish, + "Doesn't work as intended, this is a Bug", + aMaxDamage, + aEntityDamage, + false); + } + + /** + * Creates a new electric Drill Item + */ + public static GT_Tool_Item constructElectricDrillItem(String aUnlocalized, String aEnglish, int aMaxDamage, + int aEntityDamage, int aToolQuality, float aToolStrength, int aEnergyConsumptionPerBlockBreak, + int aDisChargedGTID) { + try { + return (GT_Tool_Item) Class.forName("gregtechmod.api.items.GT_DrillIC_Item") + .getConstructors()[0].newInstance( + aUnlocalized, + aEnglish, + aMaxDamage, + aEntityDamage, + aToolQuality, + aToolStrength, + aEnergyConsumptionPerBlockBreak, + aDisChargedGTID); + } catch (Throwable e) { + /* Do nothing */ + } + return new gregtech.api.items.GT_Tool_Item( + aUnlocalized, + aEnglish, + "Doesn't work as intended, this is a Bug", + aMaxDamage, + aEntityDamage, + false); + } + + /** + * Creates a new electric Soldering Tool + */ + public static GT_Tool_Item constructElectricSolderingToolItem(String aUnlocalized, String aEnglish, int aMaxDamage, + int aEntityDamage, int aDisChargedGTID) { + try { + return (GT_Tool_Item) Class.forName("gregtechmod.api.items.GT_SolderingToolIC_Item") + .getConstructors()[0].newInstance(aUnlocalized, aEnglish, aMaxDamage, aEntityDamage, aDisChargedGTID); + } catch (Throwable e) { + /* Do nothing */ + } + return new gregtech.api.items.GT_Tool_Item( + aUnlocalized, + aEnglish, + "Doesn't work as intended, this is a Bug", + aMaxDamage, + aEntityDamage, + false); + } + + /** + * Creates a new empty electric Tool + */ + public static GT_Tool_Item constructEmptyElectricToolItem(String aUnlocalized, String aEnglish, int aMaxDamage, + int aChargedGTID) { + try { + return (GT_Tool_Item) Class.forName("gregtechmod.api.items.GT_EmptyToolIC_Item") + .getConstructors()[0].newInstance(aUnlocalized, aEnglish, aMaxDamage, aChargedGTID); + } catch (Throwable e) { + /* Do nothing */ + } + return new gregtech.api.items.GT_Tool_Item( + aUnlocalized, + aEnglish, + "Doesn't work as intended, this is a Bug", + aMaxDamage, + 0, + false); + } + + /** + * Provides a new BaseMetaTileEntity. Because some interfaces are not always loaded (Buildcraft, Universal + * Electricity) we have to use invocation at the constructor of the BaseMetaTileEntity. + */ + public static BaseMetaTileEntity constructBaseMetaTileEntity() { + if (sBaseMetaTileEntityClass == null) { + try { + return (sBaseMetaTileEntityClass = BaseMetaTileEntity.class).getDeclaredConstructor() + .newInstance(); + } catch (Throwable ignored) {} + } + + try { + return sBaseMetaTileEntityClass.getDeclaredConstructor() + .newInstance(); + } catch (Throwable e) { + GT_Log.err.println("GT_Mod: Fatal Error occurred while initializing TileEntities, crashing Minecraft."); + e.printStackTrace(GT_Log.err); + throw new RuntimeException(e); + } + } + + /** + * Register a new ItemStack as configuration circuits. Duplicates or invalid stacks will be silently ignored. + */ + public static void registerConfigurationCircuit(ItemStack aStack) { + registerConfigurationCircuit(aStack, 0); + } + + /** + * Register a new ItemStack as configuration circuits. Duplicates or invalid stacks will be silently ignored. + * + * @param minTier the minimal tier this circuit can be offered for free, e.g. normal configuration circuit is + * available in LV+ single blocks, GT++ breakthrough circuit is offered in HV+ single blocks + */ + public static void registerConfigurationCircuit(ItemStack aStack, int minTier) { + if (GT_Utility.isStackInvalid(aStack)) return; + for (ItemStack tRegistered : sRealConfigurationList.values()) + if (GT_Utility.areStacksEqual(tRegistered, aStack)) return; + ItemStack stack = GT_Utility.copyAmount(0, aStack); + sRealConfigurationList.put(minTier, stack); + for (Map.Entry<Integer, List<ItemStack>> e : sConfigurationLists.entrySet()) { + if (e.getKey() >= minTier) { + e.getValue() + .add(stack); + e.getValue() + .sort(getConfigurationCircuitsComparator()); + } + } + } + + /** + * Get a list of Configuration circuits. These stacks will have a stack size of 0. Use + * {@link #registerConfigurationCircuit(ItemStack, int)} or its overload to add to this list. + * + * @param machineTier The voltage tier where this list will be used. use Integer.MAX_VALUE to get all circuits + * @return An unmodifiable view of actual list. DO NOT MODIFY THE ItemStacks! + */ + public static List<ItemStack> getConfigurationCircuitList(int machineTier) { + return Collections.unmodifiableList( + sConfigurationLists.computeIfAbsent( + machineTier, + (t) -> sRealConfigurationList.entries() + .stream() + .filter(e -> e.getKey() <= machineTier) + .map(Map.Entry::getValue) + .sorted(getConfigurationCircuitsComparator()) + .collect(Collectors.toList()))); + } + + public static Comparator<ItemStack> getConfigurationCircuitsComparator() { + return Comparator.comparingInt((ItemStack is) -> { + // By default, the Programmed Circuit should be the earliest configuration circuit to which the + // player is exposed + if (GT_Mod.gregtechproxy.mCircuitsOrder.isEmpty()) + return is.getItem() instanceof GT_IntegratedCircuit_Item ? 0 : 1; + return GT_Mod.gregtechproxy.mCircuitsOrder + .getOrDefault(String.valueOf(GameRegistry.findUniqueIdentifierFor(is.getItem())), Integer.MAX_VALUE); + }) + .thenComparing(ItemStack::getUnlocalizedName) + .thenComparing(ItemStack::getItemDamage); + } + + public static void registerCircuitProgrammer(ItemStack stack, boolean ignoreNBT, boolean useContainer) { + registerCircuitProgrammer(rhs -> GT_Utility.areStacksEqual(stack, rhs, ignoreNBT), useContainer); + } + + public static void registerCircuitProgrammer(Predicate<ItemStack> predicate, boolean useContainer) { + sRealCircuitProgrammerList.put( + predicate, + useContainer ? (s, p) -> s.getItem() + .getContainerItem(s) : (s, p) -> s); + } + + public static void registerCircuitProgrammer(Predicate<ItemStack> predicate, + BiFunction<ItemStack, EntityPlayerMP, ItemStack> doDamage) { + sRealCircuitProgrammerList.put(predicate, doDamage); + } + + public static void registerCover(ItemStack aStack, ITexture aCover, GT_CoverBehavior aBehavior) { + registerCover(aStack, aCover, (GT_CoverBehaviorBase<?>) aBehavior); + } + + public static void registerCover(ItemStack aStack, ITexture aCover, GT_CoverBehaviorBase<?> aBehavior) { + if (!sCovers.containsKey(new GT_ItemStack(aStack))) sCovers.put( + new GT_ItemStack(aStack), + aCover == null || !aCover.isValidTexture() ? Textures.BlockIcons.ERROR_RENDERING[0] : aCover); + if (aBehavior != null) sCoverBehaviors.put(new GT_ItemStack(aStack), aBehavior); + } + + public static void registerCoverBehavior(ItemStack aStack, GT_CoverBehavior aBehavior) { + registerCoverBehavior(aStack, (GT_CoverBehaviorBase<?>) aBehavior); + } + + public static void registerCoverBehavior(ItemStack aStack, GT_CoverBehaviorBase<?> aBehavior) { + sCoverBehaviors.put(new GT_ItemStack(aStack), aBehavior == null ? sDefaultBehavior : aBehavior); + } + + /** + * Registers multiple Cover Items. I use that for the OreDict Functionality. + * + * @param aBehavior can be null + */ + public static void registerCover(Collection<ItemStack> aStackList, ITexture aCover, GT_CoverBehavior aBehavior) { + registerCover(aStackList, aCover, (GT_CoverBehaviorBase<?>) aBehavior); + } + + /** + * Registers multiple Cover Items. I use that for the OreDict Functionality. + * + * @param aBehavior can be null + */ + public static void registerCover(Collection<ItemStack> aStackList, ITexture aCover, + GT_CoverBehaviorBase<?> aBehavior) { + if (aCover.isValidTexture()) + aStackList.forEach(tStack -> GregTech_API.registerCover(tStack, aCover, aBehavior)); + } + + /** + * returns a Cover behavior, guaranteed to not return null after preload + */ + @Deprecated + public static GT_CoverBehavior getCoverBehavior(ItemStack aStack) { + if (aStack == null || aStack.getItem() == null) return sNoBehavior; + GT_CoverBehaviorBase<?> rCover = sCoverBehaviors.get(new GT_ItemStack(aStack)); + if (!(rCover instanceof GT_CoverBehavior)) return sDefaultBehavior; + return (GT_CoverBehavior) rCover; + } + + /** + * returns a Cover behavior, guaranteed to not return null after preload + * + * @return The Cover behavior + */ + public static GT_CoverBehaviorBase<?> getCoverBehaviorNew(ItemStack aStack) { + if (aStack == null || aStack.getItem() == null) return sNoBehavior; + GT_CoverBehaviorBase<?> rCover = sCoverBehaviors.get(new GT_ItemStack(aStack)); + if (rCover != null) return rCover; + rCover = sCoverBehaviors.get(new GT_ItemStack(aStack, true)); + if (rCover != null) return rCover; + return sDefaultBehavior; + } + + /** + * returns a Cover behavior, guaranteed to not return null + */ + @Deprecated + public static GT_CoverBehavior getCoverBehavior(int aStack) { + if (aStack == 0) return sNoBehavior; + return getCoverBehavior(GT_Utility.intToStack(aStack)); + } + + /** + * returns a Cover behavior, guaranteed to not return null + */ + public static GT_CoverBehaviorBase<?> getCoverBehaviorNew(int aStack) { + if (aStack == 0) return sNoBehavior; + return getCoverBehaviorNew(GT_Utility.intToStack(aStack)); + } + + /** + * Register a Wrench to be usable on GregTech Machines. The Wrench MUST have some kind of Durability unlike certain + * Buildcraft Wrenches. + * <p/> + * You need to register Tools in the Load Phase, because otherwise the Auto-detection will assign a Tool Type in + * certain Cases during postload (When IToolWrench or similar Interfaces are implemented). + * <p/> + * ----- + * <p/> + * Returning true at isDamageable was a great Idea, KingLemming. Well played. Since the OmniWrench is just a + * Single-Item-Mod, people can choose if they want your infinite durability or not. So that's not really a Problem. + * I even have a new Config to auto-disable most infinite BC Wrenches (but that one is turned off). + * <p/> + * One last Bug for you to fix: My Auto-registration detects Railcraft's Crowbars, Buildcraft's Wrenches and alike, + * due to their Interfaces. Guess what now became a Crowbar by accident. Try registering the Wrench at the load + * phase to prevent things like that from happening. Yes, I know that "You need to register Tools in the Load + * Phase"-Part wasn't there before this. Sorry about that. + */ + public static boolean registerWrench(ItemStack aTool) { + return registerTool(aTool, sWrenchList); + } + + /** + * Register a Crowbar to extract Covers from Machines Crowbars are NOT Wrenches btw. + * <p/> + * You need to register Tools in the Load Phase, because otherwise the Auto-detection will assign a Tool Type in + * certain Cases during postload (When IToolWrench or similar Interfaces are implemented). + */ + public static boolean registerCrowbar(ItemStack aTool) { + return registerTool(aTool, sCrowbarList); + } + + /** + * Register a Screwdriver to interact directly with Machines and Covers Did I mention, that it is intentionally not + * possible to make a Multi-tool, which doesn't switch ItemID (like a Mode) all the time? + * <p/> + * You need to register Tools in the Load Phase, because otherwise the Auto-detection will assign a Tool Type in + * certain Cases during postload (When IToolWrench or similar Interfaces are implemented). + */ + @SuppressWarnings("UnusedReturnValue") // Retains API method signature + public static boolean registerScrewdriver(ItemStack aTool) { + return registerTool(aTool, sScrewdriverList); + } + + /** + * Register a Soft Hammer to interact with Machines + * <p/> + * You need to register Tools in the Load Phase, because otherwise the Auto-detection will assign a Tool Type in + * certain Cases during postload (When IToolWrench or similar Interfaces are implemented). + */ + public static boolean registerSoftHammer(ItemStack aTool) { + return registerTool(aTool, sSoftHammerList); + } + + /** + * Register a Hard Hammer to interact with Machines + * <p/> + * You need to register Tools in the Load Phase, because otherwise the Auto-detection will assign a Tool Type in + * certain Cases during postload (When IToolWrench or similar Interfaces are implemented). + */ + public static boolean registerHardHammer(ItemStack aTool) { + return registerTool(aTool, sHardHammerList); + } + + /** + * Register a Wire Cutter to interact with Machines + * <p/> + * You need to register Tools in the Load Phase, because otherwise the Auto-detection will assign a Tool Type in + * certain Cases during postload (When IToolWrench or similar Interfaces are implemented). + */ + public static boolean registerWireCutter(ItemStack aTool) { + return registerTool(aTool, sWireCutterList); + } + + /** + * Register a Soldering Tool to interact with Machines + * <p/> + * You need to register Tools in the Load Phase, because otherwise the Auto-detection will assign a Tool Type in + * certain Cases during postload (When IToolWrench or similar Interfaces are implemented). + */ + @SuppressWarnings("UnusedReturnValue") // Retains API method signature + public static boolean registerSolderingTool(ItemStack aTool) { + return registerTool(aTool, sSolderingToolList); + } + + /** + * Register a Soldering Tin to interact with Soldering Tools + * <p/> + * You need to register Tools in the Load Phase, because otherwise the Auto-detection will assign a Tool Type in + * certain Cases during postload (When IToolWrench or similar Interfaces are implemented). + */ + @SuppressWarnings("UnusedReturnValue") // Retains API method signature + public static boolean registerSolderingMetal(ItemStack aTool) { + return registerTool(aTool, sSolderingMetalList); + } + + /** + * Generic Function to add Tools to the Lists. Contains all sanity Checks for Tools, like preventing one Tool from + * being registered for multiple purposes as controls would override each other. + */ + public static boolean registerTool(ItemStack aTool, Collection<GT_ItemStack> aToolList) { + if (aTool == null || GT_Utility.isStackInList(aTool, sToolList) + || (!aTool.getItem() + .isDamageable() && !GT_ModHandler.isElectricItem(aTool) + && !(aTool.getItem() instanceof IDamagableItem))) + return false; + aToolList.add(new GT_ItemStack(GT_Utility.copyAmount(1, aTool))); + sToolList.add(new GT_ItemStack(GT_Utility.copyAmount(1, aTool))); + return true; + } + + /** + * Sets the {@link IIconRegister} for Block Icons + * + * @param aIconRegister The {@link IIconRegister} Icon Register + */ + @SideOnly(Side.CLIENT) + public static void setBlockIconRegister(IIconRegister aIconRegister) { + sBlockIcons = aIconRegister; + } + + /** + * Sets the {@link IIconRegister} for Items Icons + * + * @param aIconRegister The {@link IIconRegister} Icon Register + */ + @SideOnly(Side.CLIENT) + public static void setItemIconRegister(IIconRegister aIconRegister) { + GregTech_API.sItemIcons = aIconRegister; + } + + public static void registerTileEntityConstructor(int meta, IntFunction<TileEntity> constructor) { + if (meta < 0 || meta > 15 || constructor == null) throw new IllegalArgumentException(); + if (teCreators[meta] != null) throw new IllegalStateException( + "previous constructor: " + teCreators[meta] + " new constructor: " + constructor + " meta:" + meta); + teCreators[meta] = constructor; + } + + public static TileEntity createTileEntity(int meta) { + meta = GT_Utility.clamp(meta, 0, 15); + if (teCreators[meta] == null) return null; + return teCreators[meta].apply(meta); + } +} diff --git a/src/main/java/gregtech/api/damagesources/GT_DamageSources.java b/src/main/java/gregtech/api/damagesources/GT_DamageSources.java new file mode 100644 index 0000000000..65a2519001 --- /dev/null +++ b/src/main/java/gregtech/api/damagesources/GT_DamageSources.java @@ -0,0 +1,119 @@ +package gregtech.api.damagesources; + +import javax.annotation.Nullable; + +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.item.ItemStack; +import net.minecraft.util.ChatComponentText; +import net.minecraft.util.DamageSource; +import net.minecraft.util.EntityDamageSource; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.IChatComponent; + +public class GT_DamageSources { + + public static DamageSource getElectricDamage() { + return ic2.api.info.Info.DMG_ELECTRIC; + } + + public static DamageSource getRadioactiveDamage() { + return ic2.api.info.Info.DMG_RADIATION; + } + + public static DamageSource getNukeExplosionDamage() { + return ic2.api.info.Info.DMG_NUKE_EXPLOSION; + } + + public static DamageSource getExplodingDamage() { + return new DamageSourceExploding(); + } + + public static DamageSource getCombatDamage(String aType, EntityLivingBase aPlayer, IChatComponent aDeathMessage) { + return new DamageSourceCombat(aType, aPlayer, aDeathMessage); + } + + public static DamageSource getHeatDamage() { + return new DamageSourceHeat(); + } + + public static DamageSource getFrostDamage() { + return new DamageSourceFrost(); + } + + private static class DamageSourceCombat extends EntityDamageSource { + + private final IChatComponent mDeathMessage; + + public DamageSourceCombat(String aType, EntityLivingBase aPlayer, IChatComponent aDeathMessage) { + super(aType, aPlayer); + mDeathMessage = aDeathMessage; + } + + @Override + public IChatComponent func_151519_b(EntityLivingBase aTarget) { + return mDeathMessage == null ? super.func_151519_b(aTarget) : mDeathMessage; + } + } + + private static class DamageSourceFrost extends DamageSource { + + public DamageSourceFrost() { + super("frost"); + setDifficultyScaled(); + } + + @Override + public IChatComponent func_151519_b(EntityLivingBase aTarget) { + return new ChatComponentText( + EnumChatFormatting.RED + aTarget.getCommandSenderName() + EnumChatFormatting.WHITE + " got frozen"); + } + } + + private static class DamageSourceHeat extends DamageSource { + + public DamageSourceHeat() { + super("steam"); + setFireDamage(); + setDifficultyScaled(); + } + + @Override + public IChatComponent func_151519_b(EntityLivingBase aTarget) { + return new ChatComponentText( + EnumChatFormatting.RED + aTarget.getCommandSenderName() + + EnumChatFormatting.WHITE + + " was boiled alive"); + } + } + + public static class DamageSourceHotItem extends DamageSourceHeat { + + @Nullable + private final ItemStack stack; + + public DamageSourceHotItem(@Nullable ItemStack cause) { + this.stack = cause; + } + + @Nullable + public ItemStack getDamagingStack() { + return stack; + } + } + + public static class DamageSourceExploding extends DamageSource { + + public DamageSourceExploding() { + super("exploded"); + setDamageAllowedInCreativeMode(); + setDamageBypassesArmor(); + setDamageIsAbsolute(); + } + + @Override + public IChatComponent func_151519_b(EntityLivingBase aTarget) { + return new ChatComponentText( + EnumChatFormatting.RED + aTarget.getCommandSenderName() + EnumChatFormatting.WHITE + " exploded"); + } + } +} diff --git a/src/main/java/gregtech/api/enchants/Enchantment_EnderDamage.java b/src/main/java/gregtech/api/enchants/Enchantment_EnderDamage.java new file mode 100644 index 0000000000..07c13b3509 --- /dev/null +++ b/src/main/java/gregtech/api/enchants/Enchantment_EnderDamage.java @@ -0,0 +1,72 @@ +package gregtech.api.enchants; + +import net.minecraft.enchantment.EnchantmentDamage; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.boss.EntityDragon; +import net.minecraft.entity.monster.EntityEnderman; +import net.minecraft.potion.Potion; +import net.minecraft.potion.PotionEffect; + +import gregtech.api.enums.ConfigCategories; +import gregtech.api.enums.Materials; +import gregtech.api.util.GT_Config; +import gregtech.api.util.GT_LanguageManager; + +public class Enchantment_EnderDamage extends EnchantmentDamage { + + public static Enchantment_EnderDamage INSTANCE; + + public Enchantment_EnderDamage() { + super(GT_Config.addIDConfig(ConfigCategories.IDs.enchantments, "Disjunction", 15), 2, -1); + GT_LanguageManager.addStringLocalization(getName(), "Disjunction"); + Materials.Silver.setEnchantmentForTools(this, 2); + Materials.Mercury.setEnchantmentForTools(this, 3); + Materials.Electrum.setEnchantmentForTools(this, 3); + Materials.SterlingSilver.setEnchantmentForTools(this, 4); + Materials.AstralSilver.setEnchantmentForTools(this, 5); + INSTANCE = this; + } + + @Override + public int getMinEnchantability(int aLevel) { + return 5 + (aLevel - 1) * 8; + } + + @Override + public int getMaxEnchantability(int aLevel) { + return this.getMinEnchantability(aLevel) + 20; + } + + @Override + public int getMaxLevel() { + return 5; + } + + @Override + public void func_151367_b(EntityLivingBase aHurtEntity, Entity aDamagingEntity, int aLevel) { + if ((aHurtEntity instanceof EntityEnderman || aHurtEntity instanceof EntityDragon + || (aHurtEntity.getClass() + .getName() + .contains(".") + && aHurtEntity.getClass() + .getName() + .substring( + aHurtEntity.getClass() + .getName() + .lastIndexOf(".")) + .contains("Ender")))) { + // Weakness causes Endermen to not be able to teleport with GT being installed. + aHurtEntity + .addPotionEffect(new PotionEffect(Potion.weakness.id, aLevel * 200, Math.max(1, (5 * aLevel) / 7))); + // They also get Poisoned. If you have this Enchant on an Arrow, you can kill the Ender Dragon easier. + aHurtEntity + .addPotionEffect(new PotionEffect(Potion.poison.id, aLevel * 200, Math.max(1, (5 * aLevel) / 7))); + } + } + + @Override + public String getName() { + return "enchantment.damage.endermen"; + } +} diff --git a/src/main/java/gregtech/api/enchants/Enchantment_Hazmat.java b/src/main/java/gregtech/api/enchants/Enchantment_Hazmat.java new file mode 100644 index 0000000000..ecbe654698 --- /dev/null +++ b/src/main/java/gregtech/api/enchants/Enchantment_Hazmat.java @@ -0,0 +1,56 @@ +package gregtech.api.enchants; + +import net.minecraft.enchantment.Enchantment; +import net.minecraft.enchantment.EnumEnchantmentType; +import net.minecraft.item.ItemArmor; +import net.minecraft.item.ItemStack; + +import gregtech.api.enums.ConfigCategories; +import gregtech.api.util.GT_Config; +import gregtech.api.util.GT_LanguageManager; + +public class Enchantment_Hazmat extends Enchantment { + + public static Enchantment_Hazmat INSTANCE; + + public Enchantment_Hazmat() { + super(GT_Config.addIDConfig(ConfigCategories.IDs.enchantments, "Hazmat", 13), 0, EnumEnchantmentType.armor); + GT_LanguageManager.addStringLocalization(getName(), "Hazmat"); + INSTANCE = this; + } + + @Override + public int getMinEnchantability(int aLevel) { + return 50; + } + + @Override + public int getMaxEnchantability(int aLevel) { + return 100; + } + + @Override + public int getMaxLevel() { + return 1; + } + + @Override + public boolean canApply(ItemStack aStack) { + return aStack != null && (aStack.getItem() instanceof ItemArmor); + } + + @Override + public boolean canApplyAtEnchantingTable(ItemStack itemStack) { + return false; + } + + @Override + public boolean isAllowedOnBooks() { + return false; + } + + @Override + public String getName() { + return "enchantment.protection.hazmat"; + } +} diff --git a/src/main/java/gregtech/api/enchants/Enchantment_Radioactivity.java b/src/main/java/gregtech/api/enchants/Enchantment_Radioactivity.java new file mode 100644 index 0000000000..e68e55cd6a --- /dev/null +++ b/src/main/java/gregtech/api/enchants/Enchantment_Radioactivity.java @@ -0,0 +1,68 @@ +package gregtech.api.enchants; + +import net.minecraft.enchantment.EnchantmentDamage; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.item.ItemStack; + +import gregtech.api.enums.ConfigCategories; +import gregtech.api.enums.Materials; +import gregtech.api.util.GT_Config; +import gregtech.api.util.GT_LanguageManager; +import gregtech.api.util.GT_Utility; + +public class Enchantment_Radioactivity extends EnchantmentDamage { + + public static Enchantment_Radioactivity INSTANCE; + + public Enchantment_Radioactivity() { + super(GT_Config.addIDConfig(ConfigCategories.IDs.enchantments, "Radioactivity", 14), 0, -1); + GT_LanguageManager.addStringLocalization(getName(), "Radioactivity"); + Materials.Plutonium.setEnchantmentForTools(this, 1) + .setEnchantmentForArmors(this, 1); + Materials.Uranium235.setEnchantmentForTools(this, 2) + .setEnchantmentForArmors(this, 2); + Materials.Plutonium241.setEnchantmentForTools(this, 3) + .setEnchantmentForArmors(this, 3); + Materials.NaquadahEnriched.setEnchantmentForTools(this, 4) + .setEnchantmentForArmors(this, 4); + Materials.Naquadria.setEnchantmentForTools(this, 5) + .setEnchantmentForArmors(this, 5); + INSTANCE = this; + } + + @Override + public int getMinEnchantability(int aLevel) { + return Integer.MAX_VALUE; + } + + @Override + public int getMaxEnchantability(int aLevel) { + return 0; + } + + @Override + public int getMaxLevel() { + return 5; + } + + @Override + public boolean canApply(ItemStack itemStack) { + return false; + } + + @Override + public boolean isAllowedOnBooks() { + return false; + } + + @Override + public void func_151367_b(EntityLivingBase aHurtEntity, Entity aDamagingEntity, int aLevel) { + GT_Utility.applyRadioactivity(aHurtEntity, aLevel, 1); + } + + @Override + public String getName() { + return "enchantment.damage.radioactivity"; + } +} diff --git a/src/main/java/gregtech/api/enums/ConfigCategories.java b/src/main/java/gregtech/api/enums/ConfigCategories.java new file mode 100644 index 0000000000..83deec4f58 --- /dev/null +++ b/src/main/java/gregtech/api/enums/ConfigCategories.java @@ -0,0 +1,65 @@ +package gregtech.api.enums; + +public enum ConfigCategories { + + news, + general, + machineconfig, + specialunificationtargets; + + public enum IDs { + crops, + enchantments + } + + public enum Materials { + heatdamage, + oreprocessingoutputmultiplier, + blastfurnacerequirements, + blastinductionsmelter, + } + + public enum Recipes { + harderrecipes, + gregtechrecipes, + disabledrecipes, + recipereplacements, + storageblockcrafting, + storageblockdecrafting + } + + public enum Machines { + smelting, + squeezer, + liquidtransposer, + liquidtransposerfilling, + liquidtransposeremptying, + extractor, + sawmill, + compression, + thermalcentrifuge, + orewashing, + inductionsmelter, + rcblastfurnace, + scrapboxdrops, + massfabamplifier, + maceration, + rockcrushing, + pulverization + } + + public enum Fuels { + boilerfuels + } + + public enum Tools { + mortar, + hammerplating, + hammermultiingot, + hammerdoubleplate, + hammertripleplate, + hammerquadrupleplate, + hammerquintupleplate, + scoop + } +} diff --git a/src/main/java/gregtech/api/enums/Dyes.java b/src/main/java/gregtech/api/enums/Dyes.java new file mode 100644 index 0000000000..3f1bc31c21 --- /dev/null +++ b/src/main/java/gregtech/api/enums/Dyes.java @@ -0,0 +1,126 @@ +package gregtech.api.enums; + +import java.util.ArrayList; + +import net.minecraft.util.EnumChatFormatting; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidStack; + +import gregtech.api.interfaces.IColorModulationContainer; +import gregtech.api.objects.GT_ArrayList; +import gregtech.api.util.GT_Utility; + +public enum Dyes implements IColorModulationContainer { + + /** + * The valid Colors, see VALUES Array below + */ + dyeBlack(0, 32, 32, 32, "Black", EnumChatFormatting.BLACK), + dyeRed(1, 255, 0, 0, "Red", EnumChatFormatting.RED), + dyeGreen(2, 0, 255, 0, "Green", EnumChatFormatting.DARK_GREEN), + dyeBrown(3, 96, 64, 0, "Brown", EnumChatFormatting.GOLD), + dyeBlue(4, 0, 32, 255, "Blue", EnumChatFormatting.DARK_BLUE), + dyePurple(5, 128, 0, 128, "Purple", EnumChatFormatting.DARK_PURPLE), + dyeCyan(6, 0, 255, 255, "Cyan", EnumChatFormatting.DARK_AQUA), + dyeLightGray(7, 192, 192, 192, "Light Gray", EnumChatFormatting.GRAY), + dyeGray(8, 128, 128, 128, "Gray", EnumChatFormatting.DARK_GRAY), + dyePink(9, 255, 192, 192, "Pink", EnumChatFormatting.LIGHT_PURPLE), + dyeLime(10, 128, 255, 128, "Lime", EnumChatFormatting.GREEN), + dyeYellow(11, 255, 255, 0, "Yellow", EnumChatFormatting.YELLOW), + dyeLightBlue(12, 96, 128, 255, "Light Blue", EnumChatFormatting.AQUA), + dyeMagenta(13, 255, 0, 255, "Magenta", EnumChatFormatting.LIGHT_PURPLE), + dyeOrange(14, 255, 128, 0, "Orange", EnumChatFormatting.GOLD), + dyeWhite(15, 255, 255, 255, "White", EnumChatFormatting.WHITE), + /** + * The NULL Color + */ + _NULL(-1, 255, 255, 255, "INVALID COLOR"), + /** + * Additional Colors only used for direct Color referencing + */ + CABLE_INSULATION(-1, 64, 64, 64, "Cable Insulation"), + CONSTRUCTION_FOAM(-1, 64, 64, 64, "Construction Foam"), + MACHINE_METAL(-1, 210, 220, 255, "Machine Metal"); + + public static final Dyes[] VALUES = { dyeBlack, dyeRed, dyeGreen, dyeBrown, dyeBlue, dyePurple, dyeCyan, + dyeLightGray, dyeGray, dyePink, dyeLime, dyeYellow, dyeLightBlue, dyeMagenta, dyeOrange, dyeWhite }; + + public final byte mIndex; + public final String mName; + public final short[] mRGBa; + public final short[] mOriginalRGBa; + public final EnumChatFormatting formatting; + private final ArrayList<Fluid> mFluidDyes = new GT_ArrayList<>(false, 1); + + Dyes(int aIndex, int aR, int aG, int aB, String aName) { + this(aIndex, aR, aG, aB, aName, EnumChatFormatting.GRAY); + } + + Dyes(int aIndex, int aR, int aG, int aB, String aName, EnumChatFormatting formatting) { + mIndex = (byte) aIndex; + mName = aName; + mRGBa = new short[] { (short) aR, (short) aG, (short) aB, 0 }; + mOriginalRGBa = mRGBa.clone(); + this.formatting = formatting; + } + + public static Dyes get(int aColor) { + if (aColor >= 0 && aColor < 16) return VALUES[aColor]; + return _NULL; + } + + public static short[] getModulation(int aColor, short[] aDefaultModulation) { + if (aColor >= 0 && aColor < 16) return VALUES[aColor].mRGBa; + return aDefaultModulation; + } + + public static Dyes get(String aColor) { + Object tObject = GT_Utility.getFieldContent(Dyes.class, aColor, false, false); + if (tObject instanceof Dyes) return (Dyes) tObject; + return _NULL; + } + + public static boolean isAnyFluidDye(FluidStack aFluid) { + return aFluid != null && isAnyFluidDye(aFluid.getFluid()); + } + + public static boolean isAnyFluidDye(Fluid aFluid) { + if (aFluid != null) for (Dyes tDye : VALUES) if (tDye.isFluidDye(aFluid)) return true; + return false; + } + + public boolean isFluidDye(FluidStack aFluid) { + return aFluid != null && isFluidDye(aFluid.getFluid()); + } + + public boolean isFluidDye(Fluid aFluid) { + return aFluid != null && mFluidDyes.contains(aFluid); + } + + public boolean addFluidDye(Fluid aDye) { + if (aDye == null || mFluidDyes.contains(aDye)) return false; + mFluidDyes.add(aDye); + return true; + } + + public int getSizeOfFluidList() { + return mFluidDyes.size(); + } + + /** + * @param aAmount 1 Fluid Material Unit (144) = 1 Dye Item + */ + public FluidStack getFluidDye(int aIndex, long aAmount) { + if (aIndex >= mFluidDyes.size() || aIndex < 0) return null; + return new FluidStack(mFluidDyes.get(aIndex), (int) aAmount); + } + + @Override + public short[] getRGBA() { + return mRGBa; + } + + public static Dyes getDyeFromIndex(short index) { + return index != -1 ? Dyes.get(index) : Dyes.MACHINE_METAL; + } +} diff --git a/src/main/java/gregtech/api/enums/Element.java b/src/main/java/gregtech/api/enums/Element.java new file mode 100644 index 0000000000..0931663b0b --- /dev/null +++ b/src/main/java/gregtech/api/enums/Element.java @@ -0,0 +1,341 @@ +package gregtech.api.enums; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +import javax.annotation.Nonnull; + +/** + * This is some kind of Periodic Table, which I use to determine Properties of the Materials. + */ +public enum Element { + + _NULL(0, 0, 0, -1, null, "", false), + H(1, 0, 0, -1, null, "Hydrogen", false), + D(1, 1, 0, -1, "H", "Deuterium", true), + T(1, 2, 0, -1, "D", "Tritium", true), + He(2, 2, 0, -1, null, "Helium", false), + He_3(2, 1, 0, -1, "H&D", "Helium-3", true), + Li(3, 4, 0, -1, null, "Lithium", false), + Be(4, 5, 0, -1, null, "Beryllium", false), + B(5, 5, 0, -1, null, "Boron", false), + C(6, 6, 0, -1, null, "Carbon", false), + N(7, 7, 0, -1, null, "Nitrogen", false), + O(8, 8, 0, -1, null, "Oxygen", false), + F(9, 9, 0, -1, null, "Fluorine", false), + Ne(10, 10, 0, -1, null, "Neon", false), + Na(11, 11, 0, -1, null, "Sodium", false), + Mg(12, 12, 0, -1, null, "Magnesium", false), + Al(13, 13, 0, -1, null, "Aluminium", false), + Si(14, 14, 0, -1, null, "Silicon", false), + P(15, 15, 0, -1, null, "Phosphorus", false), + S(16, 16, 0, -1, null, "Sulfur", false), + Cl(17, 18, 0, -1, null, "Chlorine", false), + Ar(18, 22, 0, -1, null, "Argon", false), + K(19, 20, 0, -1, null, "Potassium", false), + Ca(20, 20, 0, -1, null, "Calcium", false), + Sc(21, 24, 0, -1, null, "Scandium", false), + Ti(22, 26, 0, -1, null, "Titanium", false), + V(23, 28, 0, -1, null, "Vanadium", false), + Cr(24, 28, 0, -1, null, "Chrome", false), + Mn(25, 30, 0, -1, null, "Manganese", false), + Fe(26, 30, 0, -1, null, "Iron", false), + Co(27, 32, 0, -1, null, "Cobalt", false), + Ni(28, 30, 0, -1, null, "Nickel", false), + Cu(29, 34, 0, -1, null, "Copper", false), + Zn(30, 35, 0, -1, null, "Zinc", false), + Ga(31, 39, 0, -1, null, "Gallium", false), + Ge(32, 40, 0, -1, null, "Germanium", false), + As(33, 42, 0, -1, null, "Arsenic", false), + Se(34, 45, 0, -1, null, "Selenium", false), + Br(35, 44, 0, -1, null, "Bromine", false), + Kr(36, 48, 0, -1, null, "Krypton", false), + Rb(37, 48, 0, -1, null, "Rubidium", false), + Sr(38, 49, 0, -1, null, "Strontium", false), + Y(39, 50, 0, -1, null, "Yttrium", false), + Zr(40, 51, 0, -1, null, "Zirconium", false), + Nb(41, 53, 0, -1, null, "Niobium", false), + Mo(42, 53, 0, -1, null, "Molybdenum", false), + Tc(43, 55, 0, -1, null, "Technetium", false), + Ru(44, 57, 0, -1, null, "Ruthenium", false), + Rh(45, 58, 0, -1, null, "Rhodium", false), + Pd(46, 60, 0, -1, null, "Palladium", false), + Ag(47, 60, 0, -1, null, "Silver", false), + Cd(48, 64, 0, -1, null, "Cadmium", false), + In(49, 65, 0, -1, null, "Indium", false), + Sn(50, 68, 0, -1, null, "Tin", false), + Sb(51, 70, 0, -1, null, "Antimony", false), + Te(52, 75, 0, -1, null, "Tellurium", false), + I(53, 74, 0, -1, null, "Iodine", false), + Xe(54, 77, 0, -1, null, "Xenon", false), + Cs(55, 77, 0, -1, null, "Caesium", false), + Ba(56, 81, 0, -1, null, "Barium", false), + La(57, 81, 0, -1, null, "Lantanium", false), + Ce(58, 82, 0, -1, null, "Cerium", false), + Pr(59, 81, 0, -1, null, "Praseodymium", false), + Nd(60, 84, 0, -1, null, "Neodymium", false), + Pm(61, 83, 0, -1, null, "Promethium", false), + Sm(62, 88, 0, -1, null, "Samarium", false), + Eu(63, 88, 0, -1, null, "Europium", false), + Gd(64, 93, 0, -1, null, "Gadolinium", false), + Tb(65, 93, 0, -1, null, "Terbium", false), + Dy(66, 96, 0, -1, null, "Dysprosium", false), + Ho(67, 97, 0, -1, null, "Holmium", false), + Er(68, 99, 0, -1, null, "Erbium", false), + Tm(69, 99, 0, -1, null, "Thulium", false), + Yb(70, 103, 0, -1, null, "Ytterbium", false), + Lu(71, 103, 0, -1, null, "Lutetium", false), + Hf(72, 106, 0, -1, null, "Hafnium", false), + Ta(73, 107, 0, -1, null, "Tantalum", false), + W(74, 109, 0, -1, null, "Wolframium", false), + Re(75, 111, 0, -1, null, "Rhenium", false), + Os(76, 114, 0, -1, null, "Osmium", false), + Ir(77, 115, 0, -1, null, "Iridium", false), + Pt(78, 117, 0, -1, null, "Platinum", false), + Au(79, 117, 0, -1, null, "Gold", false), + Hg(80, 120, 0, -1, null, "Mercury", false), + Tl(81, 123, 0, -1, null, "Thallium", false), + Pb(82, 125, 0, -1, null, "Lead", false), + Bi(83, 125, 0, -1, null, "Bismuth", false), + Po(84, 124, 0, -1, null, "Polonium", false), + At(85, 124, 0, -1, null, "Astatine", false), + Rn(86, 134, 0, -1, null, "Radon", false), + Fr(87, 134, 0, -1, null, "Francium", false), + Ra(88, 136, 0, -1, null, "Radium", false), + Ac(89, 136, 0, -1, null, "Actinium", false), + Th(90, 140, 0, -1, null, "Thorium", false), + Pa(91, 138, 0, -1, null, "Protactinium", false), + U(92, 146, 0, -1, null, "Uranium", false), + U_235(92, 143, 0, -1, null, "Uranium-235", true), + Np(93, 144, 0, -1, null, "Neptunium", false), + Pu(94, 152, 0, -1, null, "Plutonium", false), + Pu_241(94, 149, 0, -1, null, "Plutonium-241", true), + Am(95, 150, 0, -1, null, "Americium", false), + Cm(96, 153, 0, -1, null, "Curium", false), + Bk(97, 152, 0, -1, null, "Berkelium", false), + Cf(98, 153, 0, -1, null, "Californium", false), + Es(99, 153, 0, -1, null, "Einsteinium", false), + Fm(100, 157, 0, -1, null, "Fermium", false), + Md(101, 157, 0, -1, null, "Mendelevium", false), + No(102, 157, 0, -1, null, "Nobelium", false), + Lr(103, 159, 0, -1, null, "Lawrencium", false), + Rf(104, 161, 0, -1, null, "Rutherfordium", false), + Db(105, 163, 0, -1, null, "Dubnium", false), + Sg(106, 165, 0, -1, null, "Seaborgium", false), + Bh(107, 163, 0, -1, null, "Bohrium", false), + Hs(108, 169, 0, -1, null, "Hassium", false), + Mt(109, 167, 0, -1, null, "Meitnerium", false), + Ds(110, 171, 0, -1, null, "Darmstadtium", false), + Rg(111, 169, 0, -1, null, "Roentgenium", false), + Cn(112, 173, 0, -1, null, "Copernicium", false), + Nh(113, 171, 0, -1, null, "Nihonium", false), + Fl(114, 175, 0, -1, null, "Flerovium", false), + Mc(115, 173, 0, -1, null, "Moscovium", false), + Lv(116, 177, 0, -1, null, "Livermorium", false), + Ts(117, 177, 0, -1, null, "Teness", false), + Og(118, 176, 0, -1, null, "Oganesson", false), + Tn(125, 198, 0, -1, null, "Tritanium", false), + + SpFe(26, 42, 0, -1, null, "Meteoric Iron", false), + De(22, 27, 0, -1, null, "Desh", false), + Oh(76, 125, 0, -1, null, "Oriharukon", false), + Di(500, 500, 0, -1, null, "Dimensionally Transcendent Matter", false), + + Ma(0, 0, 100, -1, null, "Magic", false), + Nq(130, 200, 0, -1, null, "Naquadah", false), + Nt(0, 100, 0, -1, null, "Neutronium", false), + + $H(-1, -0, 0, -1, null, "Anti-Hydrogen", false), + $D(-1, -1, 0, -1, "H", "Anti-Deuterium", true), + $T(-1, -2, 0, -1, "D", "Anti-Tritium", true), + $He(-2, -2, 0, -1, null, "Anti-Helium", false), + $He_3(-2, -1, 0, -1, "H&D", "Anti-Helium-3", true), + $Li(-3, -4, 0, -1, null, "Anti-Lithium", false), + $Be(-4, -5, 0, -1, null, "Anti-Beryllium", false), + $B(-5, -5, 0, -1, null, "Anti-Boron", false), + $C(-6, -6, 0, -1, null, "Anti-Carbon", false), + $N(-7, -7, 0, -1, null, "Anti-Nitrogen", false), + $O(-8, -8, 0, -1, null, "Anti-Oxygen", false), + $F(-9, -9, 0, -1, null, "Anti-Fluorine", false), + $Ne(-10, -10, 0, -1, null, "Anti-Neon", false), + $Na(-11, -11, 0, -1, null, "Anti-Sodium", false), + $Mg(-12, -12, 0, -1, null, "Anti-Magnesium", false), + $Al(-13, -13, 0, -1, null, "Anti-Aluminium", false), + $Si(-14, -14, 0, -1, null, "Anti-Silicon", false), + $P(-15, -15, 0, -1, null, "Anti-Phosphorus", false), + $S(-16, -16, 0, -1, null, "Anti-Sulfur", false), + $Cl(-17, -18, 0, -1, null, "Anti-Chlorine", false), + $Ar(-18, -22, 0, -1, null, "Anti-Argon", false), + $K(-19, -20, 0, -1, null, "Anti-Potassium", false), + $Ca(-20, -20, 0, -1, null, "Anti-Calcium", false), + $Sc(-21, -24, 0, -1, null, "Anti-Scandium", false), + $Ti(-22, -26, 0, -1, null, "Anti-Titanium", false), + $V(-23, -28, 0, -1, null, "Anti-Vanadium", false), + $Cr(-24, -28, 0, -1, null, "Anti-Chrome", false), + $Mn(-25, -30, 0, -1, null, "Anti-Manganese", false), + $Fe(-26, -30, 0, -1, null, "Anti-Iron", false), + $Co(-27, -32, 0, -1, null, "Anti-Cobalt", false), + $Ni(-28, -30, 0, -1, null, "Anti-Nickel", false), + $Cu(-29, -34, 0, -1, null, "Anti-Copper", false), + $Zn(-30, -35, 0, -1, null, "Anti-Zinc", false), + $Ga(-31, -39, 0, -1, null, "Anti-Gallium", false), + $Ge(-32, -40, 0, -1, null, "Anti-Germanium", false), + $As(-33, -42, 0, -1, null, "Anti-Arsenic", false), + $Se(-34, -45, 0, -1, null, "Anti-Selenium", false), + $Br(-35, -44, 0, -1, null, "Anti-Bromine", false), + $Kr(-36, -48, 0, -1, null, "Anti-Krypton", false), + $Rb(-37, -48, 0, -1, null, "Anti-Rubidium", false), + $Sr(-38, -49, 0, -1, null, "Anti-Strontium", false), + $Y(-39, -50, 0, -1, null, "Anti-Yttrium", false), + $Zr(-40, -51, 0, -1, null, "Anti-Zirconium", false), + $Nb(-41, -53, 0, -1, null, "Anti-Niobium", false), + $Mo(-42, -53, 0, -1, null, "Anti-Molybdenum", false), + $Tc(-43, -55, 0, -1, null, "Anti-Technetium", false), + $Ru(-44, -57, 0, -1, null, "Anti-Ruthenium", false), + $Rh(-45, -58, 0, -1, null, "Anti-Rhodium", false), + $Pd(-46, -60, 0, -1, null, "Anti-Palladium", false), + $Ag(-47, -60, 0, -1, null, "Anti-Silver", false), + $Cd(-48, -64, 0, -1, null, "Anti-Cadmium", false), + $In(-49, -65, 0, -1, null, "Anti-Indium", false), + $Sn(-50, -68, 0, -1, null, "Anti-Tin", false), + $Sb(-51, -70, 0, -1, null, "Anti-Antimony", false), + $Te(-52, -75, 0, -1, null, "Anti-Tellurium", false), + $I(-53, -74, 0, -1, null, "Anti-Iodine", false), + $Xe(-54, -77, 0, -1, null, "Anti-Xenon", false), + $Cs(-55, -77, 0, -1, null, "Anti-Caesium", false), + $Ba(-56, -81, 0, -1, null, "Anti-Barium", false), + $La(-57, -81, 0, -1, null, "Anti-Lantanium", false), + $Ce(-58, -82, 0, -1, null, "Anti-Cerium", false), + $Pr(-59, -81, 0, -1, null, "Anti-Praseodymium", false), + $Nd(-60, -84, 0, -1, null, "Anti-Neidymium", false), + $Pm(-61, -83, 0, -1, null, "Anti-Promethium", false), + $Sm(-62, -88, 0, -1, null, "Anti-Samarium", false), + $Eu(-63, -88, 0, -1, null, "Anti-Europium", false), + $Gd(-64, -93, 0, -1, null, "Anti-Gadolinium", false), + $Tb(-65, -93, 0, -1, null, "Anti-Terbium", false), + $Dy(-66, -96, 0, -1, null, "Anti-Dysprosium", false), + $Ho(-67, -97, 0, -1, null, "Anti-Holmium", false), + $Er(-68, -99, 0, -1, null, "Anti-Erbium", false), + $Tm(-69, -99, 0, -1, null, "Anti-Thulium", false), + $Yb(-70, -103, 0, -1, null, "Anti-Ytterbium", false), + $Lu(-71, -103, 0, -1, null, "Anti-Lutetium", false), + $Hf(-72, -106, 0, -1, null, "Anti-Hafnium", false), + $Ta(-73, -107, 0, -1, null, "Anti-Tantalum", false), + $W(-74, -109, 0, -1, null, "Anti-Wolframium", false), + $Re(-75, -111, 0, -1, null, "Anti-Rhenium", false), + $Os(-76, -114, 0, -1, null, "Anti-Osmium", false), + $Ir(-77, -115, 0, -1, null, "Anti-Iridium", false), + $Pt(-78, -117, 0, -1, null, "Anti-Platinum", false), + $Au(-79, -117, 0, -1, null, "Anti-Gold", false), + $Hg(-80, -120, 0, -1, null, "Anti-Mercury", false), + $Tl(-81, -123, 0, -1, null, "Anti-Thallium", false), + $Pb(-82, -125, 0, -1, null, "Anti-Lead", false), + $Bi(-83, -125, 0, -1, null, "Anti-Bismuth", false), + $Po(-84, -124, 0, -1, null, "Anti-Polonium", false), + $At(-85, -124, 0, -1, null, "Anti-Astatine", false), + $Rn(-86, -134, 0, -1, null, "Anti-Radon", false), + $Fr(-87, -134, 0, -1, null, "Anti-Francium", false), + $Ra(-88, -136, 0, -1, null, "Anti-Radium", false), + $Ac(-89, -136, 0, -1, null, "Anti-Actinium", false), + $Th(-90, -140, 0, -1, null, "Anti-Thorium", false), + $Pa(-91, -138, 0, -1, null, "Anti-Protactinium", false), + $U(-92, -146, 0, -1, null, "Anti-Uranium", false), + $U_235(-92, -143, 0, -1, null, "Anti-Uranium-235", true), + $Np(-93, -144, 0, -1, null, "Anti-Neptunium", false), + $Pu(-94, -152, 0, -1, null, "Anti-Plutonium", false), + $Pu_241(-94, -149, 0, -1, null, "Anti-Plutonium-241", true), + $Am(-95, -150, 0, -1, null, "Anti-Americium", false), + $Cm(-96, -153, 0, -1, null, "Anti-Curium", false), + $Bk(-97, -152, 0, -1, null, "Anti-Berkelium", false), + $Cf(-98, -153, 0, -1, null, "Anti-Californium", false), + $Es(-99, -153, 0, -1, null, "Anti-Einsteinium", false), + $Fm(-100, -157, 0, -1, null, "Anti-Fermium", false), + $Md(-101, -157, 0, -1, null, "Anti-Mendelevium", false), + $No(-102, -157, 0, -1, null, "Anti-Nobelium", false), + $Lr(-103, -159, 0, -1, null, "Anti-Lawrencium", false), + $Rf(-104, -161, 0, -1, null, "Anti-Rutherfordium", false), + $Db(-105, -163, 0, -1, null, "Anti-Dubnium", false), + $Sg(-106, -165, 0, -1, null, "Anti-Seaborgium", false), + $Bh(-107, -163, 0, -1, null, "Anti-Bohrium", false), + $Hs(-108, -169, 0, -1, null, "Anti-Hassium", false), + $Mt(-109, -167, 0, -1, null, "Anti-Meitnerium", false), + $Ds(-110, -171, 0, -1, null, "Anti-Darmstadtium", false), + $Rg(-111, -169, 0, -1, null, "Anti-Roentgenium", false), + $Cn(-112, -173, 0, -1, null, "Anti-Copernicium", false), + $Nh(-113, -171, 0, -1, null, "Anti-Nihonium", false), + $Fl(-114, -175, 0, -1, null, "Anti-Flerovium", false), + $Mc(-115, -173, 0, -1, null, "Anti-Moscovium", false), + $Lv(-116, -177, 0, -1, null, "Anti-Livermorium", false), + $Ts(-117, -177, 0, -1, null, "Anti-Tenness", false), + $Og(-118, -176, 0, -1, null, "Anti-Oganesson", false), + $Tn(-125, -198, 0, -1, null, "Anti-Tritanium", false), + + $SpFe(-26, -42, 0, -1, null, "Anti-Meteoric Iron", true), + $De(-22, -27, 0, -1, null, "Anti-Desh", true), + $Oh(-76, -125, 0, -1, null, "Anti-Oriharukon", true), + + $Ma(0, 0, -100, -1, null, "Anti-Magic", false), + $Nq(-130, -200, 0, -1, null, "Anti-Naquadah", false), + $Nt(0, -10000, 0, -1, null, "Anti-Neutronium", false); + + public final long mProtons, mNeutrons, mAdditionalMass, mHalfLifeSeconds; + public final String mName, mDecayTo; + public final boolean mIsIsotope; + + /** + * Links to every pure Material containing just this Element. + */ + // bartworks.system.material.werkstoff_loaders.registration.BridgeMaterialsLoader reassigns it, so no final here + @SuppressWarnings("NonFinalFieldInEnum") + public ArrayList<Materials> mLinkedMaterials = new ArrayList<>(); + + /** + * @param aProtons Amount of Protons. Antiprotons if negative. + * @param aNeutrons Amount of Neutrons. Antineutrons if negative. (I could have made mistakes with the + * Neutron amount calculation, please tell me if I did something wrong) + * @param aHalfLifeSeconds Amount of Half Life this Material has in Seconds. -1 for stable Materials. + * @param aDecayTo String representing the Elements it decays to. Separated by an '&' Character. + * @param aName Name of the Element + */ + Element(long aProtons, long aNeutrons, long aAdditionalMass, long aHalfLifeSeconds, String aDecayTo, String aName, + boolean aIsIsotope) { + mProtons = aProtons; + mNeutrons = aNeutrons; + mAdditionalMass = aAdditionalMass; + mHalfLifeSeconds = aHalfLifeSeconds; + mDecayTo = aDecayTo; + mName = aName; + mIsIsotope = aIsIsotope; + Companion.VALUES.put(name(), this); + } + + @Nonnull + public static Element get(String aMaterialName) { + return Companion.VALUES.getOrDefault(aMaterialName, _NULL); + } + + public long getProtons() { + return mProtons; + } + + public long getNeutrons() { + return mNeutrons; + } + + public long getMass() { + return mProtons + mNeutrons + mAdditionalMass; + } + + /** + * A companion object to workaround java limitations + */ + private static final class Companion { + + /** + * Why is this a separate map and populated by enum constructor instead of a Map prepoluated with values()? + * Because apparently there are people hacking into this enum via EnumHelper. + */ + private static final Map<String, Element> VALUES = new HashMap<>(); + } +} diff --git a/src/main/java/gregtech/api/enums/FluidState.java b/src/main/java/gregtech/api/enums/FluidState.java new file mode 100644 index 0000000000..5f6f8824f5 --- /dev/null +++ b/src/main/java/gregtech/api/enums/FluidState.java @@ -0,0 +1,17 @@ +package gregtech.api.enums; + +public enum FluidState { + + GAS, + LIQUID, + MOLTEN, + PLASMA, + SLURRY; + + public static final FluidState[] VALID_STATES = new FluidState[] { SLURRY, LIQUID, GAS, PLASMA, MOLTEN }; + + public static FluidState fromValue(int stateValue) { + return stateValue >= 0 && stateValue < FluidState.VALID_STATES.length ? FluidState.VALID_STATES[stateValue] + : FluidState.LIQUID; + } +} diff --git a/src/main/java/gregtech/api/enums/GTNH_ExtraMaterials.java b/src/main/java/gregtech/api/enums/GTNH_ExtraMaterials.java new file mode 100644 index 0000000000..b65ac53499 --- /dev/null +++ b/src/main/java/gregtech/api/enums/GTNH_ExtraMaterials.java @@ -0,0 +1,626 @@ +package gregtech.api.enums; + +import static gregtech.GT_Mod.GT_FML_LOGGER; + +import gregtech.api.interfaces.IMaterialHandler; + +public class GTNH_ExtraMaterials implements IMaterialHandler { + + public GTNH_ExtraMaterials() { + GT_FML_LOGGER.info("Registering GTNH-Materials (post Java 64kb limit)"); + Materials.add(this); + } + + /** + * This Class is for adding new Materials since Java has a Limiation of 64kb per Method / Class header + */ + public static Materials Signalum = new Materials( + -1, + TextureSet.SET_NONE, + 1.0F, + 0, + 2, + 1 | 2, + 255, + 255, + 255, + 0, + "Signalum", + "Signalum", + 0, + 0, + -1, + 0, + false, + false, + 3, + 1, + 1, + Dyes._NULL); + + public static Materials Lumium = new Materials( + -1, + TextureSet.SET_NONE, + 1.0F, + 0, + 2, + 1 | 2, + 255, + 255, + 255, + 0, + "Lumium", + "Lumium", + 0, + 0, + -1, + 0, + false, + false, + 3, + 1, + 1, + Dyes._NULL); + public static Materials EnrichedCopper = new Materials( + -1, + TextureSet.SET_NONE, + 1.0F, + 0, + 2, + 1 | 2, + 255, + 255, + 255, + 0, + "EnrichedCopper", + "Enriched Copper", + 0, + 0, + -1, + 0, + false, + false, + 3, + 1, + 1, + Dyes._NULL); + public static Materials DiamondCopper = new Materials( + -1, + TextureSet.SET_NONE, + 1.0F, + 0, + 2, + 1 | 2, + 255, + 255, + 255, + 0, + "DiamondCopper", + "Diamond Copper", + 0, + 0, + -1, + 0, + false, + false, + 3, + 1, + 1, + Dyes._NULL); + public static Materials TarPitch = new Materials( + -1, + TextureSet.SET_NONE, + 1.0F, + 0, + 2, + 1 | 2, + 255, + 255, + 255, + 0, + "TarPitch", + "Tar Pitch", + 0, + 0, + -1, + 0, + false, + false, + 3, + 1, + 1, + Dyes._NULL); + public static Materials LimePure = new Materials( + -1, + TextureSet.SET_NONE, + 1.0F, + 0, + 0, + 0, + 255, + 255, + 255, + 0, + "LimePure", + "Pure Lime", + 0, + 0, + -1, + 0, + false, + false, + 1, + 1, + 1, + Dyes.dyeLime); + public static Materials Wimalite = new Materials( + -1, + TextureSet.SET_NONE, + 1.0F, + 0, + 2, + 8, + 255, + 255, + 255, + 0, + "Wimalite", + "Wimalite", + 0, + 0, + -1, + 0, + false, + false, + 3, + 1, + 1, + Dyes.dyeYellow); + public static Materials Yellorite = new Materials( + -1, + TextureSet.SET_NONE, + 1.0F, + 0, + 2, + 8, + 255, + 255, + 255, + 0, + "Yellorite", + "Yellorite", + 0, + 0, + -1, + 0, + false, + false, + 3, + 1, + 1, + Dyes.dyeYellow); + public static Materials Quantum = new Materials( + -1, + TextureSet.SET_NONE, + 1.0F, + 0, + 0, + 0, + 255, + 255, + 255, + 0, + "Quantum", + "Quantum", + 0, + 0, + -1, + 0, + false, + false, + 1, + 1, + 1, + Dyes.dyeWhite); + public static Materials Turquoise = new Materials( + -1, + TextureSet.SET_NONE, + 1.0F, + 0, + 1, + 1, + 255, + 255, + 255, + 0, + "Turquoise", + "Turquoise", + 0, + 0, + -1, + 0, + false, + false, + 3, + 1, + 1, + Dyes._NULL); + public static Materials Tapazite = new Materials( + -1, + TextureSet.SET_NONE, + 1.0F, + 0, + 1, + 1, + 255, + 255, + 255, + 0, + "Tapazite", + "Tapazite", + 0, + 0, + -1, + 0, + false, + false, + 3, + 1, + 1, + Dyes.dyeGreen); + public static Materials Thyrium = new Materials( + -1, + TextureSet.SET_NONE, + 1.0F, + 0, + 1, + 1 | 2 | 8, + 255, + 255, + 255, + 0, + "Thyrium", + "Thyrium", + 0, + 0, + -1, + 0, + false, + false, + 3, + 1, + 1, + Dyes._NULL); + public static Materials Tourmaline = new Materials( + -1, + TextureSet.SET_RUBY, + 1.0F, + 0, + 1, + 1, + 255, + 255, + 255, + 0, + "Tourmaline", + "Tourmaline", + 0, + 0, + -1, + 0, + false, + false, + 3, + 1, + 1, + Dyes._NULL); + public static Materials Spinel = new Materials( + -1, + TextureSet.SET_NONE, + 1.0F, + 0, + 1, + 0, + 255, + 255, + 255, + 0, + "Spinel", + "Spinel", + 0, + 0, + -1, + 0, + false, + false, + 3, + 1, + 1, + Dyes._NULL); + public static Materials Starconium = new Materials( + -1, + TextureSet.SET_NONE, + 1.0F, + 0, + 1, + 1 | 2 | 8, + 255, + 255, + 255, + 0, + "Starconium", + "Starconium", + 0, + 0, + -1, + 0, + false, + false, + 3, + 1, + 1, + Dyes._NULL); + public static Materials Sugilite = new Materials( + -1, + TextureSet.SET_NONE, + 1.0F, + 0, + 1, + 1, + 255, + 255, + 255, + 0, + "Sugilite", + "Sugilite", + 0, + 0, + -1, + 0, + false, + false, + 3, + 1, + 1, + Dyes._NULL); + public static Materials Prismarine = new Materials( + -1, + TextureSet.SET_NONE, + 1.0F, + 0, + 2, + 1 | 4, + 255, + 255, + 255, + 0, + "Prismarine", + "Prismarine", + 0, + 0, + -1, + 0, + false, + false, + 3, + 1, + 1, + Dyes._NULL); + public static Materials GraveyardDirt = new Materials( + -1, + TextureSet.SET_NONE, + 1.0F, + 0, + 2, + 1, + 255, + 255, + 255, + 0, + "GraveyardDirt", + "Graveyard Dirt", + 0, + 0, + -1, + 0, + false, + false, + 3, + 1, + 1, + Dyes._NULL); + public static Materials Tennantite = new Materials( + -1, + TextureSet.SET_NONE, + 1.0F, + 0, + 2, + 1, + 255, + 255, + 255, + 0, + "Tennantite", + "Tennantite", + 0, + 0, + -1, + 0, + false, + false, + 3, + 1, + 1, + Dyes._NULL); + public static Materials Fairy = new Materials( + -1, + TextureSet.SET_NONE, + 1.0F, + 0, + 2, + 1 | 2, + 255, + 255, + 255, + 0, + "Fairy", + "Fairy", + 0, + 0, + -1, + 0, + false, + false, + 3, + 1, + 1, + Dyes._NULL); + public static Materials Ludicrite = new Materials( + -1, + TextureSet.SET_NONE, + 1.0F, + 0, + 2, + 1 | 2, + 255, + 255, + 255, + 0, + "Ludicrite", + "Ludicrite", + 0, + 0, + -1, + 0, + false, + false, + 3, + 1, + 1, + Dyes._NULL); + public static Materials AquaRegia = new Materials( + -1, + TextureSet.SET_NONE, + 1.0F, + 0, + 2, + 0, + 255, + 255, + 255, + 0, + "AquaRegia", + "Aqua Regia", + 0, + 0, + -1, + 0, + false, + false, + 3, + 1, + 1, + Dyes._NULL); + public static Materials SolutionBlueVitriol = new Materials( + -1, + TextureSet.SET_NONE, + 1.0F, + 0, + 2, + 0, + 255, + 255, + 255, + 0, + "SolutionBlueVitriol", + "Blue Vitriol Solution", + 0, + 0, + -1, + 0, + false, + false, + 3, + 1, + 1, + Dyes._NULL); + public static Materials SolutionNickelSulfate = new Materials( + -1, + TextureSet.SET_NONE, + 1.0F, + 0, + 2, + 0, + 255, + 255, + 255, + 0, + "SolutionNickelSulfate", + "Nickel Sulfate Solution", + 0, + 0, + -1, + 0, + false, + false, + 3, + 1, + 1, + Dyes._NULL); + public static Materials Lodestone = new Materials( + -1, + TextureSet.SET_NONE, + 1.0F, + 0, + 1, + 1 | 8, + 255, + 255, + 255, + 0, + "Lodestone", + "Lodestone", + 0, + 0, + -1, + 0, + false, + false, + 1, + 1, + 1, + Dyes._NULL); + public static Materials Luminite = new Materials( + -1, + TextureSet.SET_NONE, + 1.0F, + 0, + 1, + 1 | 8, + 250, + 250, + 250, + 0, + "Luminite", + "Luminite", + 0, + 0, + -1, + 0, + false, + false, + 3, + 1, + 1, + Dyes.dyeWhite); + + private static void initSubTags() { + SubTag.METAL.addTo(Signalum, Lumium, EnrichedCopper, DiamondCopper); + SubTag.NO_SMASHING.addTo(TarPitch); + } + + @Override + public void onMaterialsInit() { + initSubTags(); + } +} diff --git a/src/main/java/gregtech/api/enums/GTVoltageIndex.java b/src/main/java/gregtech/api/enums/GTVoltageIndex.java new file mode 100644 index 0000000000..c5c2c215b0 --- /dev/null +++ b/src/main/java/gregtech/api/enums/GTVoltageIndex.java @@ -0,0 +1,22 @@ +package gregtech.api.enums; + +public class GTVoltageIndex { + + public final static int ULV = 0; + public final static int LV = 1; + public final static int MV = 2; + public final static int HV = 3; + public final static int EV = 4; + public final static int IV = 5; + public final static int LuV = 6; + public final static int ZPM = 7; + public final static int UV = 8; + public final static int UHV = 9; + public final static int UEV = 10; + public final static int UIV = 11; + public final static int UMV = 12; + public final static int UXV = 13; + public final static int MAX = 14; + + private GTVoltageIndex() {} +} diff --git a/src/main/java/gregtech/api/enums/GT_HatchElement.java b/src/main/java/gregtech/api/enums/GT_HatchElement.java new file mode 100644 index 0000000000..3a21d5a2eb --- /dev/null +++ b/src/main/java/gregtech/api/enums/GT_HatchElement.java @@ -0,0 +1,112 @@ +package gregtech.api.enums; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import gregtech.api.interfaces.IHatchElement; +import gregtech.api.interfaces.metatileentity.IMetaTileEntity; +import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_Dynamo; +import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_Energy; +import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_Input; +import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_InputBus; +import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_Maintenance; +import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_Muffler; +import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_Output; +import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_OutputBus; +import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_MultiBlockBase; +import gregtech.api.util.GT_ExoticEnergyInputHelper; +import gregtech.api.util.IGT_HatchAdder; + +public enum GT_HatchElement implements IHatchElement<GT_MetaTileEntity_MultiBlockBase> { + + Muffler(GT_MetaTileEntity_MultiBlockBase::addMufflerToMachineList, GT_MetaTileEntity_Hatch_Muffler.class) { + + @Override + public long count(GT_MetaTileEntity_MultiBlockBase t) { + return t.mMufflerHatches.size(); + } + }, + Maintenance(GT_MetaTileEntity_MultiBlockBase::addMaintenanceToMachineList, + GT_MetaTileEntity_Hatch_Maintenance.class) { + + @Override + public long count(GT_MetaTileEntity_MultiBlockBase t) { + return t.mMaintenanceHatches.size(); + } + }, + InputHatch(GT_MetaTileEntity_MultiBlockBase::addInputHatchToMachineList, GT_MetaTileEntity_Hatch_Input.class) { + + @Override + public long count(GT_MetaTileEntity_MultiBlockBase t) { + return t.mInputHatches.size(); + } + }, + InputBus(GT_MetaTileEntity_MultiBlockBase::addInputBusToMachineList, GT_MetaTileEntity_Hatch_InputBus.class) { + + @Override + public long count(GT_MetaTileEntity_MultiBlockBase t) { + return t.mInputBusses.size(); + } + }, + OutputHatch(GT_MetaTileEntity_MultiBlockBase::addOutputHatchToMachineList, GT_MetaTileEntity_Hatch_Output.class) { + + @Override + public long count(GT_MetaTileEntity_MultiBlockBase t) { + return t.mOutputHatches.size(); + } + }, + OutputBus(GT_MetaTileEntity_MultiBlockBase::addOutputBusToMachineList, GT_MetaTileEntity_Hatch_OutputBus.class) { + + @Override + public long count(GT_MetaTileEntity_MultiBlockBase t) { + return t.mOutputBusses.size(); + } + }, + Energy(GT_MetaTileEntity_MultiBlockBase::addEnergyInputToMachineList, GT_MetaTileEntity_Hatch_Energy.class) { + + @Override + public long count(GT_MetaTileEntity_MultiBlockBase t) { + return t.mEnergyHatches.size(); + } + }, + Dynamo(GT_MetaTileEntity_MultiBlockBase::addDynamoToMachineList, GT_MetaTileEntity_Hatch_Dynamo.class) { + + @Override + public long count(GT_MetaTileEntity_MultiBlockBase t) { + return t.mDynamoHatches.size(); + } + }, + ExoticEnergy(GT_MetaTileEntity_MultiBlockBase::addExoticEnergyInputToMachineList) { + + @Override + public List<? extends Class<? extends IMetaTileEntity>> mteClasses() { + return GT_ExoticEnergyInputHelper.getAllClasses(); + } + + @Override + public long count(GT_MetaTileEntity_MultiBlockBase t) { + return t.getExoticEnergyHatches() + .size(); + } + },; + + private final List<Class<? extends IMetaTileEntity>> mteClasses; + private final IGT_HatchAdder<GT_MetaTileEntity_MultiBlockBase> adder; + + @SafeVarargs + GT_HatchElement(IGT_HatchAdder<GT_MetaTileEntity_MultiBlockBase> adder, + Class<? extends IMetaTileEntity>... mteClasses) { + this.mteClasses = Collections.unmodifiableList(Arrays.asList(mteClasses)); + this.adder = adder; + } + + @Override + public List<? extends Class<? extends IMetaTileEntity>> mteClasses() { + return mteClasses; + } + + public IGT_HatchAdder<? super GT_MetaTileEntity_MultiBlockBase> adder() { + return adder; + } +} diff --git a/src/main/java/gregtech/api/enums/GT_Values.java b/src/main/java/gregtech/api/enums/GT_Values.java new file mode 100644 index 0000000000..64e1907507 --- /dev/null +++ b/src/main/java/gregtech/api/enums/GT_Values.java @@ -0,0 +1,658 @@ +package gregtech.api.enums; + +import static gregtech.api.enums.Mods.GregTech; +import static gregtech.api.enums.Mods.IndustrialCraft2; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import net.minecraft.item.ItemStack; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.world.World; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.FluidTankInfo; +import net.minecraftforge.fluids.IFluidTank; +import net.minecraftforge.oredict.OreDictionary; + +import gregtech.api.fluid.FluidTankGT; +import gregtech.api.interfaces.IIconContainer; +import gregtech.api.interfaces.internal.IGT_Mod; +import gregtech.api.interfaces.internal.IGT_RecipeAdder; +import gregtech.api.net.IGT_NetworkHandler; + +/** + * Made for static imports, this Class is just a Helper. + * <p/> + * I am doing this to have a better Table alike view on my Code, so I can change things faster using the Block Selection + * Mode of eclipse. + * <p/> + * Go to "Window > Preferences > Java > Editor > Content Assist > Favorites" to set static importable Constant Classes + * such as this one as AutoCompleteable. + */ +@SuppressWarnings("unused") // API Legitimately has unused fields and methods +public class GT_Values { + // unused: A, C, D, G, H, I, J, K, N, O, Q, R, S, T + + // TODO: Rename Material Units to 'U' + // TODO: Rename OrePrefixes Class to 'P' + // TODO: Rename Materials Class to 'M' + + /** + * Empty String for an easier Call Hierarchy + */ + public static final String E = ""; + + /** + * The first 32 Bits + */ + @SuppressWarnings("PointlessBitwiseExpression") // Nicer source layout this way + public static final int[] B = new int[] { 1 << 0, 1 << 1, 1 << 2, 1 << 3, 1 << 4, 1 << 5, 1 << 6, 1 << 7, 1 << 8, + 1 << 9, 1 << 10, 1 << 11, 1 << 12, 1 << 13, 1 << 14, 1 << 15, 1 << 16, 1 << 17, 1 << 18, 1 << 19, 1 << 20, + 1 << 21, 1 << 22, 1 << 23, 1 << 24, 1 << 25, 1 << 26, 1 << 27, 1 << 28, 1 << 29, 1 << 30, 1 << 31 }; + + /** + * Renamed from "MATERIAL_UNIT" to just "M" + * <p/> + * This is worth exactly one normal Item. This Constant can be divided by many commonly used Numbers such as 1, 2, + * 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 15, 16, 18, 20, 21, 24, ... 64 or 81 without losing precision and is for that + * reason used as Unit of Amount. But it is also small enough to be multiplied with larger Numbers. + * <p/> + * This is used to determine the amount of Material contained inside a prefixed Ore. For example Nugget = M / 9 as + * it contains out of 1/9 of an Ingot. + */ + public static final long M = 3628800; + + /** + * Renamed from "FLUID_MATERIAL_UNIT" to just "L" + * <p/> + * Fluid per Material Unit (Prime Factors: 3 * 3 * 2 * 2 * 2 * 2) + */ + public static final long L = 144; + + /** + * The Item WildCard Tag. Even shorter than the "-1" of the past + */ + public static final short W = OreDictionary.WILDCARD_VALUE; + + /** + * The Voltage Tiers. Use this Array instead of the old named Voltage Variables + */ + public static final long[] V = new long[] { 8L, 32L, 128L, 512L, 2048L, 8192L, 32_768L, 131_072L, 524_288L, + 2_097_152L, 8_388_608L, 33_554_432L, 134_217_728L, 536_870_912L, Integer.MAX_VALUE - 7, + // Error tier to prevent out of bounds errors. Not really a real tier (for now). + 8_589_934_592L }; + + /** + * The Voltage Practical. These are recipe voltage you should use if you expect the recipe to use a full amp of that + * tier. These leave a bit of headroom for cable and transformer losses, but not enough to make it a great gain. + */ + // this will correctly map ULV to 7. + public static final long[] VP = Arrays.stream(V) + .map( + i -> BigInteger.valueOf(i) + .multiply(BigInteger.valueOf(30)) + .divide(BigInteger.valueOf(32)) + .longValueExact()) + .toArray(); + // TODO:Adding that in coremod!!! + // TODO:tier 14,15 wires and transformers only (not even cables !!!) + // TODO:tier 12,13 the above + batteries, battery buffers, (maybe cables,12 also works for machines) + // TODO:tier 10,11 the above + chargers and other machines, (cables would be nice) + // TODO:tier 9 machines and batteries + + // TODO:AND ALL THE MATERIALS... for that + // TODO:LIST OF MACHINES WITH POINTLESS TIERS (unless you implement some other tiering mechanism like reducing eu + // cost if time=1tick) + // Macerator/Compressor/Furnace... and for cheap recipes any + + /** + * Array of Maximum Amperes at given Tier index + * <p> + * keeping Voltage*Amps < Integer.MAX_VALUE-7 for machines (and tier logic 4x EUt 2/ time) + * </p> + * <p> + * AMV[4]= max amps at tier 4 + * </p> + */ + public static final long[] AatV = new long[] { 268435455, 67108863, 16777215, 4194303, 1048575, 262143, 65535, + 16383, 4095, 1023, 255, 63, 15, 3, 1, 1 }; + /** + * The short Names for the Voltages + */ + public static final String[] VN = new String[] { "ULV", // 0 + "LV", // 1 + "MV", // 2 + "HV", // 3 + "EV", // 4 + "IV", // 5 + "LuV", // 6 + "ZPM", // 7 + "UV", // 8 + "UHV", // 9 + "UEV", // 10 + "UIV", // 11 + "UMV", // 12 + "UXV", // 13 + "MAX", // 14 + "MAX+" // 15 + }; + + /** + * The long Names for the Voltages + */ + public static final String[] VOLTAGE_NAMES = new String[] { "Ultra Low Voltage", // 0 + "Low Voltage", // 1 + "Medium Voltage", // 2 + "High Voltage", // 3 + "Extreme Voltage", // 4 + "Insane Voltage", // 5 + "Ludicrous Voltage", // 6 + "ZPM Voltage", // 7 + "Ultimate Voltage", // 8 + "Ultimate High Voltage", // 9 + "Ultimate Extreme Voltage", // 10 + "Ultimate Insane Voltage", // 11 + "Ultimate Mega Voltage", // 12 + "Ultimate Extended Mega Voltage", // 13 + "Maximum Voltage", // 14 + "Error Voltage, report this" // 15 + }; + + public static final String[] TIER_COLORS = new String[] { EnumChatFormatting.RED.toString(), // ULV, 0 + EnumChatFormatting.GRAY.toString(), // LV, 1 + EnumChatFormatting.GOLD.toString(), // MV, 2 + EnumChatFormatting.YELLOW.toString(), // HV, 3 + EnumChatFormatting.DARK_GRAY.toString(), // EV, 4 + EnumChatFormatting.GREEN.toString(), // IV, 5 + EnumChatFormatting.LIGHT_PURPLE.toString(), // LuV, 6 + EnumChatFormatting.AQUA.toString(), // ZPM, 7 + EnumChatFormatting.DARK_GREEN.toString(), // UV, 8 + EnumChatFormatting.DARK_RED.toString(), // UHV, 9 + EnumChatFormatting.DARK_PURPLE.toString(), // UEV, 10 + EnumChatFormatting.DARK_BLUE.toString() + EnumChatFormatting.BOLD, // UIV, 11 + EnumChatFormatting.RED.toString() + EnumChatFormatting.BOLD + EnumChatFormatting.UNDERLINE, // UMV, 12 + EnumChatFormatting.DARK_RED.toString() + EnumChatFormatting.BOLD + EnumChatFormatting.UNDERLINE, // UXV, 13 + EnumChatFormatting.WHITE.toString() + EnumChatFormatting.BOLD + EnumChatFormatting.UNDERLINE, // MAX, 14 + EnumChatFormatting.WHITE.toString() + EnumChatFormatting.BOLD + + EnumChatFormatting.UNDERLINE + + EnumChatFormatting.ITALIC, // MAX+, 15 + }; + + /** + * This way it is possible to have a Call Hierarchy of NullPointers in ItemStack based Functions, and also because + * most of the time I don't know what kind of Data Type the "null" stands for + */ + public static final ItemStack NI = null; + /** + * This way it is possible to have a Call Hierarchy of NullPointers in FluidStack based Functions, and also because + * most of the time I don't know what kind of Data Type the "null" stands for + */ + public static final FluidStack NF = null; + /** + * MOD ID Strings, since they are very common Parameters. + */ + @Deprecated + public static final String MOD_ID = "gregtech"; + @Deprecated + public static final String MOD_ID_IC2 = "IC2"; + @Deprecated + public static final String MOD_ID_NC = "IC2NuclearControl"; + @Deprecated + public static final String MOD_ID_TC = "Thaumcraft"; + @Deprecated + public static final String MOD_ID_TF = "TwilightForest"; + @Deprecated + public static final String MOD_ID_RC = "Railcraft"; + @Deprecated + public static final String MOD_ID_TE = "ThermalExpansion"; + @Deprecated + public static final String MOD_ID_AE = "appliedenergistics2"; + @Deprecated + public static final String MOD_ID_TFC = "terrafirmacraft"; + @Deprecated + public static final String MOD_ID_PFAA = "PFAAGeologica"; + @Deprecated + public static final String MOD_ID_FR = "Forestry"; + @Deprecated + public static final String MOD_ID_HaC = "harvestcraft"; + @Deprecated + public static final String MOD_ID_APC = "AppleCore"; + @Deprecated + public static final String MOD_ID_MaCr = "magicalcrops"; + @Deprecated + public static final String MOD_ID_GaEn = "ganysend"; + @Deprecated + public static final String MOD_ID_GaSu = "ganyssurface"; + @Deprecated + public static final String MOD_ID_GaNe = "ganysnether"; + @Deprecated + public static final String MOD_ID_BC_SILICON = "BuildCraft|Silicon"; + @Deprecated + public static final String MOD_ID_BC_TRANSPORT = "BuildCraft|Transport"; + @Deprecated + public static final String MOD_ID_BC_FACTORY = "BuildCraft|Factory"; + @Deprecated + public static final String MOD_ID_BC_ENERGY = "BuildCraft|Energy"; + @Deprecated + public static final String MOD_ID_BC_BUILDERS = "BuildCraft|Builders"; + @Deprecated + public static final String MOD_ID_BC_CORE = "BuildCraft|Core"; + @Deprecated + public static final String MOD_ID_GC_CORE = "GalacticraftCore"; + @Deprecated + public static final String MOD_ID_GC_MARS = "GalacticraftMars"; + @Deprecated + public static final String MOD_ID_GC_PLANETS = "GalacticraftPlanets"; + @Deprecated + public static final String MOD_ID_DC = "dreamcraft"; + @Deprecated + public static final String MOD_ID_GTPP = "miscutils"; + /** + * File Paths and Resource Paths + */ + @Deprecated + public static final String TEX_DIR = "textures/"; + @Deprecated + public static final String TEX_DIR_GUI = TEX_DIR + "gui/"; + @Deprecated + public static final String TEX_DIR_ITEM = TEX_DIR + "items/"; + @Deprecated + public static final String TEX_DIR_BLOCK = TEX_DIR + "blocks/"; + @Deprecated + public static final String TEX_DIR_ENTITY = TEX_DIR + "entity/"; + @Deprecated + public static final String TEX_DIR_ASPECTS = TEX_DIR + "aspects/"; + @Deprecated + public static final String RES_PATH = GregTech.getResourcePath(TEX_DIR); + @Deprecated + public static final String RES_PATH_GUI = GregTech.getResourcePath("textures", "gui/"); + @Deprecated + public static final String RES_PATH_ITEM = GregTech.getResourcePath(); + @Deprecated + public static final String RES_PATH_BLOCK = GregTech.getResourcePath(); + @Deprecated + public static final String RES_PATH_ENTITY = GregTech.getResourcePath("textures", "entity/"); + @Deprecated + public static final String RES_PATH_ASPECTS = GregTech.getResourcePath("textures", "aspects/"); + @Deprecated + public static final String RES_PATH_MODEL = GregTech.getResourcePath("textures", "models/"); + @Deprecated + public static final String RES_PATH_IC2 = IndustrialCraft2.getResourcePath(); + + /** + * NBT String Keys + */ + public static final class NBT { + + public static final String COLOR = "gt.color", // Integer + COVERS = "gt.covers", // String + CUSTOM_NAME = "name", // String + DISPLAY = "gt.display", // String + TIER = "gt.tier", // Number + FACING = "gt.facing", // Byte + LOCK_UPGRADE = "gt.locked", // Boolean + MATERIAL = "gt.material", // String containing the Material Name. + MODE = "gt.mode", // Number + ALLOWED_MODES = "gt.amode", // Number + MTE_ID = "gt.mte.id", // Containing the MTE ID + MTE_REG = "gt.mte.reg", // Containing the MTE Registry ID + OWNER = "gt.owner", // String + OWNER_UUID = "gt.ownerUuid", // UUID (String) + + // Machines + ACTIVE = "gt.active", // Boolean + FLUID_OUT = "gt.fluidout", // Output Fluid + ITEM_OUT = "gt.itemout", // Output Item + PARALLEL = "gt.parallel", // Number + TANK_CAPACITY = "gt.tankcap", // Number + TANK_IN = "gt.tank.in.", // FluidStack + TANK_OUT = "gt.tank.out.", // FluidStack + TEXTURE_FOLDER = "gt.texture.folder", // String + INV_INPUT_SIZE = "gt.invsize.in", // Number + INV_OUTPUT_SIZE = "gt.invsize.out", // Number + INV_INPUT_LIST = "gt.invlist.in", // NBT List + INV_OUTPUT_LIST = "gt.invlist.out", // NBT List + VOLTAGE = "gt.voltage", // Number + AMPERAGE = "gt.amperage", // Number + STORED_ENERGY = "gt.stored.energy", // Number + MAXIMUM_ENERGY = "gt.maximum.energy", // Number + EUT_CONSUMPTION = "gt.eut.consumption", // Number + BURN_TIME_LEFT = "gt.burn.time.left", // Number + TOTAL_BURN_TIME = "gt.total.burn.time", // Number + ALLOWED_WORK = "gt.allowed.work", // Boolean + TASKS = "gt.tasks", // Compound + + // MultiBlock + STRUCTURE_OK = "gt.structure.ok", ROTATION = "gt.eRotation", FLIP = "gt.eFlip", TARGET = "gt.target", // Boolean + TARGET_X = "gt.target.x", // Number + TARGET_Y = "gt.target.y", // Number + TARGET_Z = "gt.target.z", // Number + LOCKED_FLUID = "gt.locked.fluid", // String + LOCKED_INVENTORY = "gt.locked.inv", // String + LOCKED_INVENTORY_INDEX = "gt.locked.inv.index", // Number + UPGRADE_INVENTORY_SIZE = "gt.invsize.upg", // String + UPGRADE_INVENTORY_UUID = "gt.invuuid.upg", // String + UPGRADE_INVENTORY_NAME = "gt.invname.upg", // String + UPGRADE_INVENTORIES_INPUT = "gt.invlist.upg.in", // NBT List + UPGRADE_INVENTORIES_OUTPUT = "gt.invlist.upg.out", // NBT List + UPGRADE_TANK_CAPACITY = "gt.tank.cap.upg", // Long + UPGRADE_TANK_COUNT = "gt.tank.ct.upg", // Int + UPGRADE_TANK_CAPACITY_MULTIPLIER = "gt.tank.cap.mult.upg", // Long + UPGRADE_TANK_UUID = "gt.tankuuid.upg", // String + UPGRADE_TANK_NAME = "gt.tankname.upg", // String + UPGRADE_TANKS_INPUT = "gt.tanklist.upg.in", // NBT List + UPGRADE_TANKS_OUTPUT = "gt.tanklist.upg.out", // NBT List + UPGRADE_TANKS_PREFIX = "gt.tank.upg", // NBT Tag + UPGRADE_AMPERAGE = "gt.amp.upg", // Long + SEPARATE_INPUTS = "gt.separate.inputs", // Boolean + VOIDING_MODE = "gt.voiding.mode", // String + BATCH_MODE = "gt.batch.mode", // Boolean + RECIPE_LOCK = "gt.recipe.lock", // Boolean + + // Logic + POWER_LOGIC = "gt.pow.logic", // NBT Tag + POWER_LOGIC_STORED_ENERGY = "gt.pow.energy", // Number + POWER_LOGIC_ENERGY_CAPACITY = "gt.pow.energy.cap", // Number + POWER_LOGIC_VOLTAGE = "gt.pow.volt", // Number + POWER_LOGIC_AMPERAGE = "gt.pow.amp", // Number + POWER_LOGIC_TYPE = "gt.pow.type", // Number + empty_ = ""; + } + + /** The Color White as RGB Short Array. */ + public static final short[] UNCOLORED_RGBA = { 255, 255, 255, 255 }; + /** The Color White as simple Integer (0x00ffffff). */ + public static final int UNCOLORED = 0x00ffffff; + + /** + * Sides + */ + public static final byte SIDE_BOTTOM = 0, SIDE_DOWN = 0, SIDE_TOP = 1, SIDE_UP = 1, SIDE_NORTH = 2, // Also a Side + // with a + // stupidly + // mirrored + // Texture + SIDE_SOUTH = 3, SIDE_WEST = 4, SIDE_EAST = 5, // Also a Side with a stupidly mirrored Texture + SIDE_ANY = 6, SIDE_UNKNOWN = 6, SIDE_INVALID = 6, SIDE_INSIDE = 6, SIDE_UNDEFINED = 6; + + /** Compass alike Array for the proper ordering of North, East, South and West. */ + public static final byte[] COMPASS_DIRECTIONS = { SIDE_NORTH, SIDE_EAST, SIDE_SOUTH, SIDE_WEST }; + + /** + * An Array containing all Sides which follow the Condition, in order to iterate over them for example. + */ + public static final byte[] ALL_SIDES = { 0, 1, 2, 3, 4, 5, 6 }, ALL_VALID_SIDES = { 0, 1, 2, 3, 4, 5 }; + + /** + * For Facing Checks. + */ + public static final boolean[] INVALID_SIDES = { false, false, false, false, false, false, true }, + VALID_SIDES = { true, true, true, true, true, true, false }; + + /** + * Side->Offset Mappings. + */ + public static final byte[] OFFX = { 0, 0, 0, 0, -1, +1, 0 }, OFFY = { -1, +1, 0, 0, 0, 0, 0 }, + OFFZ = { 0, 0, -1, +1, 0, 0, 0 }; + + /** + * Side->Opposite Mappings. + **/ + public static final byte[] OPOS = { 1, 0, 3, 2, 5, 4, 6 }; + + /** + * [Facing,Side]->Side Mappings for Blocks, which don't face up- and downwards. 0 = bottom, 1 = top, 2 = left, 3 = + * front, 4 = right, 5 = back, 6 = undefined. + */ + public static final byte[][] FACING_ROTATIONS = { { 0, 1, 2, 3, 4, 5, 6 }, { 0, 1, 2, 3, 4, 5, 6 }, + { 0, 1, 3, 5, 4, 2, 6 }, { 0, 1, 5, 3, 2, 4, 6 }, { 0, 1, 2, 4, 3, 5, 6 }, { 0, 1, 4, 2, 5, 3, 6 }, + { 0, 1, 2, 3, 4, 5, 6 } }; + + /** + * The Mod Object itself. That is the GT_Mod-Object. It's needed to open GUI's and similar. + */ + public static IGT_Mod GT; + /** + * Use this Object to add Recipes. (Recipe Adder) + */ + public static IGT_RecipeAdder RA; + /** + * For Internal Usage (Network) + */ + public static IGT_NetworkHandler NW; + /** + * Control percentage of filled 3x3 chunks. Lower number means less oreveins spawn + */ + public static int oreveinPercentage; + /** + * Control number of attempts to find a valid orevein. Generally this maximum limit isn't hit, selecting a vein is + * cheap + */ + public static int oreveinAttempts; + /** + * Control number of attempts to place a valid ore vein. + * <p> + * If a vein wasn't placed due to height restrictions, completely in the water, etc, another attempt is tried. + * </p> + */ + public static int oreveinMaxPlacementAttempts; + /** + * Whether to place small ores as placer ores for an orevein + */ + public static boolean oreveinPlacerOres; + /** + * Multiplier to control how many placer ores get generated. + */ + public static int oreveinPlacerOresMultiplier; + /** + * Not really Constants, but they set using the Config and therefore should be constant (those are for the Debug + * Mode) + */ + public static boolean D1 = false, D2 = false; + /** + * Debug parameter for cleanroom testing. + */ + public static boolean debugCleanroom = false; + /** + * Debug parameter for driller testing. + */ + public static boolean debugDriller = false; + /** + * Debug parameter for world generation. Tracks chunks added/removed from run queue. + */ + public static boolean debugWorldGen = false; + /** + * Debug parameter for orevein generation. + */ + public static boolean debugOrevein = false; + /** + * Debug parameter for small ore generation. + */ + public static boolean debugSmallOres = false; + /** + * Debug parameter for stones generation. + */ + public static boolean debugStones = false; + /** + * Debug parameter for single block pump + */ + public static boolean debugBlockPump = false; + /** + * Debug parameter for single block miner + */ + public static boolean debugBlockMiner = false; + /** + * Debug parameter for entity cramming reduction + */ + public static boolean debugEntityCramming = false; + /** + * Debug parameter for {@link gregtech.api.util.GT_ChunkAssociatedData} + */ + public static boolean debugWorldData = false; + /** + * Parameter if multi tile entities (MuTEs) should be enabled in the pack. Turned off by default until + * implementation is done. + */ + public static boolean enableMultiTileEntities = false; + /** + * Number of ticks between sending sound packets to clients for electric machines. Default is 1.5 seconds. Trying to + * mitigate lag and FPS drops. + */ + public static int ticksBetweenSounds = 30; + /** + * If you have to give something a World Parameter but there is no World... (Dummy World) + */ + public static World DW; + + /** + * This will prevent NEI from crashing but spams the Log. + */ + public static boolean allow_broken_recipemap = false; + /** + * This will set the percentage how much ReinforcedGlass is Allowed in Cleanroom Walls. + */ + public static float cleanroomGlass = 5.0f; + /** + * This will let machines such as drills and pumps chunkload their work area. + */ + public static boolean enableChunkloaders = true; + /** + * This will make all chunkloading machines act as World Anchors (true) or Passive Anchors (false) + */ + public static boolean alwaysReloadChunkloaders = false; + + public static boolean debugChunkloaders = false; + public static boolean cls_enabled; + public static final Set<String> mCTMEnabledBlock = new HashSet<>(); + public static final Set<String> mCTMDisabledBlock = new HashSet<>(); + + public static final int STEAM_PER_WATER = 160; + /** + * If true, then digital chest with AE2 storage bus will be accessible only through AE2 + */ + public static boolean disableDigitalChestsExternalAccess = false; + + public static boolean lateConfigSave = true; + public static boolean worldTickHappened = false; + + public static final int[] emptyIntArray = new int[0]; + + public static final IFluidTank[] emptyFluidTank = new IFluidTank[0]; + public static final FluidTankGT[] emptyFluidTankGT = new FluidTankGT[0]; + public static final FluidTankInfo[] emptyFluidTankInfo = new FluidTankInfo[0]; + public static final FluidStack[] emptyFluidStack = new FluidStack[0]; + public static final ItemStack[] emptyItemStackArray = new ItemStack[0]; + public static final IIconContainer[] emptyIconContainerArray = new IIconContainer[3]; + + /** + * Pretty formatting for author names. + */ + public static final String Colen = "" + EnumChatFormatting.DARK_RED + + EnumChatFormatting.BOLD + + EnumChatFormatting.ITALIC + + EnumChatFormatting.UNDERLINE + + "C" + + EnumChatFormatting.GOLD + + EnumChatFormatting.BOLD + + EnumChatFormatting.ITALIC + + EnumChatFormatting.UNDERLINE + + "o" + + EnumChatFormatting.GREEN + + EnumChatFormatting.BOLD + + EnumChatFormatting.ITALIC + + EnumChatFormatting.UNDERLINE + + "l" + + EnumChatFormatting.DARK_AQUA + + EnumChatFormatting.BOLD + + EnumChatFormatting.ITALIC + + EnumChatFormatting.UNDERLINE + + "e" + + EnumChatFormatting.DARK_PURPLE + + EnumChatFormatting.BOLD + + EnumChatFormatting.ITALIC + + EnumChatFormatting.UNDERLINE + + "n"; + + public static final String AuthorColen = "Author: " + Colen; + public static final String AuthorKuba = "Author: " + EnumChatFormatting.DARK_RED + + EnumChatFormatting.BOLD + + "k" + + EnumChatFormatting.RED + + EnumChatFormatting.BOLD + + "u" + + EnumChatFormatting.GOLD + + EnumChatFormatting.BOLD + + "b" + + EnumChatFormatting.YELLOW + + EnumChatFormatting.BOLD + + "a" + + EnumChatFormatting.DARK_GREEN + + EnumChatFormatting.BOLD + + "6" + + EnumChatFormatting.GREEN + + EnumChatFormatting.BOLD + + "0" + + EnumChatFormatting.AQUA + + EnumChatFormatting.BOLD + + "0" + + EnumChatFormatting.DARK_AQUA + + EnumChatFormatting.BOLD + + "0"; + + public static final String AuthorBlueWeabo = "Author: " + EnumChatFormatting.BLUE + + EnumChatFormatting.BOLD + + "Blue" + + EnumChatFormatting.AQUA + + EnumChatFormatting.BOLD + + "Weabo"; + + public static final String Authorminecraft7771 = "Author: " + EnumChatFormatting.BLUE + + EnumChatFormatting.LIGHT_PURPLE + + "minecraft7771"; + + public static final String AuthorQuerns = "Author: " + EnumChatFormatting.RED + "Querns"; + public static final String AuthorSilverMoon = "Author: " + EnumChatFormatting.AQUA + "SilverMoon"; + public static final String AuthorTheEpicGamer274 = "Author: " + "TheEpicGamer274"; + + // 7.5F comes from GT_Tool_Turbine_Large#getBaseDamage() given huge turbines are the most efficient now. + public static double getMaxPlasmaTurbineEfficiencyFromMaterial(Materials material) { + return (5F + (7.5F + material.mToolQuality)) / 10.0; + } + + // Called once in GT_Client on world load, has to be called late so that Materials is populated. + public static void calculateMaxPlasmaTurbineEfficiency() { + + ArrayList<Double> effArray = new ArrayList<>(); + + // Iteration seems to work but need to check turbine as all items appear null. + for (Materials material : Materials.values()) { + effArray.add(getMaxPlasmaTurbineEfficiencyFromMaterial(material)); + } + + maxPlasmaTurbineEfficiency = Collections.max(effArray); + } + + private static double maxPlasmaTurbineEfficiency; + + public static double getMaxPlasmaTurbineEfficiency() { + return maxPlasmaTurbineEfficiency; + } + + private static final long[] EXPLOSION_LOOKUP_V = new long[] { V[0], V[1], V[2], V[3], V[4], V[4] * 2, V[5], V[6], + V[7], V[8], V[8] * 2, V[9], V[10], V[11], V[12], V[12] * 2, V[13], V[14], V[15] }; + private static final float[] EXPLOSION_LOOKUP_POWER = new float[] { 1.0F, 2.0F, 3.0F, 4.0F, 5.0F, 6.0F, 7.0F, 8.0F, + 9.0F, 10.0F, 11.0F, 12.0F, 13.0F, 14.0F, 15.0F, 16.0F, 17.0F, 18.0F, 19.0F, 20.0F }; + + public static float getExplosionPowerForVoltage(long voltage) { + for (int i = 0; i < EXPLOSION_LOOKUP_V.length; i++) { + if (voltage < EXPLOSION_LOOKUP_V[i]) { + return EXPLOSION_LOOKUP_POWER[i]; + } + } + return EXPLOSION_LOOKUP_POWER[EXPLOSION_LOOKUP_POWER.length - 1]; + } +} diff --git a/src/main/java/gregtech/api/enums/HeatingCoilLevel.java b/src/main/java/gregtech/api/enums/HeatingCoilLevel.java new file mode 100644 index 0000000000..f281695d5a --- /dev/null +++ b/src/main/java/gregtech/api/enums/HeatingCoilLevel.java @@ -0,0 +1,101 @@ +package gregtech.api.enums; + +import javax.annotation.Nonnull; + +import net.minecraft.util.StatCollector; + +public enum HeatingCoilLevel { + + None, // 0 + ULV, // Not implemented 901 + LV, // Cupronickel 1801 + MV, // KANTHAL 2701 + HV, // NICHROME 3601 + EV, // TPVALLOY 4501 + IV, // HSSG 5401 + LuV, // HSSS 6301 + ZPM, // NAQUADAH 7201 + UV, // NAQUADAHALLOY 8101 + UHV, // TRINIUM 9001 + UEV, // ELECTRUMFLUX 9901 + UIV, // AWAKENEDDRACONIUM 10801 + UMV, // INFINITY 11701 + UXV, // HYPOGEN 12601 + MAX, // ETERNAL 13501 + ; + + private static final HeatingCoilLevel[] VALUES = values(); + + /** + * @return the coil heat, used for recipes in the Electronic Blast Furnace for example. + */ + public long getHeat() { + return this == None ? 0 : 1L + (900L * this.ordinal()); + } + + /** + * @return the coil tier, used for discount in the Pyrolyse Oven for example. LV == 0 + */ + public byte getTier() { + return (byte) (this.ordinal() - 2); + } + + /** + * @return the coil Level, used for Parallels in the Multi Furnace for example. + */ + public byte getLevel() { + return (byte) (1 << Math.min(Math.max(0, this.ordinal() - 2), 4)); + } + + /** + * @return the coil Discount, used for discount in the Multi Furnace for example + */ + public int getCostDiscount() { + return 1 << Math.max(0, this.ordinal() - 5); + } + + /** + * @return Translated name of this coil + */ + public String getName() { + return StatCollector.translateToLocal("GT5U.coil." + this); + } + + @Nonnull + public static HeatingCoilLevel getFromTier(byte tier) { + if (tier < 0 || tier > getMaxTier()) return HeatingCoilLevel.None; + + return VALUES[tier + 2]; + } + + /** + * @param applyColor Whether to apply tiered color + * @return Translated coil name. Heat exceeding MAX is represented as "Eternal+". + */ + @Nonnull + public static String getDisplayNameFromHeat(int heat, boolean applyColor) { + for (HeatingCoilLevel heatLevel : VALUES) { + if (heatLevel == HeatingCoilLevel.None || heatLevel == HeatingCoilLevel.ULV) continue; + if (heatLevel.getHeat() >= heat) { + String name = heatLevel.getName(); + if (applyColor) { + name = GT_Values.TIER_COLORS[heatLevel.getTier() + 1] + name; + } + return name; + } + } + String name = HeatingCoilLevel.MAX.getName() + "+"; + if (applyColor) { + name = GT_Values.TIER_COLORS[HeatingCoilLevel.MAX.getTier() + 1] + name; + } + return name; + } + + public static int size() { + return VALUES.length; + } + + public static int getMaxTier() { + return VALUES.length - 1 - 2; + } +} diff --git a/src/main/java/gregtech/api/enums/InventoryType.java b/src/main/java/gregtech/api/enums/InventoryType.java new file mode 100644 index 0000000000..f8e3c47741 --- /dev/null +++ b/src/main/java/gregtech/api/enums/InventoryType.java @@ -0,0 +1,7 @@ +package gregtech.api.enums; + +public enum InventoryType { + Input, + Output, + Both; +} diff --git a/src/main/java/gregtech/api/enums/ItemList.java b/src/main/java/gregtech/api/enums/ItemList.java new file mode 100644 index 0000000000..0773afa693 --- /dev/null +++ b/src/main/java/gregtech/api/enums/ItemList.java @@ -0,0 +1,2238 @@ +package gregtech.api.enums; + +import static gregtech.api.enums.GT_Values.NI; +import static gregtech.api.enums.GT_Values.W; + +import java.util.Locale; + +import net.minecraft.block.Block; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraftforge.fluids.Fluid; + +import gregtech.api.interfaces.IItemContainer; +import gregtech.api.util.GT_LanguageManager; +import gregtech.api.util.GT_Log; +import gregtech.api.util.GT_ModHandler; +import gregtech.api.util.GT_OreDictUnificator; +import gregtech.api.util.GT_Utility; + +/** + * Class containing all non-OreDict Items of GregTech. + */ +public enum ItemList implements IItemContainer { + + Display_ITS_FREE, + Display_Fluid, + FR_Lemon, + FR_Mulch, + FR_Fertilizer, + FR_Compost, + FR_Silk, + FR_Wax, + FR_RefractoryWax, + FR_WaxCapsule, + FR_RefractoryCapsule, + FR_Stick, + FR_Casing_Impregnated, + FR_Casing_Sturdy, + FR_Casing_Hardened, + FR_Bee_Drone, + FR_Bee_Princess, + FR_Bee_Queen, + FR_Tree_Sapling, + FR_Butterfly, + FR_Larvae, + FR_Serum, + FR_Caterpillar, + FR_PollenFertile, + TF_LiveRoot, + TF_Vial_FieryBlood, + TF_Vial_FieryTears, + RC_ShuntingWire, + RC_ShuntingWireFrame, + RC_Rail_Reinforced, + RC_Rail_Electric, + RC_Rail_Standard, + RC_Rail_Wooden, + RC_Rail_Adv, + RC_Rail_HS, + RC_Tie_Wood, + RC_Tie_Stone, + RC_Bed_Wood, + RC_Bed_Stone, + RC_Rebar, + TC_Thaumometer, + IC2_Item_Casing_Tin, + IC2_Item_Casing_Copper, + IC2_Item_Casing_Iron, + IC2_Item_Casing_Steel, + IC2_Item_Casing_Lead, + IC2_Item_Casing_Bronze, + IC2_Item_Casing_Gold, + IC2_Spray_WeedEx, + IC2_Scrap, + IC2_Scrapbox, + IC2_Fertilizer, + IC2_Mixed_Metal_Ingot, + IC2_Hops, + IC2_Resin, + IC2_Plantball, + IC2_PlantballCompressed, + IC2_CoffeeBeans, + IC2_CoffeePowder, + IC2_Crop_Seeds, + IC2_Grin_Powder, + IC2_Energium_Dust, + IC2_Compressed_Coal_Ball, + IC2_Compressed_Coal_Chunk, + IC2_Fuel_Rod_Empty, + IC2_Food_Can_Empty, + IC2_Food_Can_Filled, + IC2_Food_Can_Spoiled, + IC2_ShaftIron, + IC2_ShaftSteel, + IC2_Industrial_Diamond, + IC2_ForgeHammer, + IC2_WireCutter, + IC2_SuBattery, + IC2_ReBattery, + IC2_AdvBattery, + IC2_EnergyCrystal, + IC2_LapotronCrystal, + Arrow_Head_Glass_Emtpy, + Arrow_Head_Glass_Poison, + Arrow_Head_Glass_Poison_Long, + Arrow_Head_Glass_Poison_Strong, + Arrow_Head_Glass_Slowness, + Arrow_Head_Glass_Slowness_Long, + Arrow_Head_Glass_Weakness, + Arrow_Head_Glass_Weakness_Long, + Arrow_Head_Glass_Holy_Water, + Arrow_Wooden_Glass_Emtpy, + Arrow_Wooden_Glass_Poison, + Arrow_Wooden_Glass_Poison_Long, + Arrow_Wooden_Glass_Poison_Strong, + Arrow_Wooden_Glass_Slowness, + Arrow_Wooden_Glass_Slowness_Long, + Arrow_Wooden_Glass_Weakness, + Arrow_Wooden_Glass_Weakness_Long, + Arrow_Wooden_Glass_Holy_Water, + Arrow_Plastic_Glass_Emtpy, + Arrow_Plastic_Glass_Poison, + Arrow_Plastic_Glass_Poison_Long, + Arrow_Plastic_Glass_Poison_Strong, + Arrow_Plastic_Glass_Slowness, + Arrow_Plastic_Glass_Slowness_Long, + Arrow_Plastic_Glass_Weakness, + Arrow_Plastic_Glass_Weakness_Long, + Arrow_Plastic_Glass_Holy_Water, + Shape_Empty, + + Shape_Mold_Bottle, + Shape_Mold_Plate, + Shape_Mold_Ingot, + Shape_Mold_Casing, + Shape_Mold_Gear, + Shape_Mold_Gear_Small, + Shape_Mold_Credit, + Shape_Mold_Nugget, + Shape_Mold_Block, + Shape_Mold_Ball, + Shape_Mold_Bun, + Shape_Mold_Bread, + Shape_Mold_Baguette, + Shape_Mold_Cylinder, + Shape_Mold_Anvil, + Shape_Mold_Arrow, + Shape_Mold_Name, + Shape_Mold_Rod, + Shape_Mold_Bolt, + Shape_Mold_Round, + Shape_Mold_Screw, + Shape_Mold_Ring, + Shape_Mold_Rod_Long, + Shape_Mold_Rotor, + Shape_Mold_Turbine_Blade, + Shape_Mold_Pipe_Tiny, + Shape_Mold_Pipe_Small, + Shape_Mold_Pipe_Medium, + Shape_Mold_Pipe_Large, + Shape_Mold_Pipe_Huge, + Shape_Mold_ToolHeadDrill, + Shape_Slicer_Flat, + Shape_Slicer_Stripes, + Shape_Extruder_Bottle, + Shape_Extruder_Plate, + Shape_Extruder_Cell, + Shape_Extruder_Ring, + Shape_Extruder_Rod, + Shape_Extruder_Bolt, + Shape_Extruder_Ingot, + Shape_Extruder_Wire, + Shape_Extruder_Casing, + Shape_Extruder_Pipe_Tiny, + Shape_Extruder_Pipe_Small, + Shape_Extruder_Pipe_Medium, + Shape_Extruder_Pipe_Large, + Shape_Extruder_Pipe_Huge, + Shape_Extruder_Block, + Shape_Extruder_Sword, + Shape_Extruder_Pickaxe, + Shape_Extruder_Shovel, + Shape_Extruder_Axe, + Shape_Extruder_Hoe, + Shape_Extruder_Hammer, + Shape_Extruder_File, + Shape_Extruder_Saw, + Shape_Extruder_Gear, + Shape_Extruder_Rotor, + Shape_Extruder_Turbine_Blade, + Shape_Extruder_Small_Gear, + Shape_Extruder_ToolHeadDrill, + + White_Dwarf_Shape_Extruder_Bottle, + White_Dwarf_Shape_Extruder_Plate, + White_Dwarf_Shape_Extruder_Cell, + White_Dwarf_Shape_Extruder_Ring, + White_Dwarf_Shape_Extruder_Rod, + White_Dwarf_Shape_Extruder_Bolt, + White_Dwarf_Shape_Extruder_Ingot, + White_Dwarf_Shape_Extruder_Wire, + White_Dwarf_Shape_Extruder_Casing, + White_Dwarf_Shape_Extruder_Pipe_Tiny, + White_Dwarf_Shape_Extruder_Pipe_Small, + White_Dwarf_Shape_Extruder_Pipe_Medium, + White_Dwarf_Shape_Extruder_Pipe_Large, + White_Dwarf_Shape_Extruder_Pipe_Huge, + White_Dwarf_Shape_Extruder_Block, + White_Dwarf_Shape_Extruder_Sword, + White_Dwarf_Shape_Extruder_Pickaxe, + White_Dwarf_Shape_Extruder_Shovel, + White_Dwarf_Shape_Extruder_Axe, + White_Dwarf_Shape_Extruder_Hoe, + White_Dwarf_Shape_Extruder_Hammer, + White_Dwarf_Shape_Extruder_File, + White_Dwarf_Shape_Extruder_Saw, + White_Dwarf_Shape_Extruder_Gear, + White_Dwarf_Shape_Extruder_Rotor, + White_Dwarf_Shape_Extruder_Turbine_Blade, + White_Dwarf_Shape_Extruder_Small_Gear, + White_Dwarf_Shape_Extruder_ToolHeadDrill, + + Crate_Empty, + Credit_Copper, + Credit_Iron, + Credit_Silver, + Credit_Gold, + Credit_Platinum, + Credit_Osmium, + Credit_Greg_Copper, + Credit_Greg_Cupronickel, + Credit_Greg_Silver, + Credit_Greg_Gold, + Credit_Greg_Platinum, + Credit_Greg_Osmium, + Credit_Greg_Naquadah, + Credit_Greg_Neutronium, + Coin_Gold_Ancient, + Coin_Doge, + Coin_Chocolate, + Cell_Universal_Fluid, + Cell_Empty, + Cell_Water, + Cell_Lava, + Cell_Air, + Large_Fluid_Cell_Steel, + Large_Fluid_Cell_TungstenSteel, + Large_Fluid_Cell_Aluminium, + Large_Fluid_Cell_StainlessSteel, + Large_Fluid_Cell_Titanium, + Large_Fluid_Cell_Chrome, + Large_Fluid_Cell_Iridium, + Large_Fluid_Cell_Osmium, + Large_Fluid_Cell_Neutronium, + ThermosCan_Empty, + ThermosCan_Dark_Coffee, + ThermosCan_Dark_Cafe_au_lait, + ThermosCan_Coffee, + ThermosCan_Cafe_au_lait, + ThermosCan_Lait_au_cafe, + ThermosCan_Dark_Chocolate_Milk, + ThermosCan_Chocolate_Milk, + ThermosCan_Tea, + ThermosCan_Sweet_Tea, + ThermosCan_Ice_Tea, + Bottle_Empty, + Bottle_Milk, + Bottle_Holy_Water, + Bottle_Purple_Drink, + Bottle_Grape_Juice, + Bottle_Wine, + Bottle_Vinegar, + Bottle_Potato_Juice, + Bottle_Vodka, + Bottle_Leninade, + Bottle_Mineral_Water, + Bottle_Salty_Water, + Bottle_Reed_Water, + Bottle_Rum, + Bottle_Pirate_Brew, + Bottle_Hops_Juice, + Bottle_Dark_Beer, + Bottle_Dragon_Blood, + Bottle_Wheaty_Juice, + Bottle_Scotch, + Bottle_Glen_McKenner, + Bottle_Wheaty_Hops_Juice, + Bottle_Beer, + Bottle_Chilly_Sauce, + Bottle_Hot_Sauce, + Bottle_Diabolo_Sauce, + Bottle_Diablo_Sauce, + Bottle_Snitches_Glitch_Sauce, + Bottle_Apple_Juice, + Bottle_Cider, + Bottle_Golden_Apple_Juice, + Bottle_Golden_Cider, + Bottle_Iduns_Apple_Juice, + Bottle_Notches_Brew, + Bottle_Lemon_Juice, + Bottle_Limoncello, + Bottle_Lemonade, + Bottle_Alcopops, + Bottle_Cave_Johnsons_Grenade_Juice, + Food_Potato_On_Stick, + Food_Potato_On_Stick_Roasted, + Food_Fries, + Food_ChiliChips, + Food_PotatoChips, + Food_Baked_Potato, + Food_Poisonous_Potato, + Food_Cheese, + Food_Chum, + Food_Chum_On_Stick, + Food_Dough, + Food_Dough_Sugar, + Food_Dough_Chocolate, + Food_Raw_Cookie, + Food_Flat_Dough, + Food_Burger_Veggie, + Food_Burger_Cheese, + Food_Burger_Meat, + Food_Burger_Chum, + Food_Sandwich_Veggie, + Food_Sandwich_Cheese, + Food_Sandwich_Bacon, + Food_Sandwich_Steak, + Food_Large_Sandwich_Veggie, + Food_Large_Sandwich_Cheese, + Food_Large_Sandwich_Bacon, + Food_Large_Sandwich_Steak, + Food_Sliced_Lemon, + Food_Sliced_Tomato, + Food_Sliced_Onion, + Food_Sliced_Cucumber, + Food_Sliced_Cheese, + Food_Sliced_Bread, + Food_Sliced_Bun, + Food_Sliced_Baguette, + Food_Sliced_Breads, + Food_Sliced_Buns, + Food_Sliced_Baguettes, + Food_Packaged_Fries, + Food_Packaged_PotatoChips, + Food_Packaged_ChiliChips, + Food_Raw_Potato, + Food_Raw_Fries, + Food_Raw_PotatoChips, + Food_Raw_Bread, + Food_Raw_Bun, + Food_Raw_Baguette, + Food_Raw_Cake, + Food_Raw_Pizza_Veggie, + Food_Raw_Pizza_Cheese, + Food_Raw_Pizza_Meat, + Food_Baked_Bread, + Food_Baked_Bun, + Food_Baked_Baguette, + Food_Baked_Cake, + Food_Baked_Pizza_Veggie, + Food_Baked_Pizza_Cheese, + Food_Baked_Pizza_Meat, + Crop_Drop_Argentia, + Crop_Drop_Plumbilia, + Crop_Drop_Indigo, + Crop_Drop_Ferru, + Crop_Drop_Aurelia, + Crop_Drop_OilBerry, + Crop_Drop_MilkWart, + Crop_Drop_BobsYerUncleRanks, + Crop_Drop_Coppon, + Crop_Drop_Tine, + Crop_Drop_Chilly, + Crop_Drop_Lemon, + Crop_Drop_Onion, + Crop_Drop_Tomato, + Crop_Drop_MTomato, + Crop_Drop_Grapes, + Crop_Drop_TeaLeaf, + Crop_Drop_Cucumber, + Crop_Drop_Rape, + Schematic, + Schematic_Crafting, + Schematic_1by1, + Schematic_2by2, + Schematic_3by3, + Schematic_Dust, + + GigaChad, + + Circuit_Integrated, + Circuit_Board_Basic, + Circuit_Board_Advanced, + Circuit_Board_Elite, + Circuit_Parts_Advanced, + Circuit_Parts_Wiring_Basic, + Circuit_Parts_Wiring_Advanced, + Circuit_Parts_Wiring_Elite, + Circuit_Parts_Crystal_Chip_Elite, + Circuit_Parts_Crystal_Chip_Master, + Circuit_Parts_Crystal_Chip_Wetware, + Circuit_Primitive, + Circuit_Basic, + Circuit_Good, + Circuit_Advanced, + Circuit_Data, + Circuit_Elite, + Circuit_Master, + Circuit_Ultimate, + Circuit_Biowarecomputer, + Circuit_Biowaresupercomputer, + + Rotor_LV, // these aren't actually used + Rotor_MV, + Rotor_HV, + Rotor_EV, + Rotor_IV, + + Electric_Motor_LV, + Electric_Motor_MV, + Electric_Motor_HV, + Electric_Motor_EV, + Electric_Motor_IV, + Electric_Motor_LuV, + Electric_Motor_ZPM, + Electric_Motor_UV, + Electric_Motor_UHV, + Electric_Motor_UEV, + Electric_Motor_UIV, + Electric_Motor_UMV, + Electric_Motor_UXV, + Electric_Motor_MAX, + + Electric_Pump_LV, + Electric_Pump_MV, + Electric_Pump_HV, + Electric_Pump_EV, + Electric_Pump_IV, + Electric_Pump_LuV, + Electric_Pump_ZPM, + Electric_Pump_UV, + Electric_Pump_UHV, + Electric_Pump_UEV, + Electric_Pump_UIV, + Electric_Pump_UMV, + Electric_Pump_UXV, + Electric_Pump_MAX, + + Tesseract, + EnergisedTesseract, + Timepiece, + + Steam_Valve_LV, + Steam_Valve_MV, + Steam_Valve_HV, + Steam_Valve_EV, + Steam_Valve_IV, + + Steam_Regulator_LV, + Steam_Regulator_MV, + Steam_Regulator_HV, + Steam_Regulator_EV, + Steam_Regulator_IV, + + FluidRegulator_LV, + FluidRegulator_MV, + FluidRegulator_HV, + FluidRegulator_EV, + FluidRegulator_IV, + FluidRegulator_LuV, + FluidRegulator_ZPM, + FluidRegulator_UV, + + Conveyor_Module_LV, + Conveyor_Module_MV, + Conveyor_Module_HV, + Conveyor_Module_EV, + Conveyor_Module_IV, + Conveyor_Module_LuV, + Conveyor_Module_ZPM, + Conveyor_Module_UV, + Conveyor_Module_UHV, + Conveyor_Module_UEV, + Conveyor_Module_UIV, + Conveyor_Module_UMV, + Conveyor_Module_UXV, + Conveyor_Module_MAX, + + Electric_Piston_LV, + Electric_Piston_MV, + Electric_Piston_HV, + Electric_Piston_EV, + Electric_Piston_IV, + Electric_Piston_LuV, + Electric_Piston_ZPM, + Electric_Piston_UV, + Electric_Piston_UHV, + Electric_Piston_UEV, + Electric_Piston_UIV, + Electric_Piston_UMV, + Electric_Piston_UXV, + Electric_Piston_MAX, + + Robot_Arm_LV, + Robot_Arm_MV, + Robot_Arm_HV, + Robot_Arm_EV, + Robot_Arm_IV, + Robot_Arm_LuV, + Robot_Arm_ZPM, + Robot_Arm_UV, + Robot_Arm_UHV, + Robot_Arm_UEV, + Robot_Arm_UIV, + Robot_Arm_UMV, + Robot_Arm_UXV, + Robot_Arm_MAX, + + Emitter_LV, + Emitter_MV, + Emitter_HV, + Emitter_EV, + Emitter_IV, + Emitter_LuV, + Emitter_ZPM, + Emitter_UV, + Emitter_UHV, + Emitter_UEV, + Emitter_UIV, + Emitter_UMV, + Emitter_UXV, + Emitter_MAX, + + Sensor_LV, + Sensor_MV, + Sensor_HV, + Sensor_EV, + Sensor_IV, + Sensor_LuV, + Sensor_ZPM, + Sensor_UV, + Sensor_UHV, + Sensor_UEV, + Sensor_UIV, + Sensor_UMV, + Sensor_UXV, + Sensor_MAX, + + Field_Generator_LV, + Field_Generator_MV, + Field_Generator_HV, + Field_Generator_EV, + Field_Generator_IV, + Field_Generator_LuV, + Field_Generator_ZPM, + Field_Generator_UV, + Field_Generator_UHV, + Field_Generator_UEV, + Field_Generator_UIV, + Field_Generator_UMV, + Field_Generator_UXV, + Field_Generator_MAX, + + StableAdhesive, + SuperconductorComposite, + NaquadriaSupersolid, + + Battery_Hull_LV, + Battery_Hull_MV, + Battery_Hull_HV, + + Battery_SU_LV_SulfuricAcid, + Battery_SU_LV_Mercury, + Battery_SU_MV_SulfuricAcid, + Battery_SU_MV_Mercury, + Battery_SU_HV_SulfuricAcid, + Battery_SU_HV_Mercury, + Battery_RE_ULV_Tantalum, + Battery_RE_LV_Cadmium, + Battery_RE_LV_Lithium, + Battery_RE_LV_Sodium, + Battery_RE_MV_Cadmium, + Battery_RE_MV_Lithium, + Battery_RE_MV_Sodium, + Battery_RE_HV_Cadmium, + Battery_RE_HV_Lithium, + Battery_RE_HV_Sodium, + ZPM, + Fuel_Can_Plastic_Empty, + Fuel_Can_Plastic_Filled, + Upgrade_Battery, + Upgrade_Overclocker, + Upgrade_Muffler, + Upgrade_SteamEngine, + Upgrade_Lock, + Cover_FluidLimiter, + Cover_Controller, + Cover_ActivityDetector, + Cover_FluidDetector, + Cover_ItemDetector, + Cover_EnergyDetector, + Cover_FluidStorageMonitor, + Cover_Drain, + Cover_Shutter, + Cover_Crafting, + Cover_Screen, + Cover_SolarPanel, + Cover_SolarPanel_8V, + Cover_SolarPanel_LV, + Cover_SolarPanel_MV, + Cover_SolarPanel_HV, + Cover_SolarPanel_EV, + Cover_SolarPanel_IV, + Cover_SolarPanel_LuV, + Cover_SolarPanel_ZPM, + Cover_SolarPanel_UV, + Cover_SolarPanel_UHV, + Cover_SolarPanel_UEV, + Cover_SolarPanel_UIV, + Ingot_IridiumAlloy, + Plank_Oak, + Plank_Spruce, + Plank_Birch, + Plank_Jungle, + Plank_Acacia, + Plank_DarkOak, + Plank_Larch, + Plank_Teak, + Plank_Acacia_Green, + Plank_Lime, + Plank_Chestnut, + Plank_Wenge, + Plank_Baobab, + Plank_Sequoia, + Plank_Kapok, + Plank_Ebony, + Plank_Mahagony, + Plank_Balsa, + Plank_Willow, + Plank_Walnut, + Plank_Greenheart, + Plank_Cherry, + Plank_Mahoe, + Plank_Poplar, + Plank_Palm, + Plank_Papaya, + Plank_Pine, + Plank_Plum, + Plank_Maple, + Plank_Citrus, + Dye_Indigo, + Dye_SquidInk, + Dye_Bonemeal, + Dye_Cocoa, + Duct_Tape, + Book_Written_00, + Book_Written_01, + Book_Written_02, + Book_Written_03, + Paper_Printed_Pages, + Paper_Magic_Empty, + Paper_Magic_Page, + Paper_Magic_Pages, + Paper_Punch_Card_Empty, + Paper_Punch_Card_Encoded, + McGuffium_239, + Tool_Cover_Copy_Paste, + NC_SensorCard, + NC_SensorKit, + Tool_Matches, + Tool_MatchBox_Used, + Tool_MatchBox_Full, + Tool_Lighter_Invar_Empty, + Tool_Lighter_Invar_Used, + Tool_Lighter_Invar_Full, + Tool_Lighter_Platinum_Empty, + Tool_Lighter_Platinum_Used, + Tool_Lighter_Platinum_Full, + Tool_Cheat, + Tool_Scanner, + Tool_DataOrb, + Tool_DataStick, + Tool_Sonictron, + Tool_Sword_Bronze, + Tool_Pickaxe_Bronze, + Tool_Shovel_Bronze, + Tool_Axe_Bronze, + Tool_Hoe_Bronze, + Tool_Sword_Steel, + Tool_Pickaxe_Steel, + Tool_Shovel_Steel, + Tool_Axe_Steel, + Tool_Hoe_Steel, + + Spray_Empty, + Spray_Bug, + Spray_Ice, + Spray_Hardener, + Spray_CFoam, + Spray_Pepper, + Spray_Hydration, + + Color_00, + Color_01, + Color_02, + Color_03, + Color_04, + Color_05, + Color_06, + Color_07, + Color_08, + Color_09, + Color_10, + Color_11, + Color_12, + Color_13, + Color_14, + Color_15, + + Spray_Color_00, + Spray_Color_01, + Spray_Color_02, + Spray_Color_03, + Spray_Color_04, + Spray_Color_05, + Spray_Color_06, + Spray_Color_07, + Spray_Color_08, + Spray_Color_09, + Spray_Color_10, + Spray_Color_11, + Spray_Color_12, + Spray_Color_13, + Spray_Color_14, + Spray_Color_15, + Spray_Color_Remover, + + Spray_Color_Used_00, + Spray_Color_Used_01, + Spray_Color_Used_02, + Spray_Color_Used_03, + Spray_Color_Used_04, + Spray_Color_Used_05, + Spray_Color_Used_06, + Spray_Color_Used_07, + Spray_Color_Used_08, + Spray_Color_Used_09, + Spray_Color_Used_10, + Spray_Color_Used_11, + Spray_Color_Used_12, + Spray_Color_Used_13, + Spray_Color_Used_14, + Spray_Color_Used_15, + Spray_Color_Used_Remover, + + Spray_Color_Remover_Empty, + + Armor_Cheat, + Armor_Cloaking, + Armor_Lamp, + Armor_LithiumPack, + Armor_LapotronicPack, + Armor_ForceField, + Energy_LapotronicOrb, + Reactor_NeutronReflector, + Component_Turbine_Bronze, + Component_Turbine_Steel, + Component_Turbine_Magnalium, + Component_Turbine_TungstenSteel, + Component_Turbine_Carbon, + Component_LavaFilter, + Component_Sawblade_Diamond, + Component_Grinder_Diamond, + Component_Grinder_Tungsten, + Component_Filter, + Component_Minecart_Wheels_Iron, + Component_Minecart_Wheels_Steel, + + Generator_Diesel_LV, + Generator_Diesel_MV, + Generator_Diesel_HV, + Generator_Gas_Turbine_LV, + Generator_Gas_Turbine_MV, + Generator_Gas_Turbine_HV, + Generator_Gas_Turbine_EV, + Generator_Gas_Turbine_IV, + Generator_Steam_Turbine_LV, + Generator_Steam_Turbine_MV, + Generator_Steam_Turbine_HV, + Generator_Naquadah_Mark_I, + Generator_Naquadah_Mark_II, + Generator_Naquadah_Mark_III, + Generator_Naquadah_Mark_IV, + Generator_Naquadah_Mark_V, + // Generator_Naquadah_Mark_VI, + Machine_Bronze_Boiler, + Machine_Bronze_Boiler_Solar, + Machine_HP_Solar, + Machine_Bronze_CraftingTable, + Machine_Bronze_Furnace, + Machine_Bronze_Macerator, + Machine_Bronze_Extractor, + Machine_Bronze_Hammer, + Machine_Bronze_Compressor, + Machine_Bronze_AlloySmelter, + Machine_Bricked_BlastFurnace, + Machine_Steel_Boiler_Lava, + Machine_Steel_Boiler, + Machine_HP_Furnace, + Machine_HP_Macerator, + Machine_HP_Extractor, + Machine_HP_Hammer, + Machine_HP_Compressor, + Machine_HP_AlloySmelter, + + Hull_Bronze, + Hull_HP, + Hull_Bronze_Bricks, + Hull_HP_Bricks, + + Transformer_LV_ULV, + Transformer_MV_LV, + Transformer_HV_MV, + Transformer_EV_HV, + Transformer_IV_EV, + Transformer_LuV_IV, + Transformer_ZPM_LuV, + Transformer_UV_ZPM, + Transformer_MAX_UV, + + Casing_ULV, + Casing_LV, + Casing_MV, + Casing_HV, + Casing_EV, + Casing_IV, + Casing_LuV, + Casing_ZPM, + Casing_UV, + Casing_MAX, + Casing_BronzePlatedBricks, + Casing_HeatProof, + Casing_Dim_Trans, + Casing_Dim_Injector, + Casing_Dim_Bridge, + Casing_Coil_Superconductor, + + Casing_SolidSteel, + Casing_FrostProof, + Casing_Gearbox_Bronze, + Casing_Gearbox_Steel, + Casing_Gearbox_Titanium, + Casing_Gearbox_TungstenSteel, + Casing_Processor, + Casing_DataDrive, + Casing_ContainmentField, + Casing_Assembler, + Casing_Pump, + Casing_Motor, + Casing_Pipe_Bronze, + Casing_Pipe_Steel, + Casing_Pipe_Titanium, + Casing_Pipe_TungstenSteel, + Casing_Pipe_Polytetrafluoroethylene, + Casing_Pipe_Polybenzimidazole, + + Casing_Stripes_A, + Casing_Stripes_B, + Casing_RadioactiveHazard, + Casing_BioHazard, + Casing_ExplosionHazard, + Casing_FireHazard, + Casing_AcidHazard, + Casing_MagicHazard, + Casing_FrostHazard, + Casing_NoiseHazard, + Casing_Grate, + Casing_Vent, + Casing_Vent_T2, + Casing_RadiationProof, + Casing_AdvancedRadiationProof, + Casing_Firebox_Bronze, + Casing_Firebox_Steel, + Casing_Firebox_TungstenSteel, + Casing_Chemically_Inert, + + Casing_MiningOsmiridium, + Casing_RobustTungstenSteel, + Casing_CleanStainlessSteel, + Casing_StableTitanium, + Casing_Firebox_Titanium, + Casing_MiningNeutronium, + Casing_MiningBlackPlutonium, + Casing_Advanced_Rhodium_Palladium, + Casing_Advanced_Iridium, + Casing_Magical, + + Hull_ULV, + Hull_LV, + Hull_MV, + Hull_HV, + Hull_EV, + Hull_IV, + Hull_LuV, + Hull_ZPM, + Hull_UV, + Hull_MAX, + + CompressedFireclay, + Firebrick, + Casing_Firebricks, + + Automation_Filter_ULV, + Automation_Filter_LV, + Automation_Filter_MV, + Automation_Filter_HV, + Automation_Filter_EV, + Automation_Filter_IV, + Automation_Filter_LuV, + Automation_Filter_ZPM, + Automation_Filter_UV, + Automation_Filter_MAX, + + Automation_TypeFilter_ULV, + Automation_TypeFilter_LV, + Automation_TypeFilter_MV, + Automation_TypeFilter_HV, + Automation_TypeFilter_EV, + Automation_TypeFilter_IV, + Automation_TypeFilter_LuV, + Automation_TypeFilter_ZPM, + Automation_TypeFilter_UV, + Automation_TypeFilter_MAX, + + Automation_ChestBuffer_ULV, + Automation_ChestBuffer_LV, + Automation_ChestBuffer_MV, + Automation_ChestBuffer_HV, + Automation_ChestBuffer_EV, + Automation_ChestBuffer_IV, + Automation_ChestBuffer_LuV, + Automation_ChestBuffer_ZPM, + Automation_ChestBuffer_UV, + Automation_ChestBuffer_MAX, + + Automation_SuperBuffer_ULV, + Automation_SuperBuffer_LV, + Automation_SuperBuffer_MV, + Automation_SuperBuffer_HV, + Automation_SuperBuffer_EV, + Automation_SuperBuffer_IV, + Automation_SuperBuffer_LuV, + Automation_SuperBuffer_ZPM, + Automation_SuperBuffer_UV, + Automation_SuperBuffer_MAX, + + Automation_Regulator_ULV, + Automation_Regulator_LV, + Automation_Regulator_MV, + Automation_Regulator_HV, + Automation_Regulator_EV, + Automation_Regulator_IV, + Automation_Regulator_LuV, + Automation_Regulator_ZPM, + Automation_Regulator_UV, + Automation_Regulator_MAX, + + Automation_ItemDistributor_ULV, + Automation_ItemDistributor_LV, + Automation_ItemDistributor_MV, + Automation_ItemDistributor_HV, + Automation_ItemDistributor_EV, + Automation_ItemDistributor_IV, + Automation_ItemDistributor_LuV, + Automation_ItemDistributor_ZPM, + Automation_ItemDistributor_UV, + Automation_ItemDistributor_MAX, + + Automation_RecipeFilter_ULV, + Automation_RecipeFilter_LV, + Automation_RecipeFilter_MV, + Automation_RecipeFilter_HV, + Automation_RecipeFilter_EV, + Automation_RecipeFilter_IV, + Automation_RecipeFilter_LuV, + Automation_RecipeFilter_ZPM, + Automation_RecipeFilter_UV, + Automation_RecipeFilter_MAX, + + Hatch_Dynamo_ULV, + Hatch_Dynamo_LV, + Hatch_Dynamo_MV, + Hatch_Dynamo_HV, + Hatch_Dynamo_EV, + Hatch_Dynamo_IV, + Hatch_Dynamo_LuV, + Hatch_Dynamo_ZPM, + Hatch_Dynamo_UV, + Hatch_Dynamo_MAX, + + Hatch_Energy_ULV, + Hatch_Energy_LV, + Hatch_Energy_MV, + Hatch_Energy_HV, + Hatch_Energy_EV, + Hatch_Energy_IV, + Hatch_Energy_LuV, + Hatch_Energy_ZPM, + Hatch_Energy_UV, + Hatch_Energy_MAX, + + Wireless_Hatch_Energy_ULV, + Wireless_Hatch_Energy_LV, + Wireless_Hatch_Energy_MV, + Wireless_Hatch_Energy_HV, + Wireless_Hatch_Energy_EV, + Wireless_Hatch_Energy_IV, + Wireless_Hatch_Energy_LuV, + Wireless_Hatch_Energy_ZPM, + Wireless_Hatch_Energy_UV, + Wireless_Hatch_Energy_UHV, + Wireless_Hatch_Energy_UEV, + Wireless_Hatch_Energy_UIV, + Wireless_Hatch_Energy_UMV, + Wireless_Hatch_Energy_UXV, + Wireless_Hatch_Energy_MAX, + + Wireless_Dynamo_Energy_ULV, + Wireless_Dynamo_Energy_LV, + Wireless_Dynamo_Energy_MV, + Wireless_Dynamo_Energy_HV, + Wireless_Dynamo_Energy_EV, + Wireless_Dynamo_Energy_IV, + Wireless_Dynamo_Energy_LuV, + Wireless_Dynamo_Energy_ZPM, + Wireless_Dynamo_Energy_UV, + Wireless_Dynamo_Energy_UHV, + Wireless_Dynamo_Energy_UEV, + Wireless_Dynamo_Energy_UIV, + Wireless_Dynamo_Energy_UMV, + Wireless_Dynamo_Energy_UXV, + Wireless_Dynamo_Energy_MAX, + + Hatch_Input_ULV, + Hatch_Input_LV, + Hatch_Input_MV, + Hatch_Input_HV, + Hatch_Input_EV, + Hatch_Input_IV, + Hatch_Input_LuV, + Hatch_Input_ZPM, + Hatch_Input_UV, + Hatch_Input_MAX, + + Hatch_Input_Multi_2x2_EV, + Hatch_Input_Multi_2x2_IV, + Hatch_Input_Multi_2x2_LuV, + Hatch_Input_Multi_2x2_ZPM, + Hatch_Input_Multi_2x2_UV, + Hatch_Input_Multi_2x2_UHV, + Hatch_Input_Multi_2x2_UEV, + Hatch_Input_Multi_2x2_UIV, + Hatch_Input_Multi_2x2_UMV, + Hatch_Input_Multi_2x2_UXV, + Hatch_Input_Multi_2x2_Humongous, + + Hatch_Input_Bus_ULV, + Hatch_Input_Bus_LV, + Hatch_Input_Bus_MV, + Hatch_Input_Bus_HV, + Hatch_Input_Bus_EV, + Hatch_Input_Bus_IV, + Hatch_Input_Bus_LuV, + Hatch_Input_Bus_ZPM, + Hatch_Input_Bus_UV, + Hatch_Input_Bus_MAX, + + Hatch_Output_ULV, + Hatch_Output_LV, + Hatch_Output_MV, + Hatch_Output_HV, + Hatch_Output_EV, + Hatch_Output_IV, + Hatch_Output_LuV, + Hatch_Output_ZPM, + Hatch_Output_UV, + Hatch_Output_MAX, + + Hatch_Output_Bus_ULV, + Hatch_Output_Bus_LV, + Hatch_Output_Bus_MV, + Hatch_Output_Bus_HV, + Hatch_Output_Bus_EV, + Hatch_Output_Bus_IV, + Hatch_Output_Bus_LuV, + Hatch_Output_Bus_ZPM, + Hatch_Output_Bus_UV, + Hatch_Output_Bus_MAX, + + Hatch_Muffler_LV, + Hatch_Muffler_MV, + Hatch_Muffler_HV, + Hatch_Muffler_EV, + Hatch_Muffler_IV, + Hatch_Muffler_LuV, + Hatch_Muffler_ZPM, + Hatch_Muffler_UV, + Hatch_Muffler_MAX, + + Hatch_Maintenance, + Hatch_DataAccess_EV, + Hatch_DataAccess_LuV, + Hatch_DataAccess_UV, + + Battery_Buffer_1by1_ULV, + Battery_Buffer_1by1_LV, + Battery_Buffer_1by1_MV, + Battery_Buffer_1by1_HV, + Battery_Buffer_1by1_EV, + Battery_Buffer_1by1_IV, + Battery_Buffer_1by1_LuV, + Battery_Buffer_1by1_ZPM, + Battery_Buffer_1by1_UV, + Battery_Buffer_1by1_MAX, + + Battery_Buffer_2by2_ULV, + Battery_Buffer_2by2_LV, + Battery_Buffer_2by2_MV, + Battery_Buffer_2by2_HV, + Battery_Buffer_2by2_EV, + Battery_Buffer_2by2_IV, + Battery_Buffer_2by2_LuV, + Battery_Buffer_2by2_ZPM, + Battery_Buffer_2by2_UV, + Battery_Buffer_2by2_MAX, + + Battery_Buffer_3by3_ULV, + Battery_Buffer_3by3_LV, + Battery_Buffer_3by3_MV, + Battery_Buffer_3by3_HV, + Battery_Buffer_3by3_EV, + Battery_Buffer_3by3_IV, + Battery_Buffer_3by3_LuV, + Battery_Buffer_3by3_ZPM, + Battery_Buffer_3by3_UV, + Battery_Buffer_3by3_MAX, + + Battery_Buffer_4by4_ULV, + Battery_Buffer_4by4_LV, + Battery_Buffer_4by4_MV, + Battery_Buffer_4by4_HV, + Battery_Buffer_4by4_EV, + Battery_Buffer_4by4_IV, + Battery_Buffer_4by4_LuV, + Battery_Buffer_4by4_ZPM, + Battery_Buffer_4by4_UV, + Battery_Buffer_4by4_MAX, + + Locker_ULV, + Locker_LV, + Locker_MV, + Locker_HV, + Locker_EV, + Locker_IV, + Locker_LuV, + Locker_ZPM, + Locker_UV, + Locker_MAX, + + Machine_Multi_LargeBoiler_Bronze, + Machine_Multi_LargeBoiler_Steel, + Machine_Multi_LargeBoiler_Titanium, + Machine_Multi_LargeBoiler_TungstenSteel, + Machine_Multi_BlastFurnace, + Machine_Multi_PlasmaForge, + Machine_Multi_ImplosionCompressor, + Machine_Multi_VacuumFreezer, + Machine_Multi_Furnace, + + Machine_LV_AlloySmelter, + Machine_MV_AlloySmelter, + Machine_HV_AlloySmelter, + Machine_EV_AlloySmelter, + Machine_IV_AlloySmelter, + + Machine_LV_Assembler, + Machine_MV_Assembler, + Machine_HV_Assembler, + Machine_EV_Assembler, + Machine_IV_Assembler, + + Machine_LV_Bender, + Machine_MV_Bender, + Machine_HV_Bender, + Machine_EV_Bender, + Machine_IV_Bender, + + Machine_LV_Canner, + Machine_MV_Canner, + Machine_HV_Canner, + Machine_EV_Canner, + Machine_IV_Canner, + + Machine_LV_Compressor, + Machine_MV_Compressor, + Machine_HV_Compressor, + Machine_EV_Compressor, + Machine_IV_Compressor, + + Machine_LV_Cutter, + Machine_MV_Cutter, + Machine_HV_Cutter, + Machine_EV_Cutter, + Machine_IV_Cutter, + + Machine_LV_Slicer, + Machine_MV_Slicer, + Machine_HV_Slicer, + Machine_EV_Slicer, + Machine_IV_Slicer, + + Machine_LV_Sifter, + Machine_MV_Sifter, + Machine_HV_Sifter, + Machine_EV_Sifter, + Machine_IV_Sifter, + + Machine_LV_ArcFurnace, + Machine_MV_ArcFurnace, + Machine_HV_ArcFurnace, + Machine_EV_ArcFurnace, + Machine_IV_ArcFurnace, + + Machine_LV_PlasmaArcFurnace, + Machine_MV_PlasmaArcFurnace, + Machine_HV_PlasmaArcFurnace, + Machine_EV_PlasmaArcFurnace, + Machine_IV_PlasmaArcFurnace, + + Machine_LV_Oven, + Machine_MV_Oven, + Machine_HV_Oven, + Machine_EV_Oven, + Machine_IV_Oven, + + Machine_LV_E_Furnace, + Machine_MV_E_Furnace, + Machine_HV_E_Furnace, + Machine_EV_E_Furnace, + Machine_IV_E_Furnace, + + Machine_LV_Extractor, + Machine_MV_Extractor, + Machine_HV_Extractor, + Machine_EV_Extractor, + Machine_IV_Extractor, + + Machine_LV_Extruder, + Machine_MV_Extruder, + Machine_HV_Extruder, + Machine_EV_Extruder, + Machine_IV_Extruder, + + Machine_LV_Lathe, + Machine_MV_Lathe, + Machine_HV_Lathe, + Machine_EV_Lathe, + Machine_IV_Lathe, + + Machine_LV_Macerator, + Machine_MV_Macerator, + Machine_HV_Macerator, + Machine_EV_Macerator, + Machine_IV_Macerator, + + Machine_LV_Microwave, + Machine_MV_Microwave, + Machine_HV_Microwave, + Machine_EV_Microwave, + Machine_IV_Microwave, + + Machine_LV_Printer, + Machine_MV_Printer, + Machine_HV_Printer, + Machine_EV_Printer, + Machine_IV_Printer, + Machine_LuV_Printer, + Machine_ZPM_Printer, + Machine_UV_Printer, + + Machine_LV_Recycler, + Machine_MV_Recycler, + Machine_HV_Recycler, + Machine_EV_Recycler, + Machine_IV_Recycler, + + Machine_LV_Scanner, + Machine_MV_Scanner, + Machine_HV_Scanner, + Machine_EV_Scanner, + Machine_IV_Scanner, + + Machine_LV_Wiremill, + Machine_MV_Wiremill, + Machine_HV_Wiremill, + Machine_EV_Wiremill, + Machine_IV_Wiremill, + + Machine_LV_Electrolyzer, + Machine_MV_Electrolyzer, + Machine_HV_Electrolyzer, + Machine_EV_Electrolyzer, + Machine_IV_Electrolyzer, + + Machine_LV_Centrifuge, + Machine_MV_Centrifuge, + Machine_HV_Centrifuge, + Machine_EV_Centrifuge, + Machine_IV_Centrifuge, + + Machine_LV_ThermalCentrifuge, + Machine_MV_ThermalCentrifuge, + Machine_HV_ThermalCentrifuge, + Machine_EV_ThermalCentrifuge, + Machine_IV_ThermalCentrifuge, + + Machine_LV_OreWasher, + Machine_MV_OreWasher, + Machine_HV_OreWasher, + Machine_EV_OreWasher, + Machine_IV_OreWasher, + + Machine_LV_RockBreaker, + Machine_MV_RockBreaker, + Machine_HV_RockBreaker, + Machine_EV_RockBreaker, + Machine_IV_RockBreaker, + + Machine_LV_Boxinator, + Machine_MV_Boxinator, + Machine_HV_Boxinator, + Machine_EV_Boxinator, + Machine_IV_Boxinator, + Machine_LuV_Boxinator, + Machine_ZPM_Boxinator, + Machine_UV_Boxinator, + + Machine_LV_Unboxinator, + Machine_MV_Unboxinator, + Machine_HV_Unboxinator, + Machine_EV_Unboxinator, + Machine_IV_Unboxinator, + Machine_LuV_Unboxinator, + Machine_ZPM_Unboxinator, + Machine_UV_Unboxinator, + + Machine_LV_ChemicalReactor, + Machine_MV_ChemicalReactor, + Machine_HV_ChemicalReactor, + Machine_EV_ChemicalReactor, + Machine_IV_ChemicalReactor, + + Machine_LV_FluidCanner, + Machine_MV_FluidCanner, + Machine_HV_FluidCanner, + Machine_EV_FluidCanner, + Machine_IV_FluidCanner, + + Machine_LV_Bundler, + Machine_MV_Bundler, + Machine_HV_Bundler, + Machine_EV_Bundler, + Machine_IV_Bundler, + + Machine_LV_Massfab, + Machine_MV_Massfab, + Machine_HV_Massfab, + Machine_EV_Massfab, + Machine_IV_Massfab, + + Machine_LV_Amplifab, + Machine_MV_Amplifab, + Machine_HV_Amplifab, + Machine_EV_Amplifab, + Machine_IV_Amplifab, + + Machine_LV_Replicator, + Machine_MV_Replicator, + Machine_HV_Replicator, + Machine_EV_Replicator, + Machine_IV_Replicator, + + Machine_LV_Brewery, + Machine_MV_Brewery, + Machine_HV_Brewery, + Machine_EV_Brewery, + Machine_IV_Brewery, + + Machine_LV_Fermenter, + Machine_MV_Fermenter, + Machine_HV_Fermenter, + Machine_EV_Fermenter, + Machine_IV_Fermenter, + + Machine_LV_FluidExtractor, + Machine_MV_FluidExtractor, + Machine_HV_FluidExtractor, + Machine_EV_FluidExtractor, + Machine_IV_FluidExtractor, + + Machine_LV_FluidSolidifier, + Machine_MV_FluidSolidifier, + Machine_HV_FluidSolidifier, + Machine_EV_FluidSolidifier, + Machine_IV_FluidSolidifier, + + Machine_LV_Distillery, + Machine_MV_Distillery, + Machine_HV_Distillery, + Machine_EV_Distillery, + Machine_IV_Distillery, + + Machine_LV_ChemicalBath, + Machine_MV_ChemicalBath, + Machine_HV_ChemicalBath, + Machine_EV_ChemicalBath, + Machine_IV_ChemicalBath, + + Machine_LV_Polarizer, + Machine_MV_Polarizer, + Machine_HV_Polarizer, + Machine_EV_Polarizer, + Machine_IV_Polarizer, + + Machine_LV_ElectromagneticSeparator, + Machine_MV_ElectromagneticSeparator, + Machine_HV_ElectromagneticSeparator, + Machine_EV_ElectromagneticSeparator, + Machine_IV_ElectromagneticSeparator, + + Machine_LV_Autoclave, + Machine_MV_Autoclave, + Machine_HV_Autoclave, + Machine_EV_Autoclave, + Machine_IV_Autoclave, + + Machine_LV_Mixer, + Machine_MV_Mixer, + Machine_HV_Mixer, + Machine_EV_Mixer, + Machine_IV_Mixer, + + Machine_LV_LaserEngraver, + Machine_MV_LaserEngraver, + Machine_HV_LaserEngraver, + Machine_EV_LaserEngraver, + Machine_IV_LaserEngraver, + + Machine_LV_Press, + Machine_MV_Press, + Machine_HV_Press, + Machine_EV_Press, + Machine_IV_Press, + + Machine_LV_Hammer, + Machine_MV_Hammer, + Machine_HV_Hammer, + Machine_EV_Hammer, + Machine_IV_Hammer, + + Machine_LV_FluidHeater, + Machine_MV_FluidHeater, + Machine_HV_FluidHeater, + Machine_EV_FluidHeater, + Machine_IV_FluidHeater, + + Machine_Multi_LargeChemicalReactor, + + Machine_LV_Miner, + Machine_MV_Miner, + Machine_HV_Miner, + + Machine_IndustrialApiary, + IndustrialApiary_Upgrade_Frame, + IndustrialApiary_Upgrade_Acceleration_1, + IndustrialApiary_Upgrade_Acceleration_2, + IndustrialApiary_Upgrade_Acceleration_3, + IndustrialApiary_Upgrade_Acceleration_4, + IndustrialApiary_Upgrade_Acceleration_5, + IndustrialApiary_Upgrade_Acceleration_6, + IndustrialApiary_Upgrade_Acceleration_7, + IndustrialApiary_Upgrade_Acceleration_8, + IndustrialApiary_Upgrade_Acceleration_8_Upgraded, + IndustrialApiary_Upgrade_PRODUCTION, + IndustrialApiary_Upgrade_PLAINS, + IndustrialApiary_Upgrade_LIGHT, + IndustrialApiary_Upgrade_FLOWERING, + IndustrialApiary_Upgrade_WINTER, + IndustrialApiary_Upgrade_DRYER, + IndustrialApiary_Upgrade_AUTOMATION, + IndustrialApiary_Upgrade_HUMIDIFIER, + IndustrialApiary_Upgrade_HELL, + IndustrialApiary_Upgrade_POLLEN, + IndustrialApiary_Upgrade_DESERT, + IndustrialApiary_Upgrade_COOLER, + IndustrialApiary_Upgrade_LIFESPAN, + IndustrialApiary_Upgrade_SEAL, + IndustrialApiary_Upgrade_STABILIZER, + IndustrialApiary_Upgrade_JUNGLE, + IndustrialApiary_Upgrade_TERRITORY, + IndustrialApiary_Upgrade_OCEAN, + IndustrialApiary_Upgrade_SKY, + IndustrialApiary_Upgrade_HEATER, + IndustrialApiary_Upgrade_SIEVE, + IndustrialApiary_Upgrade_UNLIGHT, + + Neutron_Reflector, + + Reactor_Coolant_He_1, + Reactor_Coolant_He_3, + Reactor_Coolant_He_6, + Reactor_Coolant_NaK_1, + Reactor_Coolant_NaK_3, + Reactor_Coolant_NaK_6, + neutroniumHeatCapacitor, + + GlowstoneCell, + SunnariumCell, + + ThoriumCell_1, + ThoriumCell_2, + ThoriumCell_4, + + Reactor_Coolant_Sp_1, + Reactor_Coolant_Sp_2, + Reactor_Coolant_Sp_3, + Reactor_Coolant_Sp_6, + + FusionComputer_LuV, + FusionComputer_ZPMV, + FusionComputer_UV, + + Casing_Fusion_Coil, + Casing_Fusion, + Casing_Fusion2, + + Generator_Plasma_IV, + Generator_Plasma_LuV, + Generator_Plasma_ZPMV, + + MagicEnergyConverter_LV, + MagicEnergyConverter_MV, + MagicEnergyConverter_HV, + + MagicEnergyAbsorber_LV, + MagicEnergyAbsorber_MV, + MagicEnergyAbsorber_HV, + MagicEnergyAbsorber_EV, + + Depleted_Thorium_1, + Depleted_Thorium_2, + Depleted_Thorium_4, + + Processing_Array, + Distillation_Tower, + Energy_LapotronicOrb2, + Ore_Processor, + ZPM6, + ZPM5, + ZPM4, + ZPM3, + ZPM2, + Energy_Module, + Energy_Cluster, + + Quantum_Tank_LV, + Quantum_Tank_MV, + Quantum_Tank_HV, + Quantum_Tank_EV, + Quantum_Tank_IV, + Quantum_Chest_LV, + Quantum_Chest_MV, + Quantum_Chest_HV, + Quantum_Chest_EV, + Quantum_Chest_IV, + + Super_Tank_LV, + Super_Tank_MV, + Super_Tank_HV, + Super_Tank_EV, + Super_Tank_IV, + Super_Chest_LV, + Super_Chest_MV, + Super_Chest_HV, + Super_Chest_EV, + Super_Chest_IV, + + Long_Distance_Pipeline_Fluid, + Long_Distance_Pipeline_Item, + + Long_Distance_Pipeline_Fluid_Pipe, + Long_Distance_Pipeline_Item_Pipe, + + Hatch_Output_Bus_ME, + Hatch_Output_ME, + + NULL, + Cover_AdvancedRedstoneTransmitterExternal, + Cover_AdvancedRedstoneTransmitterInternal, + Cover_AdvancedRedstoneReceiverExternal, + Cover_AdvancedRedstoneReceiverInternal, + + Cover_WirelessFluidDetector, + Cover_WirelessItemDetector, + Cover_WirelessNeedsMaintainance, + Cover_WirelessActivityDetector, + + Cover_RedstoneTransmitterExternal, + Cover_RedstoneTransmitterInternal, + Cover_RedstoneReceiverExternal, + Cover_RedstoneReceiverInternal, + + LargeSteamTurbine, + LargeGasTurbine, + LargeHPSteamTurbine, + LargeAdvancedGasTurbine, + LargePlasmaTurbine, + + Ingot_Heavy1, + Ingot_Heavy2, + Ingot_Heavy3, + + Pump_LV, + Pump_MV, + Pump_HV, + Pump_EV, + Pump_IV, + Pump_LuV, + Pump_ZPM, + Pump_UV, + + Teleporter, + Cover_NeedsMaintainance, + Casing_Turbine, + Casing_Turbine1, + Casing_Turbine2, + Casing_Turbine3, + Casing_EngineIntake, + Casing_ExtremeEngineIntake, + Casing_TurbineGasAdvanced, + + Casing_Coil_Cupronickel, + Casing_Coil_Kanthal, + Casing_Coil_Nichrome, + Casing_Coil_TungstenSteel, + Casing_Coil_HSSG, + Casing_Coil_HSSS, + Casing_Coil_Naquadah, + Casing_Coil_Trinium, + Casing_Coil_NaquadahAlloy, + Casing_Coil_ElectrumFlux, + Casing_Coil_AwakenedDraconium, + Casing_Coil_CosmicNeutronium, + Casing_Coil_Infinity, + Casing_Coil_Hypogen, + Casing_Coil_Eternal, + + Casing_Tank_1, + Casing_Tank_2, + Casing_Tank_3, + Casing_Tank_4, + Casing_Tank_5, + Casing_Tank_6, + Casing_Tank_7, + Casing_Tank_8, + Casing_Tank_9, + Casing_Tank_10, + Casing_Tank_11, + Casing_Tank_12, + Casing_Tank_13, + Casing_Tank_14, + Casing_Tank_15, + Casing_Tank_0, + + MobRep_LV, + MobRep_MV, + MobRep_HV, + MobRep_EV, + MobRep_IV, + MobRep_LuV, + MobRep_ZPM, + MobRep_UV, + Cover_PlayerDetector, + Machine_Multi_HeatExchanger, + + Block_BronzePlate, + Block_SteelPlate, + Block_TitaniumPlate, + Block_IridiumTungstensteel, + Block_Plascrete, + Block_TungstenSteelReinforced, + Block_NaquadahPlate, + Block_NeutroniumPlate, + Block_BedrockiumCompressed, + + Honeycomb, + Charcoal_Pile, + Block_BrittleCharcoal, + Seismic_Prospector_Adv_LV, + Seismic_Prospector_Adv_MV, + Seismic_Prospector_Adv_HV, + Seismic_Prospector_Adv_EV, + OilDrill1, + OilDrill2, + OilDrill3, + + OilDrill4, + OilDrillInfinite, + ConcreteBackfiller1, + ConcreteBackfiller2, + OreDrill1, + OreDrill2, + OreDrill3, + OreDrill4, + PyrolyseOven, + OilCracker, + NanoForge, + Crop_Drop_UUMBerry, + Crop_Drop_UUABerry, + Empty_Board_Basic, + Empty_Board_Elite, + + Battery_Charger_4by4_ULV, + Battery_Charger_4by4_LV, + Battery_Charger_4by4_MV, + Battery_Charger_4by4_HV, + Battery_Charger_4by4_EV, + Battery_Charger_4by4_IV, + Battery_Charger_4by4_LuV, + Battery_Charger_4by4_ZPM, + Battery_Charger_4by4_UV, + Battery_Charger_4by4_MAX, + + MicroTransmitter_HV, + MicroTransmitter_EV, + MicroTransmitter_IV, + MicroTransmitter_LUV, + MicroTransmitter_ZPM, + MicroTransmitter_UV, + + Crop_Drop_Bauxite, + Crop_Drop_Ilmenite, + Crop_Drop_Pitchblende, + Crop_Drop_Uraninite, + Crop_Drop_Thorium, + Crop_Drop_Nickel, + Crop_Drop_Zinc, + Crop_Drop_Manganese, + Crop_Drop_Scheelite, + Crop_Drop_Platinum, + Crop_Drop_Iridium, + Crop_Drop_Osmium, + Crop_Drop_Naquadah, + Crop_Drop_Mica, + Uraniumcell_1, + Uraniumcell_2, + Uraniumcell_4, + Moxcell_1, + Moxcell_2, + Moxcell_4, + + Block_Powderbarrel, + GelledToluene, + + FluidFilter, + ItemFilter_Export, + ItemFilter_Import, + CuringOven, + Machine_Multi_Assemblyline, + Machine_Multi_DieselEngine, + Machine_Multi_ExtremeDieselEngine, + QuantumEye, + QuantumStar, + Gravistar, + NuclearStar, + Block_SSFUEL, + Block_MSSFUEL, + SFMixture, + MSFMixture, + + Depleted_Naquadah_1, + Depleted_Naquadah_2, + Depleted_Naquadah_4, + NaquadahCell_1, + NaquadahCell_2, + NaquadahCell_4, + Depleted_MNq_1, + Depleted_MNq_2, + Depleted_MNq_4, + MNqCell_1, + MNqCell_2, + MNqCell_4, + + Hatch_AutoMaintenance, + Machine_Multi_Cleanroom, + + Circuit_Board_Coated, + Circuit_Board_Coated_Basic, + Circuit_Board_Phenolic, + Circuit_Board_Phenolic_Good, + Circuit_Board_Epoxy, + Circuit_Board_Epoxy_Advanced, + Circuit_Board_Fiberglass, + Circuit_Board_Fiberglass_Advanced, + Circuit_Board_Multifiberglass_Elite, + Circuit_Board_Multifiberglass, + Circuit_Board_Wetware, + Circuit_Board_Wetware_Extreme, + Circuit_Board_Plastic, + Circuit_Board_Plastic_Advanced, + Circuit_Board_Bio, + Circuit_Board_Bio_Ultra, + Circuit_Board_Optical, + + Circuit_Parts_Resistor, + Circuit_Parts_ResistorSMD, + Circuit_Parts_Glass_Tube, + Circuit_Parts_Reinforced_Glass_Tube, + Circuit_Parts_Vacuum_Tube, + NandChip, + Circuit_Parts_Coil, + Circuit_Parts_Diode, + Circuit_Parts_DiodeSMD, + Circuit_Parts_Transistor, + Circuit_Parts_TransistorSMD, + Circuit_Parts_Capacitor, + Circuit_Parts_CapacitorSMD, + Circuit_Parts_GlassFiber, + Circuit_Parts_PetriDish, + + Circuit_Parts_ResistorASMD, + Circuit_Parts_DiodeASMD, + Circuit_Parts_TransistorASMD, + Circuit_Parts_CapacitorASMD, + + Circuit_Silicon_Ingot, + Circuit_Silicon_Ingot2, + Circuit_Silicon_Ingot3, + Circuit_Silicon_Ingot4, + Circuit_Silicon_Ingot5, + Circuit_Silicon_Ingot6, + Circuit_Silicon_Wafer, + Circuit_Silicon_Wafer2, + Circuit_Silicon_Wafer3, + Circuit_Silicon_Wafer4, + Circuit_Silicon_Wafer5, + Circuit_Silicon_Wafer6, + Circuit_Silicon_Wafer7, + Circuit_Wafer_ILC, + Circuit_Chip_ILC, + Circuit_Wafer_Ram, + Circuit_Chip_Ram, + + Circuit_Wafer_NAND, + Circuit_Chip_NAND, + Circuit_Wafer_NOR, + Circuit_Chip_NOR, + Circuit_Wafer_CPU, + Circuit_Chip_CPU, + Circuit_Wafer_SoC, + Circuit_Chip_SoC, + Circuit_Wafer_SoC2, + Circuit_Chip_SoC2, + Circuit_Wafer_PIC, + Circuit_Chip_PIC, + Circuit_Wafer_Simple_SoC, + Circuit_Chip_Simple_SoC, + + Circuit_Wafer_HPIC, + Circuit_Wafer_UHPIC, + Circuit_Chip_HPIC, + Circuit_Chip_UHPIC, + Circuit_Chip_ULPIC, + Circuit_Chip_LPIC, + Circuit_Chip_NPIC, + Circuit_Chip_PPIC, + Circuit_Chip_QPIC, + Circuit_Wafer_ULPIC, + Circuit_Wafer_NPIC, + Circuit_Wafer_PPIC, + Circuit_Wafer_QPIC, + Circuit_Wafer_LPIC, + Circuit_Wafer_NanoCPU, + Circuit_Chip_NanoCPU, + Circuit_Wafer_QuantumCPU, + Circuit_Chip_QuantumCPU, + Circuit_Wafer_Bioware, + + Circuit_Chip_CrystalCPU, + Circuit_Chip_CrystalSoC, + Circuit_Chip_CrystalSoC2, + Circuit_Chip_NeuroCPU, + Circuit_Chip_BioCPU, + Circuit_Chip_Stemcell, + Circuit_Parts_RawCrystalParts, + Circuit_Chip_Biocell, + Circuit_Chip_Optical, + Circuit_Parts_Chip_Bioware, + + Tube_Wires, + KevlarFiber, + WovenKevlar, + Spinneret, + GalliumArsenideCrystal, + GalliumArsenideCrystalSmallPart, + + Circuit_Microprocessor, + Circuit_Processor, + Circuit_Computer, + Circuit_Nanoprocessor, + Circuit_Nanocomputer, + Circuit_Elitenanocomputer, + Circuit_Quantumprocessor, + Circuit_Quantumcomputer, + Circuit_Masterquantumcomputer, + + Circuit_Quantummainframe, + Circuit_Crystalprocessor, + Circuit_Crystalcomputer, + Circuit_Ultimatecrystalcomputer, + Circuit_Crystalmainframe, + Circuit_Neuroprocessor, + Circuit_Wetwarecomputer, + Circuit_Wetwaresupercomputer, + Circuit_Wetwaremainframe, + Circuit_Parts_RawCrystalChip, + Circuit_Bioprocessor, + Circuit_Biomainframe, + + Circuit_OpticalProcessor, + Circuit_OpticalAssembly, + Circuit_OpticalComputer, + Circuit_OpticalMainframe, + Optical_Cpu_Containment_Housing, + Optically_Perfected_CPU, + Optically_Compatible_Memory, + + Circuit_ExoticProcessor, + Circuit_ExoticAssembly, + Circuit_ExoticComputer, + Circuit_ExoticMainframe, + + Circuit_CosmicProcessor, + Circuit_CosmicAssembly, + Circuit_CosmicComputer, + Circuit_CosmicMainframe, + + Circuit_TranscendentProcessor, + Circuit_TranscendentAssembly, + Circuit_TranscendentComputer, + Circuit_TranscendentMainframe, + + Machine_LV_CircuitAssembler, + Machine_MV_CircuitAssembler, + Machine_HV_CircuitAssembler, + Machine_EV_CircuitAssembler, + Machine_IV_CircuitAssembler, + Machine_LuV_CircuitAssembler, + Machine_ZPM_CircuitAssembler, + Machine_UV_CircuitAssembler, + + Circuit_Integrated_Good, + Machine_IV_LightningRod, + Machine_HV_LightningRod, + Machine_EV_LightningRod, + + ULV_Coil, + LV_Coil, + MV_Coil, + HV_Coil, + EV_Coil, + IV_Coil, + LuV_Coil, + ZPM_Coil, + UV_Coil, + UHV_Coil, + + Circuit_Parts_ResistorXSMD, + Circuit_Parts_DiodeXSMD, + Circuit_Parts_TransistorXSMD, + Circuit_Parts_CapacitorXSMD, + + Circuit_Parts_InductorSMD, + Circuit_Parts_InductorASMD, + Circuit_Parts_InductorXSMD, + + VOLUMETRIC_FLASK, + + Hatch_Input_Bus_ME, + Hatch_Input_Bus_ME_Advanced, + Hatch_Input_ME, + Hatch_Input_ME_Advanced, + Hatch_CraftingInput_Bus_ME, + Hatch_CraftingInput_Bus_ME_ItemOnly, + Hatch_CraftingInput_Bus_Slave, + AdvDebugStructureWriter, + + Superconducting_Magnet_Solenoid_MV, + Superconducting_Magnet_Solenoid_HV, + Superconducting_Magnet_Solenoid_EV, + Superconducting_Magnet_Solenoid_IV, + Superconducting_Magnet_Solenoid_LuV, + Superconducting_Magnet_Solenoid_ZPM, + Superconducting_Magnet_Solenoid_UV, + Superconducting_Magnet_Solenoid_UHV, + Superconducting_Magnet_Solenoid_UEV, + Superconducting_Magnet_Solenoid_UIV, + Superconducting_Magnet_Solenoid_UMV, + RadiantNaquadahAlloyCasing, + PCBFactory, + BasicPhotolithographicFrameworkCasing, + ReinforcedPhotolithographicFrameworkCasing, + RadiationProofPhotolithographicFrameworkCasing, + InfinityCooledCasing, + Machine_Multi_TranscendentPlasmaMixer, + Cover_Metrics_Transmitter, + NC_AdvancedSensorCard, + Machine_Multi_DroneCentre, + TierdDrone0, + TierdDrone1, + TierdDrone2, + Hatch_DroneDownLink; + + public static final ItemList[] DYE_ONLY_ITEMS = { Color_00, Color_01, Color_02, Color_03, Color_04, Color_05, + Color_06, Color_07, Color_08, Color_09, Color_10, Color_11, Color_12, Color_13, Color_14, Color_15 }, + SPRAY_CAN_DYES = { Spray_Color_00, Spray_Color_01, Spray_Color_02, Spray_Color_03, Spray_Color_04, + Spray_Color_05, Spray_Color_06, Spray_Color_07, Spray_Color_08, Spray_Color_09, Spray_Color_10, + Spray_Color_11, Spray_Color_12, Spray_Color_13, Spray_Color_14, Spray_Color_15 }, + SPRAY_CAN_DYES_USED = { Spray_Color_Used_00, Spray_Color_Used_01, Spray_Color_Used_02, Spray_Color_Used_03, + Spray_Color_Used_04, Spray_Color_Used_05, Spray_Color_Used_06, Spray_Color_Used_07, Spray_Color_Used_08, + Spray_Color_Used_09, Spray_Color_Used_10, Spray_Color_Used_11, Spray_Color_Used_12, Spray_Color_Used_13, + Spray_Color_Used_14, Spray_Color_Used_15 }, + TRANSFORMERS = { Transformer_LV_ULV, Transformer_MV_LV, Transformer_HV_MV, Transformer_EV_HV, Transformer_IV_EV, + Transformer_LuV_IV, Transformer_ZPM_LuV, Transformer_UV_ZPM, Transformer_MAX_UV }, + MACHINE_HULLS = { Hull_ULV, Hull_LV, Hull_MV, Hull_HV, Hull_EV, Hull_IV, Hull_LuV, Hull_ZPM, Hull_UV, + Hull_MAX }, + HATCHES_DYNAMO = { Hatch_Dynamo_ULV, Hatch_Dynamo_LV, Hatch_Dynamo_MV, Hatch_Dynamo_HV, Hatch_Dynamo_EV, + Hatch_Dynamo_IV, Hatch_Dynamo_LuV, Hatch_Dynamo_ZPM, Hatch_Dynamo_UV, Hatch_Dynamo_MAX }, + HATCHES_ENERGY = { Hatch_Energy_ULV, Hatch_Energy_LV, Hatch_Energy_MV, Hatch_Energy_HV, Hatch_Energy_EV, + Hatch_Energy_IV, Hatch_Energy_LuV, Hatch_Energy_ZPM, Hatch_Energy_UV, Hatch_Energy_MAX }, + HATCHES_INPUT = { Hatch_Input_ULV, Hatch_Input_LV, Hatch_Input_MV, Hatch_Input_HV, Hatch_Input_EV, + Hatch_Input_IV, Hatch_Input_LuV, Hatch_Input_ZPM, Hatch_Input_UV, Hatch_Input_MAX }, + HATCHES_INPUT_BUS = { Hatch_Input_Bus_ULV, Hatch_Input_Bus_LV, Hatch_Input_Bus_MV, Hatch_Input_Bus_HV, + Hatch_Input_Bus_EV, Hatch_Input_Bus_IV, Hatch_Input_Bus_LuV, Hatch_Input_Bus_ZPM, Hatch_Input_Bus_UV, + Hatch_Input_Bus_MAX }, + HATCHES_OUTPUT = { Hatch_Output_ULV, Hatch_Output_LV, Hatch_Output_MV, Hatch_Output_HV, Hatch_Output_EV, + Hatch_Output_IV, Hatch_Output_LuV, Hatch_Output_ZPM, Hatch_Output_UV, Hatch_Output_MAX }, + HATCHES_OUTPUT_BUS = { Hatch_Output_Bus_ULV, Hatch_Output_Bus_LV, Hatch_Output_Bus_MV, Hatch_Output_Bus_HV, + Hatch_Output_Bus_EV, Hatch_Output_Bus_IV, Hatch_Output_Bus_LuV, Hatch_Output_Bus_ZPM, Hatch_Output_Bus_UV, + Hatch_Output_Bus_MAX }, + HATCHES_MUFFLER = { Hatch_Muffler_LV, Hatch_Muffler_LV, Hatch_Muffler_MV, Hatch_Muffler_HV, Hatch_Muffler_EV, + Hatch_Muffler_IV, Hatch_Muffler_LuV, Hatch_Muffler_ZPM, Hatch_Muffler_UV, Hatch_Muffler_MAX }; + public static Fluid sOilExtraHeavy, sEpichlorhydrin, sDrillingFluid, sBlueVitriol, sNickelSulfate, sGreenVitriol, + sToluene, sNitrationMixture, sRocketFuel, sHydricSulfur, sIndiumConcentrate, sLeadZincSolution, + sHydrochloricAcid; + private ItemStack mStack; + private boolean mHasNotBeenSet; + private boolean mDeprecated; + private boolean mWarned; + + ItemList() { + mHasNotBeenSet = true; + } + + ItemList(boolean aDeprecated) { + if (aDeprecated) { + mDeprecated = true; + mHasNotBeenSet = true; + } + } + + @Override + public IItemContainer set(Item aItem) { + mHasNotBeenSet = false; + if (aItem == null) return this; + ItemStack aStack = new ItemStack(aItem, 1, 0); + mStack = GT_Utility.copyAmount(1, aStack); + return this; + } + + @Override + public IItemContainer set(ItemStack aStack) { + mHasNotBeenSet = false; + mStack = GT_Utility.copyAmount(1, aStack); + return this; + } + + @Override + public Item getItem() { + sanityCheck(); + if (GT_Utility.isStackInvalid(mStack)) return null; + return mStack.getItem(); + } + + @Override + public Block getBlock() { + sanityCheck(); + return GT_Utility.getBlockFromItem(getItem()); + } + + @Override + public final boolean hasBeenSet() { + return !mHasNotBeenSet; + } + + @Override + public boolean isStackEqual(Object aStack) { + return isStackEqual(aStack, false, false); + } + + @Override + public boolean isStackEqual(Object aStack, boolean aWildcard, boolean aIgnoreNBT) { + if (mDeprecated && !mWarned) { + new Exception(this + " is now deprecated").printStackTrace(GT_Log.err); + // warn only once + mWarned = true; + } + if (GT_Utility.isStackInvalid(aStack)) return false; + return GT_Utility.areUnificationsEqual((ItemStack) aStack, aWildcard ? getWildcard(1) : get(1), aIgnoreNBT); + } + + @Override + public ItemStack get(long aAmount, Object... aReplacements) { + sanityCheck(); + if (GT_Utility.isStackInvalid(mStack)) { + GT_Log.out.println("Object in the ItemList is null at:"); + new NullPointerException().printStackTrace(GT_Log.out); + return GT_Utility.copyAmount(aAmount, aReplacements); + } + return GT_Utility.copyAmount(aAmount, GT_OreDictUnificator.get(mStack)); + } + + @Override + public ItemStack getWildcard(long aAmount, Object... aReplacements) { + sanityCheck(); + if (GT_Utility.isStackInvalid(mStack)) return GT_Utility.copyAmount(aAmount, aReplacements); + return GT_Utility.copyAmountAndMetaData(aAmount, W, GT_OreDictUnificator.get(mStack)); + } + + @Override + public ItemStack getUndamaged(long aAmount, Object... aReplacements) { + sanityCheck(); + if (GT_Utility.isStackInvalid(mStack)) return GT_Utility.copyAmount(aAmount, aReplacements); + return GT_Utility.copyAmountAndMetaData(aAmount, 0, GT_OreDictUnificator.get(mStack)); + } + + @Override + public ItemStack getAlmostBroken(long aAmount, Object... aReplacements) { + sanityCheck(); + if (GT_Utility.isStackInvalid(mStack)) return GT_Utility.copyAmount(aAmount, aReplacements); + return GT_Utility.copyAmountAndMetaData(aAmount, mStack.getMaxDamage() - 1, GT_OreDictUnificator.get(mStack)); + } + + @Override + public ItemStack getWithName(long aAmount, String aDisplayName, Object... aReplacements) { + ItemStack rStack = get(1, aReplacements); + if (GT_Utility.isStackInvalid(rStack)) return NI; + + // CamelCase alphanumeric words from aDisplayName + StringBuilder tCamelCasedDisplayNameBuilder = new StringBuilder(); + final String[] tDisplayNameWords = aDisplayName.split("\\W"); + for (String tWord : tDisplayNameWords) { + if (tWord.length() > 0) tCamelCasedDisplayNameBuilder.append( + tWord.substring(0, 1) + .toUpperCase(Locale.US)); + if (tWord.length() > 1) tCamelCasedDisplayNameBuilder.append( + tWord.substring(1) + .toLowerCase(Locale.US)); + } + if (tCamelCasedDisplayNameBuilder.length() == 0) { + // CamelCased DisplayName is empty, so use hash of aDisplayName + tCamelCasedDisplayNameBuilder.append(((Long) (long) aDisplayName.hashCode())); + } + + // Construct a translation key from UnlocalizedName and CamelCased DisplayName + final String tKey = rStack.getUnlocalizedName() + ".with." + tCamelCasedDisplayNameBuilder + ".name"; + + rStack.setStackDisplayName(GT_LanguageManager.addStringLocalization(tKey, aDisplayName)); + return GT_Utility.copyAmount(aAmount, rStack); + } + + @Override + public ItemStack getWithCharge(long aAmount, int aEnergy, Object... aReplacements) { + ItemStack rStack = get(1, aReplacements); + if (GT_Utility.isStackInvalid(rStack)) return null; + GT_ModHandler.chargeElectricItem(rStack, aEnergy, Integer.MAX_VALUE, true, false); + return GT_Utility.copyAmount(aAmount, rStack); + } + + @Override + public ItemStack getWithDamage(long aAmount, long aMetaValue, Object... aReplacements) { + sanityCheck(); + if (GT_Utility.isStackInvalid(mStack)) return GT_Utility.copyAmount(aAmount, aReplacements); + return GT_Utility.copyAmountAndMetaData(aAmount, aMetaValue, GT_OreDictUnificator.get(mStack)); + } + + @Override + public IItemContainer registerOre(Object... aOreNames) { + sanityCheck(); + for (Object tOreName : aOreNames) GT_OreDictUnificator.registerOre(tOreName, get(1)); + return this; + } + + @Override + public IItemContainer registerWildcardAsOre(Object... aOreNames) { + sanityCheck(); + for (Object tOreName : aOreNames) GT_OreDictUnificator.registerOre(tOreName, getWildcard(1)); + return this; + } + + /** + * Returns the internal stack. This method is unsafe. It's here only for quick operations. DON'T CHANGE THE RETURNED + * VALUE! + */ + public ItemStack getInternalStack_unsafe() { + return mStack; + } + + private void sanityCheck() { + if (mHasNotBeenSet) + throw new IllegalAccessError("The Enum '" + name() + "' has not been set to an Item at this time!"); + if (mDeprecated && !mWarned) { + new Exception(this + " is now deprecated").printStackTrace(GT_Log.err); + // warn only once + mWarned = true; + } + } +} diff --git a/src/main/java/gregtech/api/enums/MachineType.java b/src/main/java/gregtech/api/enums/MachineType.java new file mode 100644 index 0000000000..14e1781350 --- /dev/null +++ b/src/main/java/gregtech/api/enums/MachineType.java @@ -0,0 +1,136 @@ +package gregtech.api.enums; + +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.StatCollector; + +public enum MachineType { + + ALLOY_SMELTER(FunnyTexts.ALLOY_SMELTER, "gt.recipe.alloysmelter"), + ARC_FURNACE(FunnyTexts.ARC_FURNACE, "gt.recipe.arcfurnace"), + ASSEMBLER(FunnyTexts.ASSEMBLER, "gt.recipe.assembler"), + AUTOCLAVE(FunnyTexts.AUTOCLAVE, "gt.recipe.autoclave"), + BENDING_MACHINE(FunnyTexts.BENDING_MACHINE, "gt.recipe.metalbender"), + BREWERY(FunnyTexts.BREWERY, "gt.recipe.brewer"), + CANNER(FunnyTexts.CANNER, "gt.recipe.canner"), + CENTRIFUGE(FunnyTexts.CENTRIFUGE, "gt.recipe.centrifuge"), + CHEMICAL_BATH(FunnyTexts.CHEMICAL_BATH, "gt.recipe.chemicalbath"), + CHEMICAL_REACTOR(FunnyTexts.CHEMICAL_REACTOR, "gt.recipe.chemicalreactor"), + CIRCUIT_ASSEMBLER(FunnyTexts.CIRCUIT_ASSEMBLER, "gt.recipe.circuitassembler"), + COMPRESSOR(FunnyTexts.COMPRESSOR, "gt.recipe.compressor"), + CUTTING_MACHINE(FunnyTexts.CUTTING_MACHINE, "gt.recipe.cuttingsaw"), + DISTILLERY(FunnyTexts.DISTILLERY, "gt.recipe.distillery"), + ELECTRIC_FURNACE(FunnyTexts.ELECTRIC_FURNACE, "gt.recipe.furnace"), + ELECTROLYZER(FunnyTexts.ELECTROLYZER, "gt.recipe.electrolyzer"), + ELECTROMAGNETIC_SEPARATOR(FunnyTexts.ELECTROMAGNETIC_SEPARATOR, "gt.recipe.electromagneticseparator"), + EXTRACTOR(FunnyTexts.EXTRACTOR, "gt.recipe.extractor"), + EXTRUDER(FunnyTexts.EXTRUDER, "gt.recipe.extruder"), + FERMENTER(FunnyTexts.FERMENTER, "gt.recipe.fermenter"), + FLUID_CANNER(FunnyTexts.FLUID_CANNER, "gt.recipe.fluidcanner"), + FLUID_EXTRACTOR(FunnyTexts.FLUID_EXTRACTOR, "gt.recipe.fluidextractor"), + FLUID_SOLIDIFIER(FunnyTexts.FLUID_SOLIDIFIER, "gt.recipe.fluidsolidifier"), + FORGE_HAMMER(FunnyTexts.FORGE_HAMMER, "gt.recipe.hammer"), + FORMING_PRESS(FunnyTexts.FORMING_PRESS, "gt.recipe.press"), + FLUID_HEATER(FunnyTexts.FLUID_HEATER, "gt.recipe.fluidheater"), + LASER_ENGRAVER(FunnyTexts.LASER_ENGRAVER, "gt.recipe.laserengraver"), + LATHE(FunnyTexts.LATHE, "gt.recipe.lathe"), + MACERATOR(FunnyTexts.MACERATOR, "gt.recipe.macerator"), + MACERATOR_PULVERIZER(FunnyTexts.MACERATOR_PULVERIZER, "gt.recipe.macerator_pulverizer"), + MATTER_AMPLIFIER(FunnyTexts.MATTER_AMPLIFIER, "gt.recipe.uuamplifier"), + MATTER_FABRICATOR(FunnyTexts.MATTER_FABRICATOR, "gt.recipe.massfab"), + MICROWAVE(FunnyTexts.MICROWAVE, "gt.recipe.microwave"), + MIXER(FunnyTexts.MIXER, "gt.recipe.mixer"), + ORE_WASHER(FunnyTexts.ORE_WASHER, "gt.recipe.orewasher"), + OVEN(FunnyTexts.OVEN, "gt.recipe.oven"), + PACKAGER(FunnyTexts.PACKAGER, "gt.recipe.packager"), + PLASMA_ARC_FURNACE(FunnyTexts.PLASMA_ARC_FURNACE, "gt.recipe.plasmaarcfurnace"), + POLARIZER(FunnyTexts.POLARIZER, "gt.recipe.polarizer"), + PRINTER(FunnyTexts.PRINTER, "gt.recipe.printer"), + RECYCLER(FunnyTexts.RECYCLER, "ic.recipe.recycler"), + REPLICATOR(FunnyTexts.REPLICATOR, "gt.recipe.replicator"), + SCANNER(FunnyTexts.SCANNER, "gt.recipe.scanner"), + ROCKBREAKER(FunnyTexts.ROCKBREAKER, "gt.recipe.rockbreaker"), + SIFTER(FunnyTexts.SIFTER, "gt.recipe.sifter"), + SLICER(FunnyTexts.SLICER, "gt.recipe.slicer"), + THERMAL_CENTRIFUGE(FunnyTexts.THERMAL_CENTRIFUGE, "gt.recipe.thermalcentrifuge"), + UNPACKAGER(FunnyTexts.UNPACKAGER, "gt.recipe.unpackager"), + WIREMILL(FunnyTexts.WIREMILL, "gt.recipe.wiremill"); + + private static class FunnyTexts { + + static final String ALLOY_SMELTER = "gt.recipe.alloysmelter.description"; + static final String ARC_FURNACE = "gt.recipe.arcfurnace.description"; + static final String ASSEMBLER = "gt.recipe.assembler.description"; + static final String AUTOCLAVE = "gt.recipe.autoclave.description"; + static final String BENDING_MACHINE = "gt.recipe.metalbender.description"; + static final String BREWERY = "gt.recipe.brewer.description"; + static final String CANNER = "gt.recipe.canner.description"; + static final String CENTRIFUGE = "gt.recipe.centrifuge.description"; + static final String CHEMICAL_BATH = "gt.recipe.chemicalbath.description"; + static final String CHEMICAL_REACTOR = "gt.recipe.chemicalreactor.description"; + static final String CIRCUIT_ASSEMBLER = "gt.recipe.circuitassembler.description"; + static final String COMPRESSOR = "gt.recipe.compressor.description"; + static final String CUTTING_MACHINE = "gt.recipe.cuttingsaw.description"; + static final String DISTILLERY = "gt.recipe.distillery.description"; + static final String ELECTRIC_FURNACE = "gt.recipe.furnace.description"; + static final String ELECTROLYZER = "gt.recipe.electrolyzer.description"; + static final String ELECTROMAGNETIC_SEPARATOR = "gt.recipe.electromagneticseparator.description"; + static final String EXTRACTOR = "gt.recipe.extractor.description"; + static final String EXTRUDER = "gt.recipe.extruder.description"; + static final String FERMENTER = "gt.recipe.fermenter.description"; + static final String FLUID_CANNER = "gt.recipe.fluidcanner.description"; + static final String FLUID_EXTRACTOR = "gt.recipe.fluidextractor.description"; + static final String FLUID_HEATER = "gt.recipe.fluidheater.description"; + static final String FLUID_SOLIDIFIER = "gt.recipe.fluidsolidifier.description"; + static final String FORGE_HAMMER = "gt.recipe.hammer.description"; + static final String FORMING_PRESS = "gt.recipe.press.description"; + static final String LASER_ENGRAVER = "gt.recipe.laserengraver.description"; + static final String LATHE = "gt.recipe.lathe.description"; + static final String MACERATOR = "gt.recipe.macerator.description"; + static final String MACERATOR_PULVERIZER = "gt.recipe.macerator_pulverizer.description"; + static final String MATTER_AMPLIFIER = "gt.recipe.uuamplifier.description"; + static final String MATTER_FABRICATOR = "gt.recipe.massfab.description"; + static final String MICROWAVE = "gt.recipe.microwave.description"; + static final String MIXER = "gt.recipe.mixer.description"; + static final String ORE_WASHER = "gt.recipe.orewasher.description"; + static final String OVEN = "gt.recipe.oven.description"; + static final String PACKAGER = "gt.recipe.packager.description"; + static final String PLASMA_ARC_FURNACE = "gt.recipe.plasmaarcfurnace.description"; + static final String POLARIZER = "gt.recipe.polarizer.description"; + static final String PRINTER = "gt.recipe.printer.description"; + static final String RECYCLER = "ic.recipe.recycler.description"; + static final String REPLICATOR = "gt.recipe.replicator.description"; + static final String ROCKBREAKER = "gt.recipe.rockbreaker.description"; + static final String SIFTER = "gt.recipe.sifter.description"; + static final String SCANNER = "gt.recipe.scanner.description"; + static final String SLICER = "gt.recipe.slicer.description"; + static final String THERMAL_CENTRIFUGE = "gt.recipe.thermalcentrifuge.description"; + static final String UNPACKAGER = "gt.recipe.unpackager.description"; + static final String WIREMILL = "gt.recipe.wiremill.description"; + } + + private static final String TT_machineType = "GT5U.MBTT.MachineType"; + + private final String name; + private final String description; + + MachineType(String machineDescription, String machineType) { + this.description = machineDescription; + this.name = machineType; + } + + public String type() { + return StatCollector.translateToLocal(this.name); + } + + public String description() { + return StatCollector.translateToLocal(this.description); + } + + public String[] tooltipDescription() { + return new String[] { description(), + StatCollector.translateToLocal(TT_machineType) + ": " + + EnumChatFormatting.YELLOW + + type() + + EnumChatFormatting.RESET }; + } +} diff --git a/src/main/java/gregtech/api/enums/MaterialBuilder.java b/src/main/java/gregtech/api/enums/MaterialBuilder.java new file mode 100644 index 0000000000..98ed5fa3f7 --- /dev/null +++ b/src/main/java/gregtech/api/enums/MaterialBuilder.java @@ -0,0 +1,276 @@ +package gregtech.api.enums; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import gregtech.api.objects.MaterialStack; + +public class MaterialBuilder { + + public static final int DIESEL = 0, GAS = 1, THERMAL = 2, SEMIFLUID = 3, PLASMA = 4, MAGIC = 5; + + private final int metaItemSubID; + private final TextureSet iconSet; + private float toolSpeed = 1.0f; + private int durability = 0; + private int toolQuality = 0; + private int types = 0; + private int r = 255, g = 255, b = 255, a = 0; + private String name; + private final String defaultLocalName; + private int fuelType = 0; + private int fuelPower = 0; + private int meltingPoint = 0; + private int blastFurnaceTemp = 0; + private boolean blastFurnaceRequired = false; + private boolean transparent = false; + private int oreValue = 1; + private int densityMultiplier = 1; + private int densityDivider = 1; + private Dyes color = Dyes._NULL; + private int extraData = 0; + private List<MaterialStack> materialList = new ArrayList<>(); + private List<TC_Aspects.TC_AspectStack> aspects = new ArrayList<>(); + private boolean hasCorrespondingFluid = false; + private boolean hasCorrespondingGas = false; + private boolean canBeCracked = false; + private int liquidTemperature = 300; + private int gasTemperature = 300; + + public MaterialBuilder(int metaItemSubID, TextureSet iconSet, String defaultLocalName) { + this.metaItemSubID = metaItemSubID; + this.iconSet = iconSet; + this.name = defaultLocalName.replace(" ", "") + .replace("-", ""); + this.defaultLocalName = defaultLocalName; + } + + public Materials constructMaterial() { + return new Materials( + metaItemSubID, + iconSet, + toolSpeed, + durability, + toolQuality, + types, + r, + g, + b, + a, + name, + defaultLocalName, + fuelType, + fuelPower, + meltingPoint, + blastFurnaceTemp, + blastFurnaceRequired, + transparent, + oreValue, + densityMultiplier, + densityDivider, + color, + extraData, + materialList, + aspects).setHasCorrespondingFluid(hasCorrespondingFluid) + .setHasCorrespondingGas(hasCorrespondingGas) + .setCanBeCracked(canBeCracked); + } + + public MaterialBuilder setName(String name) { + this.name = name; + return this; + } + + public MaterialBuilder setTypes(int types) { + this.types = types; + return this; + } + + public MaterialBuilder addDustItems() { + types = types | 1; + return this; + } + + public MaterialBuilder addMetalItems() { + types = types | 2; + return this; + } + + public MaterialBuilder addGemItems() { + types = types | 4; + return this; + } + + public MaterialBuilder addOreItems() { + types = types | 8; + return this; + } + + public MaterialBuilder addCell() { + types = types | 16; + return this; + } + + public MaterialBuilder addPlasma() { + types = types | 32; + return this; + } + + public MaterialBuilder addToolHeadItems() { + types = types | 64; + return this; + } + + public MaterialBuilder addGearItems() { + types = types | 128; + return this; + } + + public MaterialBuilder addFluid() { + this.hasCorrespondingFluid = true; + return this; + } + + public MaterialBuilder addGas() { + this.hasCorrespondingGas = true; + return this; + } + + public MaterialBuilder setRGBA(int r, int g, int b, int a) { + this.r = r; + this.g = g; + this.b = b; + this.a = a; + return this; + } + + public MaterialBuilder setRGB(int r, int g, int b) { + this.r = r; + this.g = g; + this.b = b; + return this; + } + + public MaterialBuilder setTransparent(boolean transparent) { + this.transparent = transparent; + return this; + } + + public MaterialBuilder setColor(Dyes color) { + this.color = color; + return this; + } + + public MaterialBuilder setToolSpeed(float toolSpeed) { + this.toolSpeed = toolSpeed; + return this; + } + + public MaterialBuilder setDurability(int durability) { + this.durability = durability; + return this; + } + + public MaterialBuilder setToolQuality(int toolQuality) { + this.toolQuality = toolQuality; + return this; + } + + public MaterialBuilder setFuelType(int fuelType) { + this.fuelType = fuelType; + return this; + } + + public MaterialBuilder setFuelPower(int fuelPower) { + this.fuelPower = fuelPower; + return this; + } + + public MaterialBuilder setMeltingPoint(int meltingPoint) { + this.meltingPoint = meltingPoint; + return this; + } + + public MaterialBuilder setBlastFurnaceTemp(int blastFurnaceTemp) { + this.blastFurnaceTemp = blastFurnaceTemp; + return this; + } + + public MaterialBuilder setBlastFurnaceRequired(boolean blastFurnaceRequired) { + this.blastFurnaceRequired = blastFurnaceRequired; + return this; + } + + public MaterialBuilder setOreValue(int oreValue) { + this.oreValue = oreValue; + return this; + } + + public MaterialBuilder setDensityMultiplier(int densityMultiplier) { + this.densityMultiplier = densityMultiplier; + return this; + } + + public MaterialBuilder setDensityDivider(int densityDivider) { + this.densityDivider = densityDivider; + return this; + } + + public MaterialBuilder setExtraData(int extraData) { + this.extraData = extraData; + return this; + } + + public MaterialBuilder addElectrolyzerRecipe() { + extraData = extraData | 1; + return this; + } + + public MaterialBuilder addCentrifugeRecipe() { + extraData = extraData | 2; + return this; + } + + public MaterialBuilder setMaterialList(List<MaterialStack> materialList) { + this.materialList = materialList; + return this; + } + + public MaterialBuilder setMaterialList(MaterialStack... materials) { + this.materialList = Arrays.asList(materials); + return this; + } + + public MaterialBuilder setAspects(List<TC_Aspects.TC_AspectStack> aspects) { + this.aspects = aspects; + return this; + } + + public int getLiquidTemperature() { + return liquidTemperature; + } + + public MaterialBuilder setLiquidTemperature(int liquidTemperature) { + this.liquidTemperature = liquidTemperature; + return this; + } + + public int getGasTemperature() { + return gasTemperature; + } + + public MaterialBuilder setGasTemperature(int gasTemperature) { + this.gasTemperature = gasTemperature; + return this; + } + + public boolean canBeCracked() { + return canBeCracked; + } + + public MaterialBuilder setCanBeCracked(boolean canBeCracked) { + this.canBeCracked = canBeCracked; + return this; + } +} diff --git a/src/main/java/gregtech/api/enums/Materials.java b/src/main/java/gregtech/api/enums/Materials.java new file mode 100644 index 0000000000..0bede04846 --- /dev/null +++ b/src/main/java/gregtech/api/enums/Materials.java @@ -0,0 +1,3307 @@ +package gregtech.api.enums; + +import static gregtech.api.enums.FluidState.GAS; +import static gregtech.api.enums.GT_Values.M; +import static gregtech.api.enums.Mods.Thaumcraft; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import javax.annotation.Nonnull; + +import net.minecraft.enchantment.Enchantment; +import net.minecraft.item.ItemStack; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidStack; + +import gregtech.GT_Mod; +import gregtech.api.GregTech_API; +import gregtech.api.enums.TC_Aspects.TC_AspectStack; +import gregtech.api.fluid.GT_FluidFactory; +import gregtech.api.interfaces.IColorModulationContainer; +import gregtech.api.interfaces.IMaterialHandler; +import gregtech.api.interfaces.ISubTagContainer; +import gregtech.api.objects.MaterialStack; +import gregtech.api.util.GT_OreDictUnificator; +import gregtech.api.util.GT_Utility; +import gregtech.common.render.items.CosmicNeutroniumRenderer; +import gregtech.common.render.items.GT_GeneratedMaterial_Renderer; +import gregtech.common.render.items.GaiaSpiritRenderer; +import gregtech.common.render.items.InfinityRenderer; +import gregtech.common.render.items.TranscendentMetalRenderer; +import gregtech.common.render.items.UniversiumRenderer; +import gregtech.loaders.materialprocessing.ProcessingConfig; +import gregtech.loaders.materialprocessing.ProcessingModSupport; + +@SuppressWarnings("unused") // API Legitimately has unused Members and Methods +public class Materials implements IColorModulationContainer, ISubTagContainer { + + public static final List<IMaterialHandler> mMaterialHandlers = new ArrayList<>(); + private static final Map<String, Materials> MATERIALS_MAP = new LinkedHashMap<>(); + + public static final Map<Fluid, Materials> FLUID_MAP = new LinkedHashMap<>(); + + /** + * This is for keeping compatibility with addons mods (Such as TinkersGregworks etc.) that looped over the old + * materials enum + */ + @Deprecated + public static Collection<Materials> VALUES = new LinkedHashSet<>(); + /** + * This is the Default Material returned in case no Material has been found or a NullPointer has been inserted at a + * location where it shouldn't happen. + */ + + // Spotless breaks the table below into many, many lines + // spotless:off + public static Materials _NULL = new Materials(-1, TextureSet.SET_NONE, 1.0F, 0, 0, 0, 255, 255, 255, 0, "NULL", "NULL", 0, 0, 0, 0, false, false, 1, 1, 1, Dyes._NULL, Element._NULL, Collections.singletonList(new TC_AspectStack(TC_Aspects.VACUOS, 1))); + /** + * Direct Elements + */ + public static Materials Aluminium = new Materials( 19, TextureSet.SET_DULL , 10.0F, 128, 2, 1|2 |8 |32|64|128 , 128, 200, 240, 0, "Aluminium" , "Aluminium" , 0, 0, 933, 1700, true, false, 3, 1, 1, Dyes.dyeLightBlue , Element.Al , Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.VOLATUS, 1))).disableAutoGeneratedBlastFurnaceRecipes(); + public static Materials Americium = new Materials( 103, TextureSet.SET_METALLIC , 1.0F, 0, 3, 1|2 |8 |32 , 200, 200, 200, 0, "Americium" , "Americium" , 0, 0, 1449, 0, false, false, 3, 1, 1, Dyes.dyeLightGray , Element.Am , Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.RADIO, 1))); + public static Materials Antimony = new Materials( 58, TextureSet.SET_SHINY , 1.0F, 0, 2, 1|2 |8 |32 , 220, 220, 240, 0, "Antimony" , "Antimony" , 0, 0, 903, 0, false, false, 2, 1, 1, Dyes.dyeLightGray , Element.Sb , Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.AQUA, 1))); + public static Materials Argon = new Materials( 24, TextureSet.SET_FLUID , 1.0F, 0, 2, 16|32 , 0, 255, 0, 240, "Argon" , "Argon" , 0, 0, 83, 0, false, true, 5, 1, 1, Dyes.dyeGreen , Element.Ar , Collections.singletonList(new TC_AspectStack(TC_Aspects.AER, 2))); + public static Materials Arsenic = new Materials( 39, TextureSet.SET_DULL , 1.0F, 0, 2, 1|2 |8|16|32 , 255, 255, 255, 0, "Arsenic" , "Arsenic" , 0, 0, 1090, 0, false, false, 3, 1, 1, Dyes.dyeOrange , Element.As , Collections.singletonList(new TC_AspectStack(TC_Aspects.VENENUM, 3))); + public static Materials Barium = new Materials( 63, TextureSet.SET_METALLIC , 1.0F, 0, 2, 1|2 |8 |32 , 255, 255, 255, 0, "Barium" , "Barium" , 0, 0, 1000, 0, false, false, 1, 1, 1, Dyes._NULL , Element.Ba , Collections.singletonList(new TC_AspectStack(TC_Aspects.VINCULUM, 3))); + public static Materials Beryllium = new Materials( 8, TextureSet.SET_METALLIC , 14.0F, 64, 2, 1|2 |8 |32|64 , 100, 180, 100, 0, "Beryllium" , "Beryllium" , 0, 0, 1560, 0, false, false, 6, 1, 1, Dyes.dyeGreen , Element.Be , Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.LUCRUM, 1))); + public static Materials Bismuth = new Materials( 90, TextureSet.SET_METALLIC , 6.0F, 64, 1, 1|2 |8 |32|64|128 , 100, 160, 160, 0, "Bismuth" , "Bismuth" , 0, 0, 544, 0, false, false, 2, 1, 1, Dyes.dyeCyan , Element.Bi , Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.INSTRUMENTUM, 1))); + public static Materials Boron = new Materials( 9, TextureSet.SET_DULL , 1.0F, 0, 2, 1 |32 , 210, 250, 210, 0, "Boron" , "Boron" , 0, 0, 2349, 0, false, false, 1, 1, 1, Dyes.dyeWhite , Element.B , Collections.singletonList(new TC_AspectStack(TC_Aspects.VITREUS, 3))); + public static Materials Caesium = new Materials( 62, TextureSet.SET_METALLIC , 1.0F, 0, 2, 1|2 |8 |32 , 176, 196, 222, 0, "Caesium" , "Caesium" , 0, 0, 301, 0, false, false, 4, 1, 1, Dyes._NULL , Element.Cs , Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.RADIO, 1))); + public static Materials Calcium = new Materials( 26, TextureSet.SET_METALLIC , 1.0F, 0, 2, 1 |32 , 255, 245, 245, 0, "Calcium" , "Calcium" , 0, 0, 1115, 1115, true, false, 4, 1, 1, Dyes.dyePink , Element.Ca , Arrays.asList(new TC_AspectStack(TC_Aspects.SANO, 1), new TC_AspectStack(TC_Aspects.TUTAMEN, 1))); + public static Materials Carbon = new Materials( 10, TextureSet.SET_DULL , 1.0F, 64, 2, 1|2 |16|32|64|128 , 20, 20, 20, 0, "Carbon" , "Carbon" , 0, 0, 3800, 0, false, false, 2, 1, 1, Dyes.dyeBlack , Element.C , Arrays.asList(new TC_Aspects.TC_AspectStack(TC_Aspects.VITREUS, 1), new TC_AspectStack(TC_Aspects.IGNIS, 1))); + public static Materials Cadmium = new Materials( 55, TextureSet.SET_SHINY , 1.0F, 0, 2, 1 |8 |32 , 50, 50, 60, 0, "Cadmium" , "Cadmium" , 0, 0, 594, 0, false, false, 3, 1, 1, Dyes.dyeGray , Element.Cd , Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 1), new TC_AspectStack(TC_Aspects.POTENTIA, 1), new TC_AspectStack(TC_Aspects.VENENUM, 1))); + public static Materials Cerium = new Materials( 65, TextureSet.SET_METALLIC , 1.0F, 0, 2, 1|2 |8 |32 , 123, 212, 144, 0, "Cerium" , "Cerium" , 0, 0, 1068, 1068, true, false, 4, 1, 1, Dyes._NULL , Element.Ce , Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.RADIO, 1))); + public static Materials Chlorine = new Materials( 23, TextureSet.SET_FLUID , 1.0F, 0, 2, 16|32 , 255, 255, 255, 0, "Chlorine" , "Chlorine" , 0, 0, 171, 0, false, false, 2, 1, 1, Dyes.dyeCyan , Element.Cl , Arrays.asList(new TC_AspectStack(TC_Aspects.AQUA, 2), new TC_AspectStack(TC_Aspects.PANNUS, 1))); + public static Materials Chrome = new Materials( 30, TextureSet.SET_SHINY , 11.0F, 256, 3, 1|2 |8 |32|64|128 , 255, 230, 230, 0, "Chrome" , "Chrome" , 0, 0, 2180, 1700, true, false, 5, 1, 1, Dyes.dyePink , Element.Cr , Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.MACHINA, 1))); + public static Materials Cobalt = new Materials( 33, TextureSet.SET_METALLIC , 8.0F, 512, 3, 1|2 |8 |32|64|128 , 80, 80, 250, 0, "Cobalt" , "Cobalt" , 0, 0, 1768, 1700, true, false, 3, 1, 1, Dyes.dyeBlue , Element.Co , Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.INSTRUMENTUM, 1))).disableAutoGeneratedBlastFurnaceRecipes(); + public static Materials Copper = new Materials( 35, TextureSet.SET_DULL , 1.0F, 0, 1, 1|2 |8 |32 |128 , 255, 100, 0, 0, "Copper" , "Copper" , 0, 0, 1357, 0, false, false, 3, 1, 1, Dyes.dyeOrange , Element.Cu , Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.PERMUTATIO, 1))); + public static Materials Deuterium = new Materials( 2, TextureSet.SET_FLUID , 1.0F, 0, 2, 16|32 , 255, 255, 0, 240, "Deuterium" , "Deuterium" , 0, 0, 14, 0, false, true, 10, 1, 1, Dyes.dyeYellow , Element.D , Collections.singletonList(new TC_AspectStack(TC_Aspects.AQUA, 3))); + public static Materials Dysprosium = new Materials( 73, TextureSet.SET_METALLIC , 1.0F, 0, 2, 1|2 |8 |32 , 105, 209, 80, 0, "Dysprosium" , "Dysprosium" , 0, 0, 1680, 1680, true, false, 4, 1, 1, Dyes._NULL , Element.Dy , Collections.singletonList(new TC_AspectStack(TC_Aspects.METALLUM, 3))); + public static Materials Empty = new Materials( 0, TextureSet.SET_NONE , 1.0F, 0, 2, 256/*Only when needed*/ , 255, 255, 255, 255, "Empty" , "Empty" , 0, 0, -1, 0, false, true, 1, 1, 1, Dyes._NULL , Element._NULL , Collections.singletonList(new TC_AspectStack(TC_Aspects.VACUOS, 2))); + public static Materials Erbium = new Materials( 75, TextureSet.SET_SHINY , 1.0F, 0, 2, 1|2 |8 |32 , 176, 152, 81, 0, "Erbium" , "Erbium" , 0, 0, 1802, 1802, true, false, 4, 1, 1, Dyes._NULL , Element.Er , Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.RADIO, 1))); + public static Materials Europium = new Materials( 70, TextureSet.SET_SHINY , 1.0F, 0, 2, 1|2 |8 |32 , 246, 181, 255, 0, "Europium" , "Europium" , 0, 0, 1099, 1099, true, false, 4, 1, 1, Dyes._NULL , Element.Eu , Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.RADIO, 1))); + public static Materials Fluorine = new Materials( 14, TextureSet.SET_FLUID , 1.0F, 0, 2, 16|32 , 255, 255, 255, 127, "Fluorine" , "Fluorine" , 0, 0, 53, 0, false, true, 2, 1, 1, Dyes.dyeGreen , Element.F , Collections.singletonList(new TC_AspectStack(TC_Aspects.PERDITIO, 2))); + public static Materials Gadolinium = new Materials( 71, TextureSet.SET_METALLIC , 1.0F, 0, 2, 1|2 |8 |32 , 59, 186, 28, 0, "Gadolinium" , "Gadolinium" , 0, 0, 1585, 1585, true, false, 4, 1, 1, Dyes._NULL , Element.Gd , Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.RADIO, 1))); + public static Materials Gallium = new Materials( 37, TextureSet.SET_SHINY , 1.0F, 64, 2, 1|2 |8 |32 , 220, 220, 255, 0, "Gallium" , "Gallium" , 0, 0, 302, 0, false, false, 5, 1, 1, Dyes.dyeLightGray , Element.Ga , Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.ELECTRUM, 1))); + public static Materials Gold = new Materials( 86, TextureSet.SET_SHINY , 12.0F, 64, 2, 1|2 |8 |32|64|128 , 255, 255, 30, 0, "Gold" , "Gold" , 0, 0, 1337, 0, false, false, 4, 1, 1, Dyes.dyeYellow , Element.Au , Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.LUCRUM, 2))); + public static Materials Holmium = new Materials( 74, TextureSet.SET_SHINY , 1.0F, 0, 2, 1|2 |8 |32 , 22, 8, 166, 0, "Holmium" , "Holmium" , 0, 0, 1734, 1734, true, false, 4, 1, 1, Dyes._NULL , Element.Ho , Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.RADIO, 1))); + public static Materials Hydrogen = new Materials( 1, TextureSet.SET_FLUID , 1.0F, 0, 2, 16|32 , 0, 0, 255, 240, "Hydrogen" , "Hydrogen" , 1, 20, 14, 0, false, true, 2, 1, 1, Dyes.dyeBlue , Element.H , Collections.singletonList(new TC_AspectStack(TC_Aspects.AQUA, 1))); + public static Materials Helium = new Materials( 4, TextureSet.SET_FLUID , 1.0F, 0, 2, 16|32 , 255, 255, 0, 240, "Helium" , "Helium" , 0, 0, 1, 0, false, true, 5, 1, 1, Dyes.dyeYellow , Element.He , Collections.singletonList(new TC_AspectStack(TC_Aspects.AER, 2))); + public static Materials Helium_3 = new Materials( 5, TextureSet.SET_FLUID , 1.0F, 0, 2, 16|32 , 255, 255, 0, 240, "Helium_3" , "Helium-3" , 0, 0, 1, 0, false, true, 10, 1, 1, Dyes.dyeYellow , Element.He_3 , Collections.singletonList(new TC_AspectStack(TC_Aspects.AER, 3))); + public static Materials Indium = new Materials( 56, TextureSet.SET_METALLIC , 1.0F, 0, 2, 1|2 |8 |32 , 64, 0, 128, 0, "Indium" , "Indium" , 0, 0, 429, 0, false, false, 4, 1, 1, Dyes.dyeGray , Element.In , Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.RADIO, 1))); + public static Materials Iridium = new Materials( 84, TextureSet.SET_DULL , 6.0F, 2560, 3, 1|2 |8 |32|64|128 , 240, 240, 245, 0, "Iridium" , "Iridium" , 0, 0, 2719, 4500, true, false, 10, 1, 1, Dyes.dyeWhite , Element.Ir , Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.MACHINA, 1))).disableAutoGeneratedBlastFurnaceRecipes().disableAutoGeneratedVacuumFreezerRecipe(); + public static Materials Iron = new Materials( 32, TextureSet.SET_METALLIC , 6.0F, 256, 2, 1|2 |8 |32|64|128 , 200, 200, 200, 0, "Iron" , "Iron" , 0, 0, 1811, 0, false, false, 3, 1, 1, Dyes.dyeLightGray , Element.Fe , Collections.singletonList(new TC_AspectStack(TC_Aspects.METALLUM, 3))); + public static Materials Lanthanum = new Materials( 64, TextureSet.SET_METALLIC , 1.0F, 0, 2, 1|2 |8 |32 , 138, 138, 138, 0, "Lanthanum" , "Lanthanum" , 0, 0, 1193, 1193, true, false, 4, 1, 1, Dyes._NULL , Element.La , Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.RADIO, 1))); + public static Materials Lead = new Materials( 89, TextureSet.SET_DULL , 8.0F, 64, 1, 1|2 |8 |32|64|128 , 140, 100, 140, 0, "Lead" , "Lead" , 0, 0, 600, 0, false, false, 3, 1, 1, Dyes.dyePurple , Element.Pb , Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.ORDO, 1))); + public static Materials Lithium = new Materials( 6, TextureSet.SET_DULL , 1.0F, 0, 2, 1|2 |8 |32 , 225, 220, 255, 0, "Lithium" , "Lithium" , 0, 0, 454, 0, false, false, 4, 1, 1, Dyes.dyeLightBlue , Element.Li , Arrays.asList(new TC_AspectStack(TC_Aspects.VITREUS, 1), new TC_AspectStack(TC_Aspects.POTENTIA, 2))); + public static Materials Lutetium = new Materials( 78, TextureSet.SET_SHINY , 1.0F, 0, 2, 1|2 |8 |32 , 188, 62, 199, 0, "Lutetium" , "Lutetium" , 0, 0, 1925, 1925, true, false, 4, 1, 1, Dyes._NULL , Element.Lu , Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.RADIO, 1))); + public static Materials Magic = new Materials(-128, TextureSet.SET_SHINY , 8.0F, 5120, 5, 1|2|4|8|16|32|64|128 , 100, 0, 200, 0, "Magic" , "Magic" , 5, 32, 5000, 0, false, false, 7, 1, 1, Dyes.dyePurple , Element.Ma , Collections.singletonList(new TC_AspectStack(TC_Aspects.PRAECANTATIO, 4))); + public static Materials Magnesium = new Materials( 18, TextureSet.SET_METALLIC , 1.0F, 0, 2, 1|2 |8 |32 , 255, 200, 200, 0, "Magnesium" , "Magnesium" , 0, 0, 923, 0, false, false, 3, 1, 1, Dyes.dyePink , Element.Mg , Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.SANO, 1))); + public static Materials Manganese = new Materials( 31, TextureSet.SET_DULL , 7.0F, 512, 2, 1|2 |8 |32|64 , 250, 250, 250, 0, "Manganese" , "Manganese" , 0, 0, 1519, 0, false, false, 3, 1, 1, Dyes.dyeWhite , Element.Mn , Collections.singletonList(new TC_AspectStack(TC_Aspects.METALLUM, 3))); + public static Materials Mercury = new Materials( 87, TextureSet.SET_SHINY , 1.0F, 0, 0, 16|32 , 255, 220, 220, 0, "Mercury" , "Mercury" , 5, 32, 234, 0, false, false, 3, 1, 1, Dyes.dyeLightGray , Element.Hg , Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 1), new TC_AspectStack(TC_Aspects.AQUA, 1), new TC_AspectStack(TC_Aspects.VENENUM, 1))); + public static Materials Molybdenum = new Materials( 48, TextureSet.SET_SHINY , 7.0F, 512, 2, 1|2 |8 |32|64 , 180, 180, 220, 0, "Molybdenum" , "Molybdenum" , 0, 0, 2896, 0, false, false, 1, 1, 1, Dyes.dyeBlue , Element.Mo , Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.INSTRUMENTUM, 1))); + public static Materials Neodymium = new Materials( 67, TextureSet.SET_METALLIC , 7.0F, 512, 2, 1|2 |8 |32|64|128 , 100, 100, 100, 0, "Neodymium" , "Neodymium" , 0, 0, 1297, 1297, true, false, 4, 1, 1, Dyes._NULL , Element.Nd , Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.MAGNETO, 2))); + public static Materials Neutronium = new Materials( 129, TextureSet.SET_DULL , 24.0F, 655360, 6, 1|2 |8 |32|64|128 , 250, 250, 250, 0, "Neutronium" , "Neutronium" , 0, 0, 10000, 10000, true, false, 20, 1, 1, Dyes.dyeWhite , Element.Nt , Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 4), new TC_AspectStack(TC_Aspects.VITREUS, 3), new TC_AspectStack(TC_Aspects.ALIENIS, 2))).disableAutoGeneratedBlastFurnaceRecipes().disableAutoGeneratedVacuumFreezerRecipe().setProcessingMaterialTierEU(TierEU.RECIPE_LuV); + public static Materials Nickel = new Materials( 34, TextureSet.SET_METALLIC , 6.0F, 64, 2, 1|2 |8 |32|64|128 , 200, 200, 250, 0, "Nickel" , "Nickel" , 0, 0, 1728, 0, false, false, 4, 1, 1, Dyes.dyeLightBlue , Element.Ni , Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.IGNIS, 1))); + public static Materials Niobium = new Materials( 47, TextureSet.SET_METALLIC , 1.0F, 0, 2, 1|2 |8 |32 , 190, 180, 200, 0, "Niobium" , "Niobium" , 0, 0, 2750, 2750, true, false, 5, 1, 1, Dyes._NULL , Element.Nb , Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.ELECTRUM, 1))); + public static Materials Nitrogen = new Materials( 12, TextureSet.SET_FLUID , 1.0F, 0, 2, 16|32 , 0, 150, 200, 240, "Nitrogen" , "Nitrogen" , 0, 0, 63, 0, false, true, 2, 1, 1, Dyes.dyeCyan , Element.N , Collections.singletonList(new TC_AspectStack(TC_Aspects.AER, 2))); + public static Materials Osmium = new Materials( 83, TextureSet.SET_METALLIC , 16.0F, 1280, 4, 1|2 |8 |32|64|128 , 50, 50, 255, 0, "Osmium" , "Osmium" , 0, 0, 3306, 4500, true, false, 10, 1, 1, Dyes.dyeBlue , Element.Os , Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.MACHINA, 1), new TC_AspectStack(TC_Aspects.NEBRISUM, 1))).disableAutoGeneratedBlastFurnaceRecipes().disableAutoGeneratedVacuumFreezerRecipe(); + public static Materials Oxygen = new Materials( 13, TextureSet.SET_FLUID , 1.0F, 0, 2, 16|32 , 0, 100, 200, 240, "Oxygen" , "Oxygen" , 0, 0, 54, 0, false, true, 1, 1, 1, Dyes.dyeWhite , Element.O , Collections.singletonList(new TC_AspectStack(TC_Aspects.AER, 1))); + public static Materials Palladium = new Materials( 52, TextureSet.SET_SHINY , 8.0F, 512, 4, 1|2 |8 |32|64|128 , 128, 128, 128, 0, "Palladium" , "Palladium" , 0, 0, 1828, 1828, true, false, 4, 1, 1, Dyes.dyeGray , Element.Pd , Collections.singletonList(new TC_AspectStack(TC_Aspects.METALLUM, 3))).disableAutoGeneratedVacuumFreezerRecipe(); + public static Materials Phosphorus = new Materials( 21, TextureSet.SET_DULL , 1.0F, 0, 2, 1 |32 , 255, 255, 0, 0, "Phosphorus" , "Phosphorus" , 0, 0, 317, 0, false, false, 2, 1, 1, Dyes.dyeYellow , Element.P , Arrays.asList(new TC_Aspects.TC_AspectStack(TC_Aspects.IGNIS, 2), new TC_AspectStack(TC_Aspects.POTENTIA, 1))); + public static Materials Platinum = new Materials( 85, TextureSet.SET_SHINY , 12.0F, 64, 4, 1|2 |8 |32|64|128 , 255, 255, 200, 0, "Platinum" , "Platinum" , 0, 0, 2041, 0, false, false, 6, 1, 1, Dyes.dyeOrange , Element.Pt , Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.NEBRISUM, 1))); + public static Materials Plutonium = new Materials( 100, TextureSet.SET_METALLIC , 6.0F, 512, 3, 1|2 |8 |32|64 , 240, 50, 50, 0, "Plutonium" , "Plutonium 239" , 0, 0, 912, 0, false, false, 6, 1, 1, Dyes.dyeLime , Element.Pu , Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.RADIO, 2))); + public static Materials Plutonium241 = new Materials( 101, TextureSet.SET_SHINY , 6.0F, 512, 3, 1|2 |8 |32|64 , 250, 70, 70, 0, "Plutonium241" , "Plutonium 241" , 0, 0, 912, 0, false, false, 6, 1, 1, Dyes.dyeLime , Element.Pu_241 , Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.RADIO, 3))); + public static Materials Potassium = new Materials( 25, TextureSet.SET_METALLIC , 1.0F, 0, 1, 1|2 |32 , 154, 172, 223, 0, "Potassium" , "Potassium" , 0, 0, 336, 0, false, false, 2, 1, 1, Dyes.dyeWhite , Element.K , Arrays.asList(new TC_AspectStack(TC_Aspects.VITREUS, 1), new TC_AspectStack(TC_Aspects.POTENTIA, 1))); + public static Materials Praseodymium = new Materials( 66, TextureSet.SET_DULL , 1.0F, 0, 2, 1|2 |8 |32 , 117, 214, 129, 0, "Praseodymium" , "Praseodymium" , 0, 0, 1208, 1208, true, false, 4, 1, 1, Dyes._NULL , Element.Pr , Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.RADIO, 1))); + public static Materials Promethium = new Materials( 68, TextureSet.SET_SHINY , 1.0F, 0, 2, 1|2 |8 |32 , 36, 181, 53, 0, "Promethium" , "Promethium" , 0, 0, 1315, 1315, true, false, 4, 1, 1, Dyes._NULL , Element.Pm , Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.RADIO, 1))); + public static Materials Radon = new Materials( 93, TextureSet.SET_FLUID , 1.0F, 0, 2, 16|32 , 255, 0, 255, 240, "Radon" , "Radon" , 0, 0, 202, 0, false, true, 5, 1, 1, Dyes.dyePurple , Element.Rn , Arrays.asList(new TC_AspectStack(TC_Aspects.AER, 1), new TC_AspectStack(TC_Aspects.RADIO, 1))); + public static Materials Rubidium = new Materials( 43, TextureSet.SET_METALLIC , 1.0F, 0, 2, 1|2 |8 |32 , 240, 30, 30, 0, "Rubidium" , "Rubidium" , 0, 0, 312, 0, false, false, 4, 1, 1, Dyes.dyeRed , Element.Rb , Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.VITREUS, 1))); + public static Materials Samarium = new Materials( 69, TextureSet.SET_METALLIC , 1.0F, 0, 2, 1|2 |8 |32 , 255, 255, 204, 0, "Samarium" , "Samarium" , 0, 0, 1345, 1345, true, false, 4, 1, 1, Dyes.dyeWhite , Element.Sm , Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.RADIO, 1), new TC_AspectStack(TC_Aspects.MAGNETO,10))); + public static Materials Scandium = new Materials( 27, TextureSet.SET_METALLIC , 1.0F, 0, 2, 1|2 |8 |32 , 204, 204, 204, 0, "Scandium" , "Scandium" , 0, 0, 1814, 1814, true, false, 2, 1, 1, Dyes.dyeYellow , Element.Sc , Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.RADIO, 1))); + public static Materials Silicon = new Materials( 20, TextureSet.SET_METALLIC , 1.0F, 0, 2, 1|2 |8 |32 , 60, 60, 80, 0, "Silicon" , "Raw Silicon" , 0, 0, 2273, 2273, true, false, 1, 1, 1, Dyes.dyeBlack , Element.Si , Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.TENEBRAE, 1))); + public static Materials Silver = new Materials( 54, TextureSet.SET_SHINY , 10.0F, 64, 2, 1|2 |8 |32|64|128 , 220, 220, 255, 0, "Silver" , "Silver" , 0, 0, 1234, 0, false, false, 3, 1, 1, Dyes.dyeLightGray , Element.Ag , Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.LUCRUM, 1))); + public static Materials Sodium = new Materials( 17, TextureSet.SET_METALLIC , 1.0F, 0, 2, 1 |16|32 , 0, 0, 150, 0, "Sodium" , "Sodium" , 0, 0, 370, 0, false, false, 1, 1, 1, Dyes.dyeBlue , Element.Na , Arrays.asList(new TC_AspectStack(TC_Aspects.VITREUS, 2), new TC_AspectStack(TC_Aspects.LUX, 1))); + public static Materials Strontium = new Materials( 44, TextureSet.SET_METALLIC , 1.0F, 0, 2, 1 |8 |32 , 200, 200, 200, 0, "Strontium" , "Strontium" , 0, 0, 1050, 0, false, false, 1, 1, 1, Dyes.dyeLightGray , Element.Sr , Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.STRONTIO, 1))); + public static Materials Sulfur = new Materials( 22, TextureSet.SET_DULL , 1.0F, 0, 2, 1 |8 |32 , 200, 200, 0, 0, "Sulfur" , "Sulfur" , 0, 0, 388, 0, false, false, 2, 1, 1, Dyes.dyeYellow , Element.S , Collections.singletonList(new TC_AspectStack(TC_Aspects.IGNIS, 1))); + public static Materials Tantalum = new Materials( 80, TextureSet.SET_SHINY , 6.0F, 2560, 3, 1|2 |8 |32 , 105, 183, 255, 0, "Tantalum" , "Tantalum" , 0, 0, 3290, 3290, true, false, 4, 1, 1, Dyes._NULL , Element.Ta , Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.VINCULUM, 1))).disableAutoGeneratedBlastFurnaceRecipes().disableAutoGeneratedVacuumFreezerRecipe(); + public static Materials Tellurium = new Materials( 59, TextureSet.SET_DULL , 1.0F, 0, 2, 1|2 |8 |32 , 206, 277, 86, 0, "Tellurium" , "Tellurium" , 0, 0, 722, 0, false, false, 4, 1, 1, Dyes.dyeGray , Element.Te , Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.RADIO, 1))); + public static Materials Terbium = new Materials( 72, TextureSet.SET_METALLIC , 1.0F, 0, 2, 1|2 |8 |32 , 255, 255, 255, 0, "Terbium" , "Terbium" , 0, 0, 1629, 1629, true, false, 4, 1, 1, Dyes._NULL , Element.Tb , Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.RADIO, 1))); + public static Materials Thorium = new Materials( 96, TextureSet.SET_SHINY , 6.0F, 512, 2, 1|2 |8 |32|64 , 0, 30, 0, 0, "Thorium" , "Thorium" , 0, 0, 2115, 0, false, false, 4, 1, 1, Dyes.dyeBlack , Element.Th , Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.RADIO, 1))); + public static Materials Thulium = new Materials( 76, TextureSet.SET_SHINY , 1.0F, 0, 2, 1|2 |8 |32 , 89, 107, 194, 0, "Thulium" , "Thulium" , 0, 0, 1818, 1818, true, false, 4, 1, 1, Dyes._NULL , Element.Tm , Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.RADIO, 1))); + public static Materials Tin = new Materials( 57, TextureSet.SET_DULL , 1.0F, 0, 3, 1|2 |8 |32 |128 , 220, 220, 220, 0, "Tin" , "Tin" , 0, 0, 505, 505, false, false, 3, 1, 1, Dyes.dyeWhite , Element.Sn , Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.VITREUS, 1))); + public static Materials Titanium = new Materials( 28, TextureSet.SET_METALLIC , 7.0F, 1600, 3, 1|2 |8 |32|64|128 , 220, 160, 240, 0, "Titanium" , "Titanium" , 0, 0, 1941, 1940, true, false, 5, 1, 1, Dyes.dyePurple , Element.Ti , Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.TUTAMEN, 1))).disableAutoGeneratedBlastFurnaceRecipes().disableAutoGeneratedVacuumFreezerRecipe(); + public static Materials Tritanium = new Materials( 329, TextureSet.SET_METALLIC , 20.0F,1435392, 6, 1|2 |8 |32|64 , 96, 0, 0, 0, "Tritanium" , "Tritanium" , 0, 0, 9900, 9900, true, false, 1, 1, 1, Dyes.dyeWhite , Element.Tn ,Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.ORDO, 2))).disableAutoGeneratedVacuumFreezerRecipe(); + public static Materials Tritium = new Materials( 3, TextureSet.SET_METALLIC , 1.0F, 0, 2, 16|32 , 255, 0, 0, 240, "Tritium" , "Tritium" , 0, 0, 14, 0, false, true, 10, 1, 1, Dyes.dyeRed , Element.T , Collections.singletonList(new TC_AspectStack(TC_Aspects.AQUA, 4))); + public static Materials Tungsten = new Materials( 81, TextureSet.SET_METALLIC , 7.0F, 2560, 3, 1|2 |8 |32|64|128 , 50, 50, 50, 0, "Tungsten" , "Tungsten" , 0, 0, 3695, 3000, true, false, 4, 1, 1, Dyes.dyeBlack , Element.W , Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 3), new TC_AspectStack(TC_Aspects.TUTAMEN, 1))).disableAutoGeneratedBlastFurnaceRecipes().disableAutoGeneratedVacuumFreezerRecipe(); + public static Materials Uranium = new Materials( 98, TextureSet.SET_METALLIC , 6.0F, 512, 3, 1|2 |8 |32|64 , 50, 240, 50, 0, "Uranium" , "Uranium 238" , 0, 0, 1405, 0, false, false, 4, 1, 1, Dyes.dyeGreen , Element.U , Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.RADIO, 1))); + public static Materials Uranium235 = new Materials( 97, TextureSet.SET_SHINY , 6.0F, 512, 3, 1|2 |8 |32|64 , 70, 250, 70, 0, "Uranium235" , "Uranium 235" , 0, 0, 1405, 0, false, false, 4, 1, 1, Dyes.dyeGreen , Element.U_235 , Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.RADIO, 2))); + public static Materials Vanadium = new Materials( 29, TextureSet.SET_METALLIC , 1.0F, 0, 2, 1|2 |8 |32 , 50, 50, 50, 0, "Vanadium" , "Vanadium" , 0, 0, 2183, 2183, true, false, 2, 1, 1, Dyes.dyeBlack , Element.V , Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.RADIO, 1))); + public static Materials Ytterbium = new Materials( 77, TextureSet.SET_SHINY , 1.0F, 0, 2, 1|2 |8 |32 , 44, 199, 80, 0, "Ytterbium" , "Ytterbium" , 0, 0, 1097, 1097, true, false, 4, 1, 1, Dyes._NULL , Element.Yb , Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.RADIO, 1))); + public static Materials Yttrium = new Materials( 45, TextureSet.SET_METALLIC , 1.0F, 0, 2, 1|2 |8 |32 , 220, 250, 220, 0, "Yttrium" , "Yttrium" , 0, 0, 1799, 1799, true, false, 4, 1, 1, Dyes._NULL , Element.Y , Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.RADIO, 1))); + public static Materials Zinc = new Materials( 36, TextureSet.SET_METALLIC , 1.0F, 0, 1, 1|2 |8 |32 , 250, 240, 240, 0, "Zinc" , "Zinc" , 0, 0, 692, 0, false, false, 2, 1, 1, Dyes.dyeWhite , Element.Zn , Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.SANO, 1))); + public static Materials Grade1PurifiedWater = new Materials( 554, TextureSet.SET_FLUID , 1.0F, 0, 2, 16 , 0, 0, 255, 0, "Grade1PurifiedWater" , "Grade 1 Purified Water" , 0, 0, 273, 0, false, true, 2, 1, 1, Dyes.dyeBlue , Collections.singletonList(new TC_AspectStack(TC_Aspects.AQUA, 1))).setHasCorrespondingFluid(true); + public static Materials Grade2PurifiedWater = new Materials( 555, TextureSet.SET_FLUID , 1.0F, 0, 2, 16 , 0, 0, 250, 0, "Grade2PurifiedWater" , "Grade 2 Purified Water" , 0, 0, 273, 0, false, true, 2, 1, 1, Dyes.dyeBlue , Collections.singletonList(new TC_AspectStack(TC_Aspects.AQUA, 1))).setHasCorrespondingFluid(true); + public static Materials Grade3PurifiedWater = new Materials( 556, TextureSet.SET_FLUID , 1.0F, 0, 2, 16 , 0, 0, 245, 0, "Grade3PurifiedWater" , "Grade 3 Purified Water" , 0, 0, 273, 0, false, true, 2, 1, 1, Dyes.dyeBlue , Collections.singletonList(new TC_AspectStack(TC_Aspects.AQUA, 1))).setHasCorrespondingFluid(true); + public static Materials Grade4PurifiedWater = new Materials( 557, TextureSet.SET_FLUID , 1.0F, 0, 2, 16 , 0, 0, 240, 0, "Grade4PurifiedWater" , "Grade 4 Purified Water" , 0, 0, 273, 0, false, true, 2, 1, 1, Dyes.dyeBlue , Collections.singletonList(new TC_AspectStack(TC_Aspects.AQUA, 1))).setHasCorrespondingFluid(true); + public static Materials Grade5PurifiedWater = new Materials( 558, TextureSet.SET_FLUID , 1.0F, 0, 2, 16 , 0, 0, 235, 0, "Grade5PurifiedWater" , "Grade 5 Purified Water" , 0, 0, 273, 0, false, true, 2, 1, 1, Dyes.dyeBlue , Collections.singletonList(new TC_AspectStack(TC_Aspects.AQUA, 1))).setHasCorrespondingFluid(true); + public static Materials Grade6PurifiedWater = new Materials( 559, TextureSet.SET_FLUID , 1.0F, 0, 2, 16 , 0, 0, 230, 0, "Grade6PurifiedWater" , "Grade 6 Purified Water" , 0, 0, 273, 0, false, true, 2, 1, 1, Dyes.dyeBlue , Collections.singletonList(new TC_AspectStack(TC_Aspects.AQUA, 1))).setHasCorrespondingFluid(true); + public static Materials Grade7PurifiedWater = new Materials( 560, TextureSet.SET_FLUID , 1.0F, 0, 2, 16 , 0, 0, 225, 0, "Grade7PurifiedWater" , "Grade 7 Purified Water" , 0, 0, 273, 0, false, true, 2, 1, 1, Dyes.dyeBlue , Collections.singletonList(new TC_AspectStack(TC_Aspects.AQUA, 1))).setHasCorrespondingFluid(true); + public static Materials Grade8PurifiedWater = new Materials( 561, TextureSet.SET_FLUID , 1.0F, 0, 2, 16 , 0, 0, 220, 0, "Grade8PurifiedWater" , "Grade 8 Purified Water" , 0, 0, 273, 0, false, true, 2, 1, 1, Dyes.dyeBlue , Collections.singletonList(new TC_AspectStack(TC_Aspects.AQUA, 1))).setHasCorrespondingFluid(true); + + //GT++ materials + + public static Materials Flerovium = new Materials( 984, TextureSet.SET_SHINY , 1.0F, 0, 0, 1|2 |8 |32|64|128 , 255,255, 255, 0, "Flerovium_GT5U" , "Flerovium" , 0, 0, 0, 0, false, false, 1 , 1, 1, Dyes.dyeWhite , Element.Fl , Arrays.asList(new TC_Aspects.TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_Aspects.TC_AspectStack(TC_Aspects.RADIO, 3))).disableAutoGeneratedBlastFurnaceRecipes(); + + /** + * The "Random Material" ones. + */ + public static Materials Organic = new Materials( -1, TextureSet.SET_LEAF , 1.0F, 0, 1, false, "Organic" , "Organic" ); + public static Materials AnyCopper = new Materials( -1, TextureSet.SET_SHINY , 1.0F, 0, 3, false, "AnyCopper" , "AnyCopper" ); + public static Materials AnyBronze = new Materials( -1, TextureSet.SET_SHINY , 1.0F, 0, 3, false, "AnyBronze" , "AnyBronze" ); + public static Materials AnyIron = new Materials( -1, TextureSet.SET_SHINY , 1.0F, 0, 3, false, "AnyIron" , "AnyIron" ); + public static Materials AnyRubber = new Materials( -1, TextureSet.SET_SHINY , 1.0F, 0, 3, false, "AnyRubber" , "AnyRubber" ); + public static Materials AnySyntheticRubber = new Materials( -1, TextureSet.SET_SHINY , 1.0F, 0, 3, false, "AnySyntheticRubber" , "AnySyntheticRubber" ); + public static Materials Crystal = new Materials( -1, TextureSet.SET_SHINY , 1.0F, 0, 3, false, "Crystal" , "Crystal" ); + public static Materials Quartz = new Materials( -1, TextureSet.SET_QUARTZ , 1.0F, 0, 2, false, "Quartz" , "Quartz" ); + public static Materials Metal = new Materials( -1, TextureSet.SET_METALLIC , 1.0F, 0, 2, false, "Metal" , "Metal" ); + public static Materials Unknown = new Materials( -1, TextureSet.SET_DULL , 1.0F, 0, 2, false, "Unknown" , "Unknown" ); + public static Materials Cobblestone = new Materials( -1, TextureSet.SET_DULL , 1.0F, 0, 1, false, "Cobblestone" , "Cobblestone" ); + public static Materials BrickNether = new Materials( -1, TextureSet.SET_DULL , 1.0F, 0, 1, false, "BrickNether" , "BrickNether" ); + + /** + * The "I don't care" Section, everything I don't want to do anything with right now, is right here. Just to make the Material Finder shut up about them. + * But I do see potential uses in some of these Materials. + */ + public static Materials Serpentine = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 2, 1|2 |8 , 255, 255, 255, 0, "Serpentine" , "Serpentine" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes._NULL ); + public static Materials Flux = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 2, 1 , 255, 255, 255, 0, "Flux" , "Flux" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes._NULL ); + public static Materials OsmiumTetroxide = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 2, 1 , 255, 255, 255, 0, "OsmiumTetroxide" , "Osmium Tetroxide" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes._NULL ); + public static Materials RubberTreeSap = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 2, 0 , 255, 255, 255, 0, "RubberTreeSap" , "Rubber Tree Sap" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes._NULL ); + public static Materials PhasedIron = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 2, 1|2 , 255, 255, 255, 0, "PhasedIron" , "Phased Iron" , 0, 0, 3300, 3300, true, false, 3, 1, 1, Dyes._NULL ); + public static Materials PhasedGold = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 2, 1|2 , 255, 255, 255, 0, "PhasedGold" , "Phased Gold" , 0, 0, -1, 1800, true, false, 3, 1, 1, Dyes._NULL ); + public static Materials HeeEndium = new Materials( 770, TextureSet.SET_DULL , 16.0F, 1024, 4, 1|2 |8 |64|128 , 165, 220, 250, 0, "HeeEndium" , "Endium" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes.dyeLightBlue ); + public static Materials Teslatite = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 2, 1 , 60, 180, 200, 0, "Teslatite" , "Teslatite" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes._NULL ); + public static Materials Fluix = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 2, 1 |4 , 255, 255, 255, 0, "Fluix" , "Fluix" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes._NULL ); + public static Materials DarkThaumium = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 2, 1|2 , 255, 255, 255, 0, "DarkThaumium" , "Dark Thaumium" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes._NULL ); + public static Materials Alfium = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 2, 1 , 255, 255, 255, 0, "Alfium" , "Alfium" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes._NULL ); + public static Materials Mutation = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 2, 1 , 255, 255, 255, 0, "Mutation" , "Mutation" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes._NULL ); + public static Materials Aquamarine = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 2, 1 |4 , 255, 255, 255, 0, "Aquamarine" , "Aquamarine" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes._NULL ); + public static Materials Ender = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 2, 1 , 255, 255, 255, 0, "Ender" , "Ender" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes._NULL ); + public static Materials SodiumPeroxide = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 2, 1 , 255, 255, 255, 0, "SodiumPeroxide" , "Sodium Peroxide" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes._NULL ); + public static Materials IridiumSodiumOxide = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 2, 1 , 255, 255, 255, 0, "IridiumSodiumOxide" , "Iridium Sodium Oxide" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes._NULL ); + public static Materials PlatinumGroupSludge = new Materials( 241, TextureSet.SET_POWDER , 1.0F, 0, 2, 1 , 0, 30, 0, 0, "PlatinumGroupSludge" , "Platinum Group Sludge" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes._NULL ); + public static Materials Draconium = new Materials( 975, TextureSet.SET_SHINY , 20.0F, 32768, 7, 1|2| 8| 32|64|128 , 122, 68, 176, 0, "Draconium" , "Draconium" , 0, 0, 5000, 7200, true, false, 3, 1, 1, Dyes.dyePink ).disableAutoGeneratedBlastFurnaceRecipes().disableAutoGeneratedVacuumFreezerRecipe().setHasCorrespondingPlasma(true); + public static Materials DraconiumAwakened = new Materials( 976, TextureSet.SET_SHINY , 40.0F, 65536, 9, 1|2| 8| 32|64|128 , 244, 78, 0, 0, "DraconiumAwakened" , "Awakened Draconium" , 0, 0, 9900, 9900, true, false, 3, 1, 1, Dyes.dyeOrange ).disableAutoGeneratedBlastFurnaceRecipes().disableAutoGeneratedVacuumFreezerRecipe().setHasCorrespondingPlasma(true); + public static Materials PurpleAlloy = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 2, 0 , 100, 180, 255, 0, "PurpleAlloy" , "Purple Alloy" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes._NULL ); + public static Materials InfusedTeslatite = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 2, 0 , 100, 180, 255, 0, "InfusedTeslatite" , "Infused Teslatite" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes._NULL ); + + /** + * Unknown Material Components. Dead End Section. + */ + public static Materials Adamantium = new Materials( 319, TextureSet.SET_SHINY , 32.0F, 8192, 10, 1|2 |8 |64|128 , 255, 255, 255, 0, "Adamantium" , "Adamantium" , 0, 0, 7200, 7200, true, false, 1, 1, 1, Dyes.dyeLightGray ).setTurbineMultipliers(1, 5, 1).disableAutoGeneratedVacuumFreezerRecipe(); + public static Materials Adamite = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 3, 1 |8 , 255, 255, 255, 0, "Adamite" , "Adamite" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeLightGray ); + public static Materials Adluorite = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 2, 1|2 |8 |64|128 , 255, 255, 255, 0, "Adluorite" , "Adluorite" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeLightBlue ); + public static Materials Agate = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 2, 1 , 255, 255, 255, 0, "Agate" , "Agate" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes._NULL ); + public static Materials Alduorite = new Materials( 485, TextureSet.SET_SHINY , 32.0F, 8192, 1, 1|2 |8 |64|128 , 159, 180, 180, 0, "Alduorite" , "Alduorite" , 0, 0, 6600, 6600, true, false, 1, 1, 1, Dyes._NULL ).disableAutoGeneratedBlastFurnaceRecipes().setTurbineMultipliers(6, 1, 1); + public static Materials Amber = new Materials( 514, TextureSet.SET_RUBY , 4.0F, 128, 2, 1 |4|8 |64 , 255, 128, 0, 127, "Amber" , "Amber" , 5, 3, -1, 0, false, true, 1, 1, 1, Dyes.dyeOrange ,1, Arrays.asList(new MaterialStack(Carbon, 10), new MaterialStack(Hydrogen, 10), new MaterialStack(Oxygen, 16)), Arrays.asList(new TC_AspectStack(TC_Aspects.VINCULUM, 2), new TC_AspectStack(TC_Aspects.VITREUS, 1))); + public static Materials Ammonium = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 2, 1 , 255, 255, 255, 0, "Ammonium" , "Ammonium" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes._NULL ); + public static Materials Amordrine = new Materials( -1, TextureSet.SET_NONE , 6.0F, 64, 2, 1|2 |8 |64 , 255, 255, 255, 0, "Amordrine" , "Amordrine" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes._NULL ); + public static Materials Andesite = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 2, 1 |8 , 255, 255, 255, 0, "Andesite" , "Andesite" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes._NULL ); + public static Materials Angmallen = new Materials( 958, TextureSet.SET_METALLIC , 10.0F, 128, 2, 1|2 |8 |64 , 215, 225, 138, 0, "Angmallen" , "Angmallen" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes._NULL ); + public static Materials Ardite = new Materials( 382, TextureSet.SET_METALLIC , 18.0F, 1024, 4, 1|2 |8 |32|64|128 , 250, 129, 0, 0, "Ardite" , "Ardite" , 0, 0, 1600, 1600, true, false, 1, 1, 1, Dyes.dyeRed ).disableAutoGeneratedBlastFurnaceRecipes().setHasCorrespondingPlasma(true); + public static Materials Aredrite = new Materials( -1, TextureSet.SET_NONE , 6.0F, 64, 2, 1|2 |8 |64 , 255, 0, 0, 0, "Aredrite" , "Aredrite" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeYellow ); + public static Materials Atlarus = new Materials( 965, TextureSet.SET_METALLIC , 6.0F, 64, 2, 1|2 |8 |64 , 255, 255, 255, 0, "Atlarus" , "Atlarus" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes._NULL ); + public static Materials Bitumen = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 2, 1 |8 , 255, 255, 255, 0, "Bitumen" , "Bitumen" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes._NULL ); + public static Materials Black = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 2, 0 , 0, 0, 0, 0, "Black" , "Black" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes.dyeBlack ); + public static Materials Blizz = new Materials( 851, TextureSet.SET_SHINY , 1.0F, 0, 2, 1 , 220, 233, 255, 0, "Blizz" , "Blizz" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes._NULL ); + public static Materials Blueschist = new Materials( 852, TextureSet.SET_DULL , 1.0F, 0, 2, 1 , 255, 255, 255, 0, "Blueschist" , "Blueschist" , 0, 0, -1, 0, false, false, 0, 1, 1, Dyes.dyeLightBlue ); + public static Materials Bluestone = new Materials( 813, TextureSet.SET_DULL , 1.0F, 0, 2, 1 , 255, 255, 255, 0, "Bluestone" , "Bluestone" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeBlue ); + public static Materials Bloodstone = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 2, 1 , 255, 255, 255, 0, "Bloodstone" , "Bloodstone" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes.dyeRed ); + public static Materials Blutonium = new Materials( -1, TextureSet.SET_SHINY , 1.0F, 0, 2, 1|2 |8 , 0, 0, 255, 0, "Blutonium" , "Blutonium" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes.dyeBlue ); + public static Materials Carmot = new Materials( 962, TextureSet.SET_METALLIC , 16.0F, 128, 1, 1|2 |8 |64 , 217, 205, 140, 0, "Carmot" , "Carmot" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes._NULL ); + public static Materials Celenegil = new Materials( 964, TextureSet.SET_METALLIC , 10.0F, 4096, 2, 1|2 |8 |64 , 148, 204, 72, 0, "Celenegil" , "Celenegil" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes._NULL ); + public static Materials CertusQuartz = new Materials( 516, TextureSet.SET_QUARTZ , 5.0F, 32, 1, 1 |4|8 |64 , 210, 210, 230, 0, "CertusQuartz" , "Certus Quartz" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes.dyeLightGray , Arrays.asList(new TC_AspectStack(TC_Aspects.POTENTIA, 1), new TC_AspectStack(TC_Aspects.VITREUS, 1))); + public static Materials CertusQuartzCharged = new Materials( 517, TextureSet.SET_QUARTZ , 5.0F, 32, 1, 8 , 221, 221, 236, 0, "ChargedCertusQuartz" , "Charged Certus Quartz" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes.dyeLightGray , Arrays.asList(new TC_AspectStack(TC_Aspects.POTENTIA, 1), new TC_AspectStack(TC_Aspects.VITREUS, 1), new TC_AspectStack(TC_Aspects.ELECTRUM, 1))); + public static Materials Ceruclase = new Materials( 952, TextureSet.SET_METALLIC , 32.0F, 1280, 2, 1|2 |8 |64|128 , 140, 189, 208, 0, "Ceruclase" , "Ceruclase" , 0, 0, 6600, 6600, true, false, 1, 1, 1, Dyes.dyeBlue ).disableAutoGeneratedBlastFurnaceRecipes().setTurbineMultipliers(1, 22, 1); + public static Materials Citrine = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 2, 1 , 255, 255, 255, 0, "Citrine" , "Citrine" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes._NULL ); + public static Materials CobaltHexahydrate = new Materials( 853, TextureSet.SET_METALLIC , 1.0F, 0, 2, 1 |16 , 80, 80, 250, 0, "CobaltHexahydrate" , "Cobalt Hexahydrate" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeBlue ); + public static Materials ConstructionFoam = new Materials( 854, TextureSet.SET_DULL , 1.0F, 0, 2, 1 |16 |64|128 , 128, 128, 128, 0, "ConstructionFoam" , "Construction Foam" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeGray ); + public static Materials Chert = new Materials( 857, TextureSet.SET_DULL , 1.0F, 0, 2, 1 , 255, 255, 255, 0, "Chert" , "Chert" , 0, 0, -1, 0, false, false, 0, 1, 1, Dyes._NULL ); + public static Materials Chimerite = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 2, 1 , 255, 255, 255, 0, "Chimerite" , "Chimerite" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes._NULL ); + public static Materials Coral = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 1, 1 , 255, 128, 255, 0, "Coral" , "Coral" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes._NULL ); + public static Materials CrudeOil = new Materials( 858, TextureSet.SET_DULL , 1.0F, 0, 2, 1 , 10, 10, 10, 0, "CrudeOil" , "Crude Oil" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeBlack ); + public static Materials Chrysocolla = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 2, 1 , 255, 255, 255, 0, "Chrysocolla" , "Chrysocolla" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes._NULL ); + public static Materials CrystalFlux = new Materials( -1, TextureSet.SET_QUARTZ , 1.0F, 0, 3, 1 |4 , 100, 50, 100, 0, "CrystalFlux" , "Flux Crystal" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes._NULL ); + public static Materials Cyanite = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 2, 1 , 255, 255, 255, 0, "Cyanite" , "Cyanite" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes.dyeCyan ); + public static Materials Dacite = new Materials( 859, TextureSet.SET_DULL , 1.0F, 0, 2, 1 , 255, 255, 255, 0, "Dacite" , "Dacite" , 0, 0, -1, 0, false, false, 0, 1, 1, Dyes.dyeLightGray ); + public static Materials DarkIron = new Materials( 342, TextureSet.SET_DULL , 7.0F, 384, 3, 1|2 |8 |64 , 55, 40, 60, 0, "DarkIron" , "Dark Iron" , 0, 0, -1, 0, false, false, 5, 1, 1, Dyes.dyePurple ); + public static Materials DarkStone = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 2, 1 , 255, 255, 255, 0, "DarkStone" , "Dark Stone" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes.dyeBlack ); + public static Materials Demonite = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 2, 1 , 255, 255, 255, 0, "Demonite" , "Demonite" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes.dyeRed ); + public static Materials Desh = new Materials( 884, TextureSet.SET_DULL , 20.0F, 1280, 4, 1|2 |8 |32|64|128 , 40, 40, 40, 0, "Desh" , "Desh" , 0, 0, 2500, 2500, true, false, 1, 1, 1, Dyes.dyeBlack ,Element.De, Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.ALIENIS, 1))).disableAutoGeneratedBlastFurnaceRecipes().disableAutoGeneratedVacuumFreezerRecipe(); + public static Materials Desichalkos = new Materials( -1, TextureSet.SET_NONE , 6.0F, 1280, 3, 1|2 |8 |64 , 255, 255, 255, 0, "Desichalkos" , "Desichalkos" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes._NULL ); + public static Materials Dilithium = new Materials( 515, TextureSet.SET_DIAMOND , 1.0F, 0, 1, 1 |4|8|16 , 255, 250, 250, 127, "Dilithium" , "Dilithium" , 0, 0, -1, 0, false, true, 1, 1, 1, Dyes.dyeWhite ); + public static Materials Draconic = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 2, 1 , 255, 255, 255, 0, "Draconic" , "Draconic" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeRed ); + public static Materials Drulloy = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 2, 1|16 , 255, 255, 255, 0, "Drulloy" , "Drulloy" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeRed ); + public static Materials Duranium = new Materials( 328, TextureSet.SET_METALLIC , 32.0F, 40960, 11, 1|2 |64 , 255, 255, 255, 0, "Duranium" , "Duranium" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeLightGray ).setTurbineMultipliers(16, 16, 1); + public static Materials Eclogite = new Materials( 860, TextureSet.SET_DULL , 1.0F, 0, 2, 1 , 255, 255, 255, 0, "Eclogite" , "Eclogite" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes._NULL ); + public static Materials ElectrumFlux = new Materials( 320, TextureSet.SET_SHINY , 16.0F, 512, 3, 1|2 |8 |64|128 , 255, 255, 120, 0, "ElectrumFlux" , "Fluxed Electrum" , 0, 0, 9000, 9000, true, false, 1, 1, 1, Dyes.dyeYellow ).disableAutoGeneratedBlastFurnaceRecipes().disableAutoGeneratedVacuumFreezerRecipe(); + public static Materials Emery = new Materials( 861, TextureSet.SET_DULL , 1.0F, 0, 2, 1 |8 , 255, 255, 255, 0, "Emery" , "Emery" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes._NULL ); + public static Materials EnderiumBase = new Materials( 380, TextureSet.SET_DULL , 16.0F, 768, 4, 1|2 |64|128 , 72, 119, 153, 0, "EnderiumBase" , "Enderium Base" , 0, 0, 3600, 3600, true, false, 1, 1, 1, Dyes.dyeGreen , 1, Arrays.asList(new MaterialStack(Tin, 2), new MaterialStack(Silver, 1), new MaterialStack(Platinum, 1)), Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.ALIENIS, 1))).disableAutoGeneratedBlastFurnaceRecipes().disableAutoGeneratedVacuumFreezerRecipe(); + public static Materials Energized = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 2, 0 , 255, 255, 255, 0, "Energized" , "Energized" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes._NULL ); + public static Materials Epidote = new Materials( 862, TextureSet.SET_DULL , 1.0F, 0, 2, 1 , 255, 255, 255, 0, "Epidote" , "Epidote" , 0, 0, -1, 0, false, false, 0, 1, 1, Dyes._NULL ); + public static Materials Eximite = new Materials( 959, TextureSet.SET_METALLIC , 5.0F, 2560, 3, 1|2 |8 |64 , 124, 90, 150, 0, "Eximite" , "Eximite" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes._NULL ); + public static Materials FierySteel = new Materials( 346, TextureSet.SET_FIERY , 8.0F, 256, 3, 1|2 |64|128 , 64, 0, 0, 0, "FierySteel" , "Fiery Steel" , 5, 2048, 1811, 1800, true, false, 1, 1, 1, Dyes.dyeRed , Arrays.asList(new TC_AspectStack(TC_Aspects.PRAECANTATIO, 3), new TC_AspectStack(TC_Aspects.IGNIS, 3), new TC_AspectStack(TC_Aspects.CORPUS, 3))).disableAutoGeneratedBlastFurnaceRecipes(); + public static Materials Firestone = new Materials( 347, TextureSet.SET_QUARTZ , 6.0F, 1280, 3, 1 |4|8 |64 , 200, 20, 0, 0, "Firestone" , "Firestone" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes.dyeRed ); + public static Materials Fluorite = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 2, 1 |8 , 255, 255, 255, 0, "Fluorite" , "Fluorite" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes.dyeGreen ); + public static Materials FoolsRuby = new Materials( 512, TextureSet.SET_RUBY , 1.0F, 0, 2, 1 |4|8 , 255, 100, 100, 127, "FoolsRuby" , "Spinel" , 0, 0, -1, 0, false, true, 3, 1, 1, Dyes.dyeRed , 1, Arrays.asList(new MaterialStack(Magnesium, 1), new MaterialStack(Aluminium, 2), new MaterialStack(Oxygen, 4)), Arrays.asList(new TC_AspectStack(TC_Aspects.LUCRUM, 2), new TC_AspectStack(TC_Aspects.VITREUS, 2))); + public static Materials Force = new Materials( 521, TextureSet.SET_DIAMOND , 10.0F, 128, 3, 1|2|4|8 |64|128 , 255, 255, 0, 0, "Force" , "Force" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes.dyeYellow , Collections.singletonList(new TC_AspectStack(TC_Aspects.POTENTIA, 5))); + public static Materials Forcicium = new Materials( 518, TextureSet.SET_DIAMOND , 1.0F, 0, 1, 1 |4|8|16 , 50, 50, 70, 0, "Forcicium" , "Forcicium" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes.dyeGreen , Collections.singletonList(new TC_AspectStack(TC_Aspects.POTENTIA, 2))); + public static Materials Forcillium = new Materials( 519, TextureSet.SET_DIAMOND , 1.0F, 0, 1, 1 |4|8|16 , 50, 50, 70, 0, "Forcillium" , "Forcillium" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes.dyeGreen , Collections.singletonList(new TC_AspectStack(TC_Aspects.POTENTIA, 2))); + public static Materials Gabbro = new Materials( 863, TextureSet.SET_DULL , 1.0F, 0, 1, 1 , 255, 255, 255, 0, "Gabbro" , "Gabbro" , 0, 0, -1, 0, false, false, 0, 1, 1, Dyes._NULL ); + public static Materials Glowstone = new Materials( 811, TextureSet.SET_SHINY , 1.0F, 0, 1, 1 |16 , 255, 255, 0, 0, "Glowstone" , "Glowstone" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeYellow , Arrays.asList(new TC_AspectStack(TC_Aspects.LUX, 2), new TC_AspectStack(TC_Aspects.SENSUS, 1))); + public static Materials Gneiss = new Materials( 864, TextureSet.SET_DULL , 1.0F, 0, 1, 1 , 255, 255, 255, 0, "Gneiss" , "Gneiss" , 0, 0, -1, 0, false, false, 0, 1, 1, Dyes._NULL ); + public static Materials Graphite = new Materials( 865, TextureSet.SET_DULL , 5.0F, 32, 2, 1 |8|16 |64 , 128, 128, 128, 0, "Graphite" , "Graphite" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes.dyeGray , 0, Collections.singletonList(new MaterialStack(Carbon, 1)), Arrays.asList(new TC_AspectStack(TC_Aspects.VITREUS, 2), new TC_AspectStack(TC_Aspects.IGNIS, 1))); + public static Materials Graphene = new Materials( 819, TextureSet.SET_DULL , 6.0F, 32, 1, 1 |64 , 128, 128, 128, 0, "Graphene" , "Graphene" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes.dyeGray , 0, Collections.singletonList(new MaterialStack(Carbon, 1)), Arrays.asList(new TC_AspectStack(TC_Aspects.VITREUS, 2), new TC_AspectStack(TC_Aspects.ELECTRUM, 1))); + public static Materials Greenschist = new Materials( 866, TextureSet.SET_DULL , 1.0F, 0, 1, 1 , 255, 255, 255, 0, "Greenschist" , "Green Schist" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeGreen ); + public static Materials Greenstone = new Materials( 867, TextureSet.SET_DULL , 1.0F, 0, 1, 1 , 255, 255, 255, 0, "Greenstone" , "Greenstone" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeGreen ); + public static Materials Greywacke = new Materials( 897, TextureSet.SET_DULL , 1.0F, 0, 1, 1 , 255, 255, 255, 0, "Greywacke" , "Greywacke" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeGray ); + public static Materials Haderoth = new Materials( 963, TextureSet.SET_METALLIC , 10.0F, 3200, 3, 1|2 |8 |64 , 119, 52, 30, 0, "Haderoth" , "Haderoth" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes._NULL ); + public static Materials Hematite = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 2, 1|2 |8 , 255, 255, 255, 0, "Hematite" , "Hematite" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes._NULL ); + public static Materials Hepatizon = new Materials( 957, TextureSet.SET_METALLIC , 12.0F, 128, 2, 1|2 |8 |64 , 117, 94, 117, 0, "Hepatizon" , "Hepatizon" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes._NULL ); + public static Materials HSLA = new Materials( 322, TextureSet.SET_METALLIC , 6.0F, 500, 3, 1|2 |64|128 , 128, 128, 128, 0, "HSLA" , "HSLA Steel" , 0, 0, 1811, 1000, true, false, 3, 1, 1, Dyes._NULL , Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 1), new TC_AspectStack(TC_Aspects.ORDO, 1))); + public static Materials Ignatius = new Materials( 950, TextureSet.SET_METALLIC , 12.0F, 512, 2, 1|2 , 255, 169, 83, 0, "Ignatius" , "Ignatius" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes._NULL ); + public static Materials Infernal = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 2, 0 , 255, 255, 255, 0, "Infernal" , "Infernal" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes._NULL ); + public static Materials Infuscolium = new Materials( 490, TextureSet.SET_METALLIC , 6.0F, 64, 2, 1|2 |8 |64 , 146, 33, 86, 0, "Infuscolium" , "Infuscolium" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes._NULL ); + public static Materials InfusedGold = new Materials( 323, TextureSet.SET_SHINY , 12.0F, 64, 3, 1|2 |8 |64|128 , 255, 200, 60, 0, "InfusedGold" , "Infused Gold" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes.dyeYellow ); + public static Materials InfusedAir = new Materials( 540, TextureSet.SET_SHARDS , 8.0F, 64, 3, 1 |4|8 |64|128 , 255, 255, 0, 0, "InfusedAir" , "Aer" , 5, 160, -1, 0, false, true, 3, 1, 1, Dyes.dyeYellow , Arrays.asList(new TC_AspectStack(TC_Aspects.PRAECANTATIO, 1), new TC_AspectStack(TC_Aspects.AER, 2))); + public static Materials InfusedFire = new Materials( 541, TextureSet.SET_SHARDS , 8.0F, 64, 3, 1 |4|8 |64|128 , 255, 0, 0, 0, "InfusedFire" , "Ignis" , 5, 320, -1, 0, false, true, 3, 1, 1, Dyes.dyeRed , Arrays.asList(new TC_AspectStack(TC_Aspects.PRAECANTATIO, 1), new TC_AspectStack(TC_Aspects.IGNIS, 2))); + public static Materials InfusedEarth = new Materials( 542, TextureSet.SET_SHARDS , 8.0F, 256, 3, 1 |4|8 |64|128 , 0, 255, 0, 0, "InfusedEarth" , "Terra" , 5, 160, -1, 0, false, true, 3, 1, 1, Dyes.dyeGreen , Arrays.asList(new TC_AspectStack(TC_Aspects.PRAECANTATIO, 1), new TC_AspectStack(TC_Aspects.TERRA, 2))); + public static Materials InfusedWater = new Materials( 543, TextureSet.SET_SHARDS , 8.0F, 64, 3, 1 |4|8 |64|128 , 0, 0, 255, 0, "InfusedWater" , "Aqua" , 5, 160, -1, 0, false, true, 3, 1, 1, Dyes.dyeBlue , Arrays.asList(new TC_AspectStack(TC_Aspects.PRAECANTATIO, 1), new TC_AspectStack(TC_Aspects.AQUA, 2))); + public static Materials InfusedEntropy = new Materials( 544, TextureSet.SET_SHARDS , 32.0F, 64, 4, 1 |4|8 |64|128 , 62, 62, 62, 0, "InfusedEntropy" , "Perditio" , 5, 320, -1, 0, false, true, 3, 1, 1, Dyes.dyeBlack , Arrays.asList(new TC_AspectStack(TC_Aspects.PRAECANTATIO, 1), new TC_AspectStack(TC_Aspects.PERDITIO, 2))); + public static Materials InfusedOrder = new Materials( 545, TextureSet.SET_SHARDS , 8.0F, 64, 3, 1 |4|8 |64|128 , 252, 252, 252, 0, "InfusedOrder" , "Ordo" , 5, 240, -1, 0, false, true, 3, 1, 1, Dyes.dyeWhite , Arrays.asList(new TC_AspectStack(TC_Aspects.PRAECANTATIO, 1), new TC_AspectStack(TC_Aspects.ORDO, 2))); + public static Materials InfusedVis = new Materials( -1, TextureSet.SET_SHARDS , 8.0F, 64, 3, 1 |4|8 |64|128 , 255, 0, 255, 0, "InfusedVis" , "Auram" , 5, 240, -1, 0, false, true, 3, 1, 1, Dyes.dyePurple , Arrays.asList(new TC_AspectStack(TC_Aspects.PRAECANTATIO, 1), new TC_AspectStack(TC_Aspects.AURAM, 2))); + public static Materials InfusedDull = new Materials( -1, TextureSet.SET_SHARDS , 32.0F, 64, 3, 1 |4|8 |64|128 , 100, 100, 100, 0, "InfusedDull" , "Vacuus" , 5, 160, -1, 0, false, true, 3, 1, 1, Dyes.dyeLightGray , Arrays.asList(new TC_AspectStack(TC_Aspects.PRAECANTATIO, 1), new TC_AspectStack(TC_Aspects.VACUOS, 2))); + public static Materials Inolashite = new Materials( 954, TextureSet.SET_NONE , 8.0F, 2304, 3, 1|2 |8 |64 , 148, 216, 187, 0, "Inolashite" , "Inolashite" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeGreen ); + public static Materials Invisium = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 2, 1 , 255, 255, 255, 0, "Invisium" , "Invisium" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes._NULL ); + public static Materials Jade = new Materials( 537, TextureSet.SET_SHINY , 1.0F, 16, 2, 1 |4|8 |64 , 0, 100, 0, 0, "Jade" , "Jade" , 0, 0, -1, 0, false, false, 5, 1, 1, Dyes.dyeGreen ,0, Arrays.asList(new MaterialStack(Sodium, 1), new MaterialStack(Aluminium, 1), new MaterialStack(Silicon, 2), new MaterialStack(Oxygen, 6)), Arrays.asList(new TC_AspectStack(TC_Aspects.LUCRUM, 6), new TC_AspectStack(TC_Aspects.VITREUS, 3))); + public static Materials Kalendrite = new Materials( 953, TextureSet.SET_METALLIC , 5.0F, 2560, 3, 1|2 , 170, 91, 189, 0, "Kalendrite" , "Kalendrite" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes._NULL ); + public static Materials Komatiite = new Materials( 869, TextureSet.SET_DULL , 1.0F, 0, 1, 1 , 255, 255, 255, 0, "Komatiite" , "Komatiite" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeYellow ); + public static Materials Lava = new Materials( 700, TextureSet.SET_FLUID , 1.0F, 0, 1, 16 , 255, 64, 0, 0, "Lava" , "Lava" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeOrange ); + public static Materials Lemurite = new Materials( 486, TextureSet.SET_METALLIC , 1.0F, 0, 2, 1 , 219, 219, 219, 0, "Lemurite" , "Lemurite" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes._NULL ); + public static Materials Limestone = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 1, 1 , 255, 255, 255, 0, "Limestone" , "Limestone" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes._NULL ); + public static Materials Magma = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 1, 0 , 255, 64, 0, 0, "Magma" , "Magma" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeOrange ); + public static Materials Mawsitsit = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 1, 1 , 255, 255, 255, 0, "Mawsitsit" , "Mawsitsit" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes._NULL ); + public static Materials Mercassium = new Materials( -1, TextureSet.SET_NONE , 6.0F, 64, 1, 1|2 |8 |64 , 255, 255, 255, 0, "Mercassium" , "Mercassium" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes._NULL ); + public static Materials MeteoricIron = new Materials( 340, TextureSet.SET_METALLIC , 6.0F, 384, 3, 1|2 |8 |32|64 , 100, 50, 80, 0, "MeteoricIron" , "Meteoric Iron" , 0, 0, 1811, 1000, true, false, 1, 1, 1, Dyes.dyeGray ,Element.SpFe, Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.MAGNETO, 1))); + public static Materials MeteoricSteel = new Materials( 341, TextureSet.SET_METALLIC , 6.0F, 768, 4, 1|2 |64 , 50, 25, 40, 0, "MeteoricSteel" , "Meteoric Steel" , 0, 0, 1811, 1000, true, false, 4, 51, 50, Dyes.dyeGray , 1, Arrays.asList(new MaterialStack(MeteoricIron, 50), new MaterialStack(Carbon, 1)), Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.MAGNETO, 1), new TC_AspectStack(TC_Aspects.ORDO, 1))); + public static Materials Meteorite = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 1, 1 |8 , 80, 35, 60, 0, "Meteorite" , "Meteorite" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyePurple ); + public static Materials Meutoite = new Materials( 487, TextureSet.SET_METALLIC , 1.0F, 0, 1, 1 |8 , 95, 82, 105, 0, "Meutoite" , "Meutoite" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes._NULL ); + public static Materials Migmatite = new Materials( 872, TextureSet.SET_DULL , 1.0F, 0, 1, 1 , 255, 255, 255, 0, "Migmatite" , "Migmatite" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes._NULL ); + public static Materials Mimichite = new Materials( -1, TextureSet.SET_GEM_VERTICAL , 1.0F, 0, 1, 1 |4|8 , 255, 255, 255, 0, "Mimichite" , "Mimichite" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes._NULL ); + public static Materials Moonstone = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 1, 1 |8 , 255, 255, 255, 0, "Moonstone" , "Moonstone" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes.dyeWhite , Arrays.asList(new TC_AspectStack(TC_Aspects.VITREUS, 1), new TC_AspectStack(TC_Aspects.ALIENIS, 1))); + public static Materials Naquadah = new Materials( 324, TextureSet.SET_METALLIC , 6.0F, 1280, 4, 1|2 |8 |32|64|128 , 50, 50, 50, 0, "Naquadah" , "Naquadah" , 0, 0, 5400, 5400, true, false, 10, 1, 1, Dyes.dyeBlack , Element.Nq, Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 3), new TC_AspectStack(TC_Aspects.RADIO, 1), new TC_AspectStack(TC_Aspects.NEBRISUM, 1))).disableAutoGeneratedBlastFurnaceRecipes().disableAutoGeneratedVacuumFreezerRecipe(); + public static Materials NaquadahAlloy = new Materials( 325, TextureSet.SET_METALLIC , 8.0F, 5120, 5, 1|2 |64|128 , 40, 40, 40, 0, "NaquadahAlloy" , "Naquadah Alloy" , 0, 0, 7200, 7200, true, false, 10, 1, 1, Dyes.dyeBlack , Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 4), new TC_AspectStack(TC_Aspects.NEBRISUM, 1))).disableAutoGeneratedBlastFurnaceRecipes().disableAutoGeneratedVacuumFreezerRecipe(); + public static Materials NaquadahEnriched = new Materials( 326, TextureSet.SET_METALLIC , 6.0F, 1280, 4, 1|2 |8 |64 , 50, 50, 50, 0, "NaquadahEnriched" , "Enriched Naquadah" , 0, 0, 4500, 4500, true, false, 15, 1, 1, Dyes.dyeBlack , Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 3), new TC_AspectStack(TC_Aspects.RADIO, 2), new TC_AspectStack(TC_Aspects.NEBRISUM, 2))).disableAutoGeneratedBlastFurnaceRecipes().disableAutoGeneratedVacuumFreezerRecipe(); + public static Materials Naquadria = new Materials( 327, TextureSet.SET_SHINY , 1.0F, 512, 4, 1|2 |8 |64 , 30, 30, 30, 0, "Naquadria" , "Naquadria" , 0, 0, 9000, 9000, true, false, 20, 1, 1, Dyes.dyeBlack , Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 4), new TC_AspectStack(TC_Aspects.RADIO, 3), new TC_AspectStack(TC_Aspects.NEBRISUM, 3))).disableAutoGeneratedBlastFurnaceRecipes().disableAutoGeneratedVacuumFreezerRecipe(); + public static Materials Nether = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 1, 0 , 255, 255, 255, 0, "Nether" , "Nether" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes._NULL ); + public static Materials NetherBrick = new Materials( 814, TextureSet.SET_DULL , 1.0F, 0, 1, 1 , 100, 0, 0, 0, "NetherBrick" , "Nether Brick" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeRed , Collections.singletonList(new TC_AspectStack(TC_Aspects.IGNIS, 1))); + public static Materials NetherQuartz = new Materials( 522, TextureSet.SET_QUARTZ , 1.0F, 32, 1, 1 |4|8 |64 , 230, 210, 210, 0, "NetherQuartz" , "Nether Quartz" , 0, 0, -1, 0, false, false, 2, 1, 1, Dyes.dyeWhite , Arrays.asList(new TC_AspectStack(TC_Aspects.POTENTIA, 1), new TC_AspectStack(TC_Aspects.VITREUS, 1))); + public static Materials NetherStar = new Materials( 506, TextureSet.SET_NETHERSTAR , 6.0F, 5120, 4, 1| 4|8 |64 , 255, 255, 255, 0, "NetherStar" , "Nether Star" , 5, 50000, -1, 0, false, false, 15, 1, 1, Dyes.dyeWhite ); + public static Materials ObsidianFlux = new Materials( -1, TextureSet.SET_DULL , 1.0F, 0, 1, 1|2 , 80, 50, 100, 0, "ObsidianFlux" , "Fluxed Obsidian" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyePurple ); + public static Materials Oilsands = new Materials( 878, TextureSet.SET_NONE , 1.0F, 0, 1, 1 |8 , 10, 10, 10, 0, "Oilsands" , "Oilsands" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes._NULL ); + public static Materials Onyx = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 1, 1 , 255, 255, 255, 0, "Onyx" , "Onyx" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes._NULL ); + public static Materials Orichalcum = new Materials( 966, TextureSet.SET_METALLIC , 32.0F, 20480, 1, 1|2 |8 |64|128 , 84, 122, 56, 0, "Orichalcum" , "Orichalcum" , 0, 0, 6000, 6000, true, false, 1, 1, 1, Dyes._NULL ).disableAutoGeneratedBlastFurnaceRecipes().setTurbineMultipliers(1, 6, 1); + public static Materials Osmonium = new Materials( -1, TextureSet.SET_NONE , 6.0F, 64, 1, 1|2 |8 |64 , 255, 255, 255, 0, "Osmonium" , "Osmonium" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes.dyeBlue ); + public static Materials Oureclase = new Materials( 961, TextureSet.SET_METALLIC , 6.0F, 1920, 3, 1|2 |8 |64 , 183, 98, 21, 0, "Oureclase" , "Oureclase" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes._NULL ); + public static Materials Painite = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 1, 0 , 255, 255, 255, 0, "Painite" , "Painite" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes._NULL ); + public static Materials Peanutwood = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 1, 0 , 255, 255, 255, 0, "Peanutwood" , "Peanut Wood" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes._NULL ); + public static Materials Petroleum = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 1, 1 |8 , 255, 255, 255, 0, "Petroleum" , "Petroleum" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes._NULL ); + public static Materials Pewter = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 1, 0 , 255, 255, 255, 0, "Pewter" , "Pewter" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes._NULL ); + public static Materials Phoenixite = new Materials( -1, TextureSet.SET_NONE , 6.0F, 64, 1, 1|2 |8 |64 , 255, 255, 255, 0, "Phoenixite" , "Phoenixite" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes._NULL ); + public static Materials Prometheum = new Materials( 960, TextureSet.SET_METALLIC , 8.0F, 512, 1, 1|2 |8 |64 , 90, 129, 86, 0, "Prometheum" , "Prometheum" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes._NULL ); + public static Materials Quartzite = new Materials( 523, TextureSet.SET_QUARTZ , 1.0F, 0, 1, 1 |4|8 , 210, 230, 210, 0, "Quartzite" , "Quartzite" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes.dyeWhite ); + public static Materials Randomite = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 1, 1 |8 , 255, 255, 255, 0, "Randomite" , "Randomite" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes._NULL ); + public static Materials Rhyolite = new Materials( 875, TextureSet.SET_DULL , 1.0F, 0, 1, 1 , 255, 255, 255, 0, "Rhyolite" , "Rhyolite" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes._NULL ); + public static Materials Rubracium = new Materials( 488, TextureSet.SET_METALLIC , 1.0F, 128, 1, 1|2 |8 |64|128 , 151, 45, 45, 0, "Rubracium" , "Rubracium" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeRed ); + public static Materials Sand = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 1, 0 , 255, 255, 255, 0, "Sand" , "Sand" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeYellow ); + public static Materials Sanguinite = new Materials( 955, TextureSet.SET_METALLIC , 3.0F, 4480, 4, 1|2 |8 , 185, 0, 0, 0, "Sanguinite" , "Sanguinite" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes._NULL ); + public static Materials Siltstone = new Materials( 876, TextureSet.SET_DULL , 1.0F, 0, 1, 1 , 255, 255, 255, 0, "Siltstone" , "Siltstone" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes._NULL ); + public static Materials Sunstone = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 1, 1 |8 , 255, 255, 255, 0, "Sunstone" , "Sunstone" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes.dyeYellow , Arrays.asList(new TC_AspectStack(TC_Aspects.VITREUS, 1), new TC_AspectStack(TC_Aspects.ALIENIS, 1))); + public static Materials Tar = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 1, 0 , 10, 10, 10, 0, "Tar" , "Tar" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeBlack ); + public static Materials Tartarite = new Materials( 956, TextureSet.SET_METALLIC , 32.0F, 20480, 3, 1|2 |8 , 255, 118, 60, 0, "Tartarite" , "Tartarite" , 0, 0, 10400, 10400, true, false, 1, 1, 1, Dyes._NULL ).disableAutoGeneratedBlastFurnaceRecipes().setTurbineMultipliers(1120, 1120, 1); + public static Materials UUAmplifier = new Materials( 721, TextureSet.SET_FLUID , 1.0F, 0, 1, 16 , 96, 0, 128, 0, "UUAmplifier" , "UU-Amplifier" , 0, 0, -1, 0, false, false, 10, 1, 1, Dyes.dyePink ); + public static Materials UUMatter = new Materials( 703, TextureSet.SET_FLUID , 1.0F, 0, 1, 16 , 128, 0, 196, 0, "UUMatter" , "UU-Matter" , 0, 0, -1, 0, false, false, 10, 1, 1, Dyes.dyePink ); + public static Materials Void = new Materials( 970, TextureSet.SET_METALLIC , 32.0F, 512, 4, 1|2 |64|128 , 28, 6, 57, 0, "Void" , "Void" , 5, 1500, -1, 0, false, true, 5, 2, 1, Dyes.dyeBlack , Collections.singletonList(new TC_AspectStack(TC_Aspects.VACUOS, 1))); + public static Materials Voidstone = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 1, 0 , 255, 255, 255, 200, "Voidstone" , "Voidstone" , 0, 0, -1, 0, false, true, 1, 1, 1, Dyes._NULL , Arrays.asList(new TC_Aspects.TC_AspectStack(TC_Aspects.VITREUS, 1), new TC_AspectStack(TC_Aspects.VACUOS, 1))); + public static Materials Vulcanite = new Materials( 489, TextureSet.SET_METALLIC , 32.0F, 20480, 2, 1|2 |8 |64|128 , 255, 132, 72, 0, "Vulcanite" , "Vulcanite" , 0, 0, 8400, 8400, true, false, 1, 1, 1, Dyes._NULL ).disableAutoGeneratedBlastFurnaceRecipes().setTurbineMultipliers(40, 1, 1); + public static Materials Vyroxeres = new Materials( 951, TextureSet.SET_METALLIC , 32.0F, 7680, 1, 1|2 |8 |64 , 85, 224, 1, 0, "Vyroxeres" , "Vyroxeres" , 0, 0, 5400, 5400, true, false, 1, 1, 1, Dyes._NULL ).disableAutoGeneratedBlastFurnaceRecipes().setTurbineMultipliers(1, 3, 1); + public static Materials Yellorium = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 2, 1|2 , 255, 255, 255, 0, "Yellorium" , "Yellorium" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes.dyeYellow ); + public static Materials Zectium = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 2, 1|2 |8 , 255, 255, 255, 0, "Zectium" , "Zectium" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes.dyeBlack ); + + /** + * Circuitry, Batteries and other Technical things + */ + public static Materials Primitive = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 0, 0 , 255, 255, 255, 0, "Primitive" , "Primitive" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeLightGray , Collections.singletonList(new TC_AspectStack(TC_Aspects.MACHINA, 1))); + public static Materials Basic = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 0, 0 , 255, 255, 255, 0, "Basic" , "Basic" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeLightGray , Collections.singletonList(new TC_AspectStack(TC_Aspects.MACHINA, 2))); + public static Materials Good = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 0, 0 , 255, 255, 255, 0, "Good" , "Good" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeLightGray , Collections.singletonList(new TC_AspectStack(TC_Aspects.MACHINA, 3))); + public static Materials Advanced = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 0, 0 , 255, 255, 255, 0, "Advanced" , "Advanced" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeLightGray , Collections.singletonList(new TC_AspectStack(TC_Aspects.MACHINA, 4))); + public static Materials Data = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 0, 0 , 255, 255, 255, 0, "Data" , "Data" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeLightGray , Collections.singletonList(new TC_AspectStack(TC_Aspects.MACHINA, 5))); + public static Materials Elite = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 0, 0 , 255, 255, 255, 0, "Elite" , "Elite" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeLightGray , Collections.singletonList(new TC_AspectStack(TC_Aspects.MACHINA, 6))); + public static Materials Master = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 0, 0 , 255, 255, 255, 0, "Master" , "Master" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeLightGray , Collections.singletonList(new TC_AspectStack(TC_Aspects.MACHINA, 7))); + public static Materials Ultimate = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 0, 0 , 255, 255, 255, 0, "Ultimate" , "Ultimate" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeLightGray , Collections.singletonList(new TC_AspectStack(TC_Aspects.MACHINA, 8))); + public static Materials Infinite = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 0, 0 , 255, 255, 255, 0, "Infinite" , "Infinite" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeLightGray , Collections.singletonList(new TC_AspectStack(TC_Aspects.ELECTRUM, 9))); + public static Materials Bio = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 0, 0 , 255, 255, 255, 0, "Bio" , "Bio" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeLightGray , Collections.singletonList(new TC_AspectStack(TC_Aspects.ELECTRUM, 10))); + public static Materials Nano = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 0, 0 , 255, 255, 255, 0, "Nano" , "Bio" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeLightGray , Collections.singletonList(new TC_AspectStack(TC_Aspects.ELECTRUM, 11))); + public static Materials Piko = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 0, 0 , 255, 255, 255, 0, "Piko" , "Bio" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeLightGray , Collections.singletonList(new TC_AspectStack(TC_Aspects.ELECTRUM, 12))); + public static Materials Quantum = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 0, 0 , 255, 255, 255, 0, "Quantum" , "Bio" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeLightGray , Collections.singletonList(new TC_AspectStack(TC_Aspects.ELECTRUM, 13))); + public static Materials Optical = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 0, 0 , 255, 255, 255, 0, "Optical" , "Optical" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeLightGray , Collections.singletonList(new TC_AspectStack(TC_Aspects.ELECTRUM, 13))); + public static Materials Exotic = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 0, 0 , 255, 255, 255, 0, "Exotic" , "Exotic" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeLightGray , Collections.singletonList(new TC_AspectStack(TC_Aspects.ELECTRUM, 14))); + public static Materials Cosmic = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 0, 0 , 255, 255, 255, 0, "Cosmic" , "Cosmic" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeLightGray , Collections.singletonList(new TC_AspectStack(TC_Aspects.ELECTRUM, 15))); + public static Materials Transcendent = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 0, 0 , 255, 255, 255, 0, "Transcendent" , "Transcendent" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeLightGray , Collections.singletonList(new TC_AspectStack(TC_Aspects.ELECTRUM, 16))); + public static Materials Resistor = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 0, 0 , 255, 255, 255, 0, "Resistor" , "Resistor" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeLightGray , Collections.singletonList(new TC_AspectStack(TC_Aspects.ELECTRUM, 1))); + public static Materials Diode = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 0, 0 , 255, 255, 255, 0, "Diode" , "Diode" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeLightGray , Collections.singletonList(new TC_AspectStack(TC_Aspects.ELECTRUM, 1))); + public static Materials Transistor = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 0, 0 , 255, 255, 255, 0, "Transistor" , "Transistor" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeLightGray , Collections.singletonList(new TC_AspectStack(TC_Aspects.ELECTRUM, 1))); + public static Materials Capacitor = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 0, 0 , 255, 255, 255, 0, "Capacitor" , "Capacitor" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeLightGray , Collections.singletonList(new TC_AspectStack(TC_Aspects.ELECTRUM, 1))); + public static Materials Inductor = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 0, 0 , 255, 255, 255, 0, "Inductor" , "Inductor" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeLightGray , Collections.singletonList(new TC_AspectStack(TC_Aspects.ELECTRUM, 1))); + + /** + * Not possible to determine exact Components + */ + public static Materials Antimatter = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 0, 0 , 255, 255, 255, 0, "Antimatter" , "Antimatter" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyePink , Arrays.asList(new TC_AspectStack(TC_Aspects.POTENTIA, 9), new TC_AspectStack(TC_Aspects.PERFODIO, 8))); + public static Materials AdvancedGlue = new MaterialBuilder(567, TextureSet.SET_FLUID , "Advanced Glue").setName("AdvancedGlue").addCell().addFluid().setRGB(255, 255, 185).setColor(Dyes.dyeYellow).setAspects(Collections.singletonList(new TC_AspectStack(TC_Aspects.LIMUS, 5))).constructMaterial(); + public static Materials BioFuel = new Materials( 705, TextureSet.SET_FLUID , 1.0F, 0, 0, 16 , 255, 128, 0, 0, "BioFuel" , "Biofuel" , 0, 6, -1, 0, false, false, 1, 1, 1, Dyes.dyeOrange ); + public static Materials Biomass = new Materials( 704, TextureSet.SET_FLUID , 1.0F, 0, 0, 16 , 0, 255, 0, 0, "Biomass" , "Forestry Biomass" , 3, 8, -1, 0, false, false, 1, 1, 1, Dyes.dyeGreen ); + public static Materials CharcoalByproducts = new MaterialBuilder(675, TextureSet.SET_FLUID , "Charcoal Byproducts").addCell().setRGB(120, 68, 33).setColor(Dyes.dyeBrown).constructMaterial(); + public static Materials Cheese = new Materials( 894, TextureSet.SET_FINE , 1.0F, 0, 0, 1 |8 , 255, 255, 0, 0, "Cheese" , "Cheese" , 0, 0, 320, 0, false, false, 1, 1, 1, Dyes.dyeYellow ); + public static Materials Chili = new Materials( 895, TextureSet.SET_FINE , 1.0F, 0, 0, 1 , 200, 0, 0, 0, "Chili" , "Chili" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeRed ); + public static Materials Chocolate = new Materials( 886, TextureSet.SET_FINE , 1.0F, 0, 0, 1 , 190, 95, 0, 0, "Chocolate" , "Chocolate" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeBrown ); + public static Materials Cluster = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 0, 0 , 255, 255, 255, 127, "Cluster" , "Cluster" , 0, 0, -1, 0, false, true, 1, 1, 1, Dyes.dyeWhite ); + public static Materials CoalFuel = new Materials( 710, TextureSet.SET_FLUID , 1.0F, 0, 0, 16 , 50, 50, 70, 0, "CoalFuel" , "Coalfuel" , 0, 16, -1, 0, false, false, 1, 1, 1, Dyes.dyeBlack ); + public static Materials Cocoa = new Materials( 887, TextureSet.SET_FINE , 1.0F, 0, 0, 1 , 190, 95, 0, 0, "Cocoa" , "Cocoa" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeBrown ); + public static Materials Coffee = new Materials( 888, TextureSet.SET_FINE , 1.0F, 0, 0, 1 , 150, 75, 0, 0, "Coffee" , "Coffee" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeBrown ); + public static Materials Creosote = new Materials( 712, TextureSet.SET_FLUID , 1.0F, 0, 0, 16 , 128, 64, 0, 0, "Creosote" , "Creosote" , 3, 8, -1, 0, false, false, 1, 1, 1, Dyes.dyeBrown ); + public static Materials Ethanol = new Materials( 706, TextureSet.SET_FLUID , 1.0F, 0, 0, 16 , 255, 128, 0, 0, "Ethanol" , "Ethanol" , 0, 192, -1, 0, false, false, 1, 1, 1, Dyes.dyeOrange , 1, Arrays.asList(new MaterialStack(Carbon, 2), new MaterialStack(Hydrogen, 6), new MaterialStack(Oxygen, 1)), Arrays.asList(new TC_AspectStack(TC_Aspects.VENENUM, 1), new TC_AspectStack(TC_Aspects.AQUA, 1))); + public static Materials FishOil = new Materials( 711, TextureSet.SET_FLUID , 1.0F, 0, 0, 16 , 255, 196, 0, 0, "FishOil" , "Fish Oil" , 3, 2, -1, 0, false, false, 1, 1, 1, Dyes.dyeYellow , Collections.singletonList(new TC_AspectStack(TC_Aspects.CORPUS, 2))); + public static Materials FermentedBiomass = new MaterialBuilder(691, TextureSet.SET_FLUID , "Fermented Biomass").addCell().addFluid().setRGB(68, 85, 0).setColor(Dyes.dyeBrown).constructMaterial(); + public static Materials Fuel = new Materials( 708, TextureSet.SET_FLUID , 1.0F, 0, 0, 16 , 255, 255, 0, 0, "Fuel" , "Diesel" , 0, 480, -1, 0, false, false, 1, 1, 1, Dyes.dyeYellow ); + public static Materials Glue = new Materials( 726, TextureSet.SET_FLUID , 1.0F, 0, 0, 16 , 200, 196, 0, 0, "Glue" , "Refined Glue" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeOrange , Collections.singletonList(new TC_AspectStack(TC_Aspects.LIMUS, 2))); + public static Materials Gunpowder = new Materials( 800, TextureSet.SET_DULL , 1.0F, 0, 0, 1 , 128, 128, 128, 0, "Gunpowder" , "Gunpowder" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeGray , Arrays.asList(new TC_AspectStack(TC_Aspects.PERDITIO, 3), new TC_AspectStack(TC_Aspects.IGNIS, 4))); + public static Materials FryingOilHot = new Materials( 727, TextureSet.SET_FLUID , 1.0F, 0, 0, 16 , 200, 196, 0, 0, "FryingOilHot" , "Hot Frying Oil" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeOrange , Arrays.asList(new TC_AspectStack(TC_Aspects.AQUA, 1), new TC_AspectStack(TC_Aspects.IGNIS, 1))); + public static Materials Honey = new Materials( 725, TextureSet.SET_FLUID , 1.0F, 0, 0, 16 , 210, 200, 0, 0, "Honey" , "Honey" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeYellow ); + public static Materials Leather = new Materials( -1, TextureSet.SET_ROUGH , 1.0F, 0, 0, 1 , 150, 150, 80, 127, "Leather" , "Leather" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeOrange ); + public static Materials Lubricant = new Materials( 724, TextureSet.SET_FLUID , 1.0F, 0, 0, 16 , 255, 196, 0, 0, "Lubricant" , "Lubricant" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeOrange , Arrays.asList(new TC_AspectStack(TC_Aspects.AQUA, 2), new TC_AspectStack(TC_Aspects.MACHINA, 1))); + public static Materials McGuffium239 = new Materials( 999, TextureSet.SET_FLUID , 1.0F, 0, 0, 16 , 200, 50, 150, 0, "McGuffium239" , "Mc Guffium 239" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyePink , Arrays.asList(new TC_AspectStack(TC_Aspects.ALIENIS, 8), new TC_AspectStack(TC_Aspects.PERMUTATIO, 8), new TC_AspectStack(TC_Aspects.SPIRITUS, 8), new TC_AspectStack(TC_Aspects.AURAM, 8), new TC_AspectStack(TC_Aspects.VITIUM, 8), new TC_AspectStack(TC_Aspects.RADIO, 8), new TC_AspectStack(TC_Aspects.MAGNETO, 8), new TC_AspectStack(TC_Aspects.ELECTRUM, 8), new TC_AspectStack(TC_Aspects.NEBRISUM, 8), new TC_AspectStack(TC_Aspects.STRONTIO, 8))); + public static Materials MeatRaw = new Materials( 892, TextureSet.SET_FINE , 1.0F, 0, 0, 1 , 255, 100, 100, 0, "MeatRaw" , "Raw Meat" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyePink ); + public static Materials MeatCooked = new Materials( 893, TextureSet.SET_FINE , 1.0F, 0, 0, 1 , 150, 60, 20, 0, "MeatCooked" , "Cooked Meat" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyePink ); + public static Materials Milk = new Materials( 885, TextureSet.SET_FINE , 1.0F, 0, 0, 1 |16 , 254, 254, 254, 0, "Milk" , "Milk" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeWhite , Collections.singletonList(new TC_AspectStack(TC_Aspects.SANO, 2))); + public static Materials Mud = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 0, 0 , 255, 255, 255, 0, "Mud" , "Mud" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeBrown ); + public static Materials Oil = new Materials( 707, TextureSet.SET_FLUID , 1.0F, 0, 0, 16 , 10, 10, 10, 0, "Oil" , "Oil" , 3, 20, -1, 0, false, false, 1, 1, 1, Dyes.dyeBlack ); + public static Materials Paper = new Materials( 879, TextureSet.SET_PAPER , 1.0F, 0, 0, 1 , 250, 250, 250, 0, "Paper" , "Paper" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeWhite , Collections.singletonList(new TC_AspectStack(TC_Aspects.COGNITIO, 1))); + public static Materials Peat = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 0, 0 , 255, 255, 255, 0, "Peat" , "Peat" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeBrown , Arrays.asList(new TC_AspectStack(TC_Aspects.POTENTIA, 2), new TC_AspectStack(TC_Aspects.IGNIS, 2))); + public static Materials RareEarth = new Materials( 891, TextureSet.SET_FINE , 1.0F, 0, 0, 1 , 128, 128, 100, 0, "RareEarth" , "Rare Earth" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeGray , Arrays.asList(new TC_AspectStack(TC_Aspects.VITREUS, 1), new TC_AspectStack(TC_Aspects.LUCRUM, 1))); + public static Materials Red = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 0, 0 , 255, 0, 0, 0, "Red" , "Red" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeRed ); + public static Materials Reinforced = new Materials( 383, TextureSet.SET_METALLIC , 7.0F, 480, 4, 1|2 |64|128 , 105, 141, 165, 0, "Reinforced" , "Reinforced" , 0, 0, -1, 1700, true, false, 1, 1, 1, Dyes.dyeBlue ).disableAutoGeneratedBlastFurnaceRecipes(); + public static Materials SeedOil = new Materials( 713, TextureSet.SET_FLUID , 1.0F, 0, 0, 16 , 196, 255, 0, 0, "SeedOil" , "Seed Oil" , 3, 2, -1, 0, false, false, 1, 1, 1, Dyes.dyeLime , Collections.singletonList(new TC_AspectStack(TC_Aspects.GRANUM, 2))); + public static Materials SeedOilHemp = new Materials( 722, TextureSet.SET_FLUID , 1.0F, 0, 0, 16 , 196, 255, 0, 0, "SeedOilHemp" , "Hemp Seed Oil" , 3, 2, -1, 0, false, false, 1, 1, 1, Dyes.dyeLime , Collections.singletonList(new TC_AspectStack(TC_Aspects.GRANUM, 2))); + public static Materials SeedOilLin = new Materials( 723, TextureSet.SET_FLUID , 1.0F, 0, 0, 16 , 196, 255, 0, 0, "SeedOilLin" , "Lin Seed Oil" , 3, 2, -1, 0, false, false, 1, 1, 1, Dyes.dyeLime , Collections.singletonList(new TC_AspectStack(TC_Aspects.GRANUM, 2))); + public static Materials Stone = new Materials( 299, TextureSet.SET_ROUGH , 4.0F, 32, 1, 1 |64|128 , 205, 205, 205, 0, "Stone" , "Stone" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeLightGray , Collections.singletonList(new TC_AspectStack(TC_Aspects.TERRA, 1))); + public static Materials TNT = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 0, 0 , 255, 255, 255, 0, "TNT" , "TNT" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeRed , Arrays.asList(new TC_AspectStack(TC_Aspects.PERDITIO, 7), new TC_AspectStack(TC_Aspects.IGNIS, 4))); + public static Materials Unstable = new Materials( 396, TextureSet.SET_SHINY , 1.0F, 0, 4, 1 , 220, 220, 220, 127, "Unstable" , "Unstable" , 0, 0, -1, 0, false, true, 1, 1, 1, Dyes.dyeWhite , Collections.singletonList(new TC_AspectStack(TC_Aspects.PERDITIO, 4))); + public static Materials Unstableingot = new Materials( -1, TextureSet.SET_NONE , 1.0F, 0, 4, 0 , 255, 255, 255, 127, "Unstableingot" , "Unstable" , 0, 0, -1, 0, false, true, 1, 1, 1, Dyes.dyeWhite , Collections.singletonList(new TC_AspectStack(TC_Aspects.PERDITIO, 4))); + public static Materials Vinegar = new MaterialBuilder(690, TextureSet.SET_FLUID , "Vinegar").setColor(Dyes.dyeBrown).constructMaterial(); + public static Materials Wheat = new Materials( 881, TextureSet.SET_POWDER , 1.0F, 0, 0, 1 , 255, 255, 196, 0, "Wheat" , "Wheat" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeYellow , Collections.singletonList(new TC_AspectStack(TC_Aspects.MESSIS, 2))); + public static Materials WoodGas = new MaterialBuilder(660, TextureSet.SET_FLUID , "Wood Gas").addCell().addGas().setRGB(222, 205, 135).setColor(Dyes.dyeBrown).setFuelType(MaterialBuilder.GAS).setFuelPower(24).constructMaterial(); + public static Materials WoodTar = new MaterialBuilder(662, TextureSet.SET_FLUID , "Wood Tar").addCell().addFluid().setRGB(40, 23, 11).setColor(Dyes.dyeBrown).constructMaterial(); + public static Materials WoodVinegar = new MaterialBuilder(661, TextureSet.SET_FLUID , "Wood Vinegar").addCell().addFluid().setRGB(212, 85, 0).setColor(Dyes.dyeBrown).constructMaterial(); + public static Materials WeedEX9000 = new MaterialBuilder(242, TextureSet.SET_FLUID , "Weed-EX 9000").addFluid().setRGB(64, 224, 86).setColor(Dyes.dyeGreen).constructMaterial(); + + /** + * TODO: This + */ + public static Materials AluminiumBrass = new Materials( -1, TextureSet.SET_METALLIC , 6.0F, 64, 2, 1|2 |64 , 255, 255, 255, 0, "AluminiumBrass" , "Aluminium Brass" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeYellow ); + public static Materials Osmiridium = new Materials( 317, TextureSet.SET_METALLIC , 7.0F, 1600, 3, 1|2 |64|128 , 100, 100, 255, 0, "Osmiridium" , "Osmiridium" , 0, 0, 3500, 4500, true, false, 1, 1, 1, Dyes.dyeLightBlue , 1, Arrays.asList(new MaterialStack(Iridium, 3), new MaterialStack(Osmium, 1))).disableAutoGeneratedBlastFurnaceRecipes().disableAutoGeneratedVacuumFreezerRecipe(); + public static Materials Sunnarium = new Materials( 318, TextureSet.SET_SHINY , 1.0F, 0, 1, 1|2 |64|128 , 255, 255, 0, 0, "Sunnarium" , "Sunnarium" , 0, 0, 4200, 4200, true, false, 1, 1, 1, Dyes.dyeYellow ).disableAutoGeneratedBlastFurnaceRecipes(); + public static Materials Endstone = new Materials( 808, TextureSet.SET_DULL , 1.0F, 0, 1, 1 , 255, 255, 255, 0, "Endstone" , "Endstone" , 0, 0, -1, 0, false, false, 0, 1, 1, Dyes.dyeYellow ); + public static Materials Netherrack = new Materials( 807, TextureSet.SET_DULL , 1.0F, 0, 0, 1 , 200, 0, 0, 0, "Netherrack" , "Netherrack" , 0, 0, -1, 0, false, false, 0, 1, 1, Dyes.dyeRed ); + public static Materials SoulSand = new Materials( -1, TextureSet.SET_DULL , 1.0F, 0, 0, 1 , 255, 255, 255, 0, "SoulSand" , "Soulsand" , 0, 0, -1, 0, false, false, 0, 1, 1, Dyes.dyeBrown ); + /** + * First Degree Compounds + */ + public static Materials Methane = new Materials( 715, TextureSet.SET_FLUID , 1.0F, 0, 1, 16 , 255, 255, 255, 0, "Methane" , "Methane" , 1, 104, -1, 0, false, false, 3, 1, 1, Dyes.dyeMagenta , 1, Arrays.asList(new MaterialStack(Carbon, 1), new MaterialStack(Hydrogen, 4))); + public static Materials CarbonDioxide = new Materials( 497, TextureSet.SET_FLUID , 1.0F, 0, 2, 16|32 , 169, 208, 245, 240, "CarbonDioxide" , "Carbon Dioxide" , 0, 0, 25, 1, false, true, 1, 1, 1, Dyes.dyeLightBlue , 0, Arrays.asList(new MaterialStack(Carbon, 1), new MaterialStack(Oxygen, 2))).setHasCorrespondingGas(true); + public static Materials NobleGases = new Materials( 496, TextureSet.SET_FLUID , 1.0F, 0, 2, 16|32 , 169, 208, 245, 240, "NobleGases" , "Noble Gases" , 0, 0, 4, 0, false, true, 1, 1, 1, Dyes.dyeLightBlue , 2, Arrays.asList(new MaterialStack(CarbonDioxide,21),new MaterialStack(Helium, 9), new MaterialStack(Methane, 3), new MaterialStack(Deuterium, 1))).setHasCorrespondingGas(true); + public static Materials Air = new Materials( -1, TextureSet.SET_FLUID , 1.0F, 0, 2, 16|32 , 169, 208, 245, 240, "Air" , "Air" , 0, 0, -1, 0, false, true, 1, 1, 1, Dyes.dyeLightBlue , 0, Arrays.asList(new MaterialStack(Nitrogen, 40), new MaterialStack(Oxygen, 11), new MaterialStack(Argon, 1),new MaterialStack(NobleGases,1))); + public static Materials LiquidAir = new Materials( 495, TextureSet.SET_FLUID , 1.0F, 0, 2, 16|32 , 169, 208, 245, 240, "LiquidAir" , "Liquid Air" , 0, 0, 4, 0, false, true, 1, 1, 1, Dyes.dyeLightBlue , 2, Arrays.asList(new MaterialStack(Nitrogen, 40), new MaterialStack(Oxygen, 11), new MaterialStack(Argon, 1),new MaterialStack(NobleGases,1))); + public static Materials LiquidNitrogen = new Materials( 494, TextureSet.SET_FLUID , 1.0F, 0, 2, 16|32 , 169, 208, 245, 240, "LiquidNitrogen" , "Liquid Nitrogen" , 0, 0, 4, 0, false, true, 1, 1, 1, Dyes.dyeLightBlue , 1, Collections.singletonList(new MaterialStack(Nitrogen, 1))); + public static Materials LiquidOxygen = new Materials( 493, TextureSet.SET_FLUID , 1.0F, 0, 2, 16|32 , 169, 208, 245, 240, "LiquidOxygen" , "Liquid Oxygen" , 0, 0, 4, 0, false, true, 1, 1, 1, Dyes.dyeLightBlue , 1, Collections.singletonList(new MaterialStack(Oxygen, 1))); + public static Materials SiliconDioxide = new MaterialBuilder(837, TextureSet.SET_QUARTZ, "Silicon Dioxide").setToolSpeed(1.0F).setDurability(0).setToolQuality(1).addDustItems().setRGB(255, 255, 255).setColor(Dyes.dyeLightGray).setOreValue(1).setExtraData(0).setMaterialList(new MaterialStack(Silicon, 1), new MaterialStack(Oxygen, 2)).constructMaterial(); + public static Materials Jasper = new Materials( 511, TextureSet.SET_EMERALD , 1.0F, 0, 2, 1 |4|8 |64 , 200, 80, 80, 100, "Jasper" , "Jasper" , 0, 0, -1, 0, false, true, 3, 1, 1, Dyes.dyeRed , 1, Collections.singletonList(new MaterialStack(SiliconDioxide, 1)), Arrays.asList(new TC_AspectStack(TC_Aspects.LUCRUM, 4), new TC_AspectStack(TC_Aspects.VITREUS, 2))); + public static Materials Almandine = new Materials( 820, TextureSet.SET_ROUGH , 1.0F, 0, 1, 1 |8 , 255, 0, 0, 0, "Almandine" , "Almandine" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes.dyeRed , 0, Arrays.asList(new MaterialStack(Aluminium, 2), new MaterialStack(Iron, 3), new MaterialStack(Silicon, 3), new MaterialStack(Oxygen, 12))); + public static Materials Andradite = new Materials( 821, TextureSet.SET_ROUGH , 1.0F, 0, 1, 1 |8 , 150, 120, 0, 0, "Andradite" , "Andradite" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes.dyeYellow , 1, Arrays.asList(new MaterialStack(Calcium, 3), new MaterialStack(Iron, 2), new MaterialStack(Silicon, 3), new MaterialStack(Oxygen, 12))); + public static Materials AnnealedCopper = new Materials( 345, TextureSet.SET_SHINY , 1.0F, 0, 2, 1|2 |128 , 255, 120, 20, 0, "AnnealedCopper" , "Annealed Copper" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes.dyeOrange , 2, Collections.singletonList(new MaterialStack(Copper, 1))); + public static Materials Asbestos = new Materials( 946, TextureSet.SET_DULL , 1.0F, 0, 1, 1 |8 , 230, 230, 230, 0, "Asbestos" , "Asbestos" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeWhite , 1, Arrays.asList(new MaterialStack(Magnesium, 3), new MaterialStack(Silicon, 2), new MaterialStack(Hydrogen, 4), new MaterialStack(Oxygen, 9))); // Mg3Si2O5(OH)4 + public static Materials Ash = new Materials( 815, TextureSet.SET_DULL , 1.0F, 0, 1, 1 , 150, 150, 150, 0, "Ash" , "Ashes" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeLightGray , 2, Collections.singletonList(new MaterialStack(Carbon, 1)), Collections.singletonList(new TC_AspectStack(TC_Aspects.PERDITIO, 1))); + public static Materials BandedIron = new Materials( 917, TextureSet.SET_DULL , 1.0F, 0, 2, 1 |8 , 145, 90, 90, 0, "BandedIron" , "Banded Iron" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeBrown , 1, Arrays.asList(new MaterialStack(Iron, 2), new MaterialStack(Oxygen, 3))); + public static Materials BatteryAlloy = new Materials( 315, TextureSet.SET_DULL , 1.0F, 0, 1, 1|2 , 156, 124, 160, 0, "BatteryAlloy" , "Battery Alloy" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyePurple , 2, Arrays.asList(new MaterialStack(Lead, 4), new MaterialStack(Antimony, 1))); + public static Materials BlueTopaz = new Materials( 513, TextureSet.SET_GEM_HORIZONTAL , 7.0F, 256, 3, 1 |4|8 |64 , 0, 0, 255, 127, "BlueTopaz" , "Blue Topaz" , 0, 0, -1, 0, false, true, 3, 1, 1, Dyes.dyeBlue , 0, Arrays.asList(new MaterialStack(Aluminium, 2), new MaterialStack(Silicon, 1), new MaterialStack(Fluorine, 2), new MaterialStack(Hydrogen, 2), new MaterialStack(Oxygen, 6)), Arrays.asList(new TC_AspectStack(TC_Aspects.LUCRUM, 6), new TC_AspectStack(TC_Aspects.VITREUS, 4))); + public static Materials Bone = new Materials( 806, TextureSet.SET_DULL , 1.0F, 0, 1, 1 , 250, 250, 250, 0, "Bone" , "Bone" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeWhite , 0, Collections.singletonList(new MaterialStack(Calcium, 1)), Arrays.asList(new TC_AspectStack(TC_Aspects.MORTUUS, 2), new TC_AspectStack(TC_Aspects.CORPUS, 1))); + public static Materials Brass = new Materials( 301, TextureSet.SET_METALLIC , 7.0F, 96, 1, 1|2 |64|128 , 255, 180, 0, 0, "Brass" , "Brass" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeYellow , 2, Arrays.asList(new MaterialStack(Zinc, 1), new MaterialStack(Copper, 3)), Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.INSTRUMENTUM, 1))); + public static Materials Bronze = new Materials( 300, TextureSet.SET_METALLIC , 6.0F, 192, 2, 1|2 |64|128 , 255, 128, 0, 0, "Bronze" , "Bronze" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeOrange , 2, Arrays.asList(new MaterialStack(Tin, 1), new MaterialStack(Copper, 3)), Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.INSTRUMENTUM, 1))); + public static Materials BrownLimonite = new Materials( 930, TextureSet.SET_METALLIC , 1.0F, 0, 1, 1 |8 , 200, 100, 0, 0, "BrownLimonite" , "Brown Limonite" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeBrown , 2, Arrays.asList(new MaterialStack(Iron, 1), new MaterialStack(Hydrogen, 1), new MaterialStack(Oxygen, 2))); // FeO(OH) + public static Materials Calcite = new Materials( 823, TextureSet.SET_DULL , 1.0F, 0, 1, 1 |8 , 250, 230, 220, 0, "Calcite" , "Calcite" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeOrange , 1, Arrays.asList(new MaterialStack(Calcium, 1), new MaterialStack(Carbon, 1), new MaterialStack(Oxygen, 3))); + public static Materials Cassiterite = new Materials( 824, TextureSet.SET_METALLIC , 1.0F, 0, 3, 1 |8 , 220, 220, 220, 0, "Cassiterite" , "Cassiterite" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeWhite , 1, Arrays.asList(new MaterialStack(Tin, 1), new MaterialStack(Oxygen, 2))); + public static Materials CassiteriteSand = new Materials( 937, TextureSet.SET_SAND , 1.0F, 0, 1, 1 |8 , 220, 220, 220, 0, "CassiteriteSand" , "Cassiterite Sand" , 0, 0, -1, 0, false, false, 4, 1, 1, Dyes.dyeWhite , 1, Arrays.asList(new MaterialStack(Tin, 1), new MaterialStack(Oxygen, 2))); + public static Materials Chalcopyrite = new Materials( 855, TextureSet.SET_DULL , 1.0F, 0, 1, 1 |8 , 160, 120, 40, 0, "Chalcopyrite" , "Chalcopyrite" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeYellow , 1, Arrays.asList(new MaterialStack(Copper, 1), new MaterialStack(Iron, 1), new MaterialStack(Sulfur, 2))); + public static Materials Charcoal = new Materials( 536, TextureSet.SET_FINE , 1.0F, 0, 1, 1 |4 , 100, 70, 70, 0, "Charcoal" , "Charcoal" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeBlack , 1, Collections.singletonList(new MaterialStack(Carbon, 1)), Arrays.asList(new TC_AspectStack(TC_Aspects.POTENTIA, 2), new TC_AspectStack(TC_Aspects.IGNIS, 2))); + public static Materials Chromite = new Materials( 825, TextureSet.SET_METALLIC , 1.0F, 0, 1, 1 |8 , 35, 20, 15, 0, "Chromite" , "Chromite" , 0, 0, 1700, 1700, true, false, 6, 1, 1, Dyes.dyePink , 1, Arrays.asList(new MaterialStack(Iron, 1), new MaterialStack(Chrome, 2), new MaterialStack(Oxygen, 4))); + public static Materials ChromiumDioxide = new Materials( 361, TextureSet.SET_DULL , 11.0F, 256, 3, 1|2 , 230, 200, 200, 0, "ChromiumDioxide" , "Chromium Dioxide" , 0, 0, 650, 650, false, false, 5, 1, 1, Dyes.dyePink , 1, Arrays.asList(new MaterialStack(Chrome, 1), new MaterialStack(Oxygen, 2)), Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.MACHINA, 1))); + public static Materials Cinnabar = new Materials( 826, TextureSet.SET_ROUGH , 1.0F, 0, 1, 1 |8 , 150, 0, 0, 0, "Cinnabar" , "Cinnabar" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes.dyeBrown , 2, Arrays.asList(new MaterialStack(Mercury, 1), new MaterialStack(Sulfur, 1))); + public static Materials Water = new Materials( 701, TextureSet.SET_FLUID , 1.0F, 0, 0, 16 , 0, 0, 255, 0, "Water" , "Water" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeBlue , 0, Arrays.asList(new MaterialStack(Hydrogen, 2), new MaterialStack(Oxygen, 1)), Collections.singletonList(new TC_AspectStack(TC_Aspects.AQUA, 2))); + public static Materials Clay = new Materials( 805, TextureSet.SET_ROUGH , 1.0F, 0, 1, 1 , 200, 200, 220, 0, "Clay" , "Clay" , 0, 0, -1, 0, false, false, 5, 1, 1, Dyes.dyeLightBlue , 0, Arrays.asList(new MaterialStack(Sodium, 2), new MaterialStack(Lithium, 1), new MaterialStack(Aluminium, 2), new MaterialStack(Silicon, 2),new MaterialStack(Oxygen,7),new MaterialStack(Water,2))); + public static Materials Coal = new Materials( 535, TextureSet.SET_ROUGH , 1.0F, 0, 1, 1 |4|8 , 70, 70, 70, 0, "Coal" , "Coal" , 0, 0, -1, 0, false, false, 2, 2, 1, Dyes.dyeBlack , 1, Collections.singletonList(new MaterialStack(Carbon, 1)), Arrays.asList(new TC_AspectStack(TC_Aspects.POTENTIA, 2), new TC_AspectStack(TC_Aspects.IGNIS, 2))); + public static Materials Cobaltite = new Materials( 827, TextureSet.SET_METALLIC , 1.0F, 0, 1, 1 |8 , 80, 80, 250, 0, "Cobaltite" , "Cobaltite" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes.dyeBlue , 1, Arrays.asList(new MaterialStack(Cobalt, 1), new MaterialStack(Arsenic, 1), new MaterialStack(Sulfur, 1))); + public static Materials Cooperite = new Materials( 828, TextureSet.SET_METALLIC , 1.0F, 0, 1, 1 |8 , 255, 255, 200, 0, "Cooperite" , "Sheldonite" , 0, 0, -1, 0, false, false, 5, 1, 1, Dyes.dyeYellow , 2, Arrays.asList(new MaterialStack(Platinum, 3), new MaterialStack(Nickel, 1), new MaterialStack(Sulfur, 1), new MaterialStack(Palladium, 1))); + public static Materials Cupronickel = new Materials( 310, TextureSet.SET_METALLIC , 6.0F, 64, 1, 1|2 |64 , 227, 150, 128, 0, "Cupronickel" , "Cupronickel" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeOrange , 2, Arrays.asList(new MaterialStack(Copper, 1), new MaterialStack(Nickel, 1))); + public static Materials DarkAsh = new Materials( 816, TextureSet.SET_DULL , 1.0F, 0, 1, 1 , 50, 50, 50, 0, "DarkAsh" , "Dark Ashes" , 0, 0, -1, 0, false, false, 1, 2, 1, Dyes.dyeGray , 1, Collections.singletonList(new MaterialStack(Carbon, 1)), Arrays.asList(new TC_AspectStack(TC_Aspects.IGNIS, 1), new TC_AspectStack(TC_Aspects.PERDITIO, 1))); + public static Materials DeepIron = new Materials( 829, TextureSet.SET_METALLIC , 6.0F, 384, 2, 1|2 |8 |64 , 150, 140, 140, 0, "DeepIron" , "Deep Iron" , 0, 0, 7500, 7500, true, false, 3, 1, 1, Dyes.dyePink , 2, Collections.singletonList(new MaterialStack(Iron, 1)), Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.MAGNETO, 1))).disableAutoGeneratedVacuumFreezerRecipe(); + public static Materials Diamond = new Materials( 500, TextureSet.SET_DIAMOND , 8.0F, 1280, 4, 1 |4|8 |64|128 , 200, 255, 255, 127, "Diamond" , "Diamond" , 0, 0, -1, 0, false, true, 5, 64, 1, Dyes.dyeWhite , 1, Collections.singletonList(new MaterialStack(Carbon, 1)), Arrays.asList(new TC_AspectStack(TC_Aspects.VITREUS, 3), new TC_AspectStack(TC_Aspects.LUCRUM, 4))); + public static Materials Electrum = new Materials( 303, TextureSet.SET_SHINY , 12.0F, 64, 2, 1|2 |8 |64|128 , 255, 255, 100, 0, "Electrum" , "Electrum" , 0, 0, -1, 0, false, false, 4, 1, 1, Dyes.dyeYellow , 2, Arrays.asList(new MaterialStack(Silver, 1), new MaterialStack(Gold, 1))); + public static Materials Emerald = new Materials( 501, TextureSet.SET_EMERALD , 7.0F, 256, 4, 1 |4|8 |64 , 80, 255, 80, 127, "Emerald" , "Emerald" , 0, 0, -1, 0, false, true, 5, 1, 1, Dyes.dyeGreen , 0, Arrays.asList(new MaterialStack(Beryllium, 3), new MaterialStack(Aluminium, 2), new MaterialStack(Silicon, 6), new MaterialStack(Oxygen, 18)), Arrays.asList(new TC_AspectStack(TC_Aspects.VITREUS, 3), new TC_AspectStack(TC_Aspects.LUCRUM, 5))); + public static Materials FreshWater = new Materials( -1, TextureSet.SET_FLUID , 1.0F, 0, 0, 16 , 0, 0, 255, 0, "FreshWater" , "Fresh Water" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeBlue , 0, Arrays.asList(new MaterialStack(Hydrogen, 2), new MaterialStack(Oxygen, 1)), Collections.singletonList(new TC_AspectStack(TC_Aspects.AQUA, 2))); + public static Materials Galena = new Materials( 830, TextureSet.SET_DULL , 1.0F, 0, 3, 1 |8 , 100, 60, 100, 0, "Galena" , "Galena" , 0, 0, -1, 0, false, false, 4, 1, 1, Dyes.dyePurple , 1, Arrays.asList(new MaterialStack(Lead, 1), new MaterialStack(Sulfur, 1))); + public static Materials Garnierite = new Materials( 906, TextureSet.SET_METALLIC , 1.0F, 0, 3, 1 |8 , 50, 200, 70, 0, "Garnierite" , "Garnierite" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeLightBlue , 1, Arrays.asList(new MaterialStack(Nickel, 1), new MaterialStack(Oxygen, 1))); + public static Materials Glyceryl = new Materials( 714, TextureSet.SET_FLUID , 1.0F, 0, 1, 16 , 0, 150, 150, 0, "Glyceryl" , "Glyceryl Trinitrate" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeCyan , 1, Arrays.asList(new MaterialStack(Carbon, 3), new MaterialStack(Hydrogen, 5), new MaterialStack(Nitrogen, 3), new MaterialStack(Oxygen, 9))); + public static Materials GreenSapphire = new MaterialBuilder(504, TextureSet.SET_GEM_HORIZONTAL, "Green Sapphire").setToolSpeed(7.0F).setDurability(256).setToolQuality(2).addDustItems().addGemItems().setTransparent(true).addOreItems().addToolHeadItems().setRGBA(100, 200, 130, 127).setColor(Dyes.dyeCyan).setOreValue(5).setExtraData(0).setMaterialList(new MaterialStack(Aluminium, 2), new MaterialStack(Oxygen, 3)).setAspects(Arrays.asList(new TC_AspectStack(TC_Aspects.LUCRUM, 5), new TC_AspectStack(TC_Aspects.VITREUS, 3))).constructMaterial().disableAutoGeneratedBlastFurnaceRecipes(); + public static Materials Grossular = new Materials( 831, TextureSet.SET_ROUGH , 1.0F, 0, 1, 1 |8 , 200, 100, 0, 0, "Grossular" , "Grossular" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes.dyeOrange , 0, Arrays.asList(new MaterialStack(Calcium, 3), new MaterialStack(Aluminium, 2), new MaterialStack(Silicon, 3), new MaterialStack(Oxygen, 12))); + public static Materials HolyWater = new Materials( 729, TextureSet.SET_FLUID , 1.0F, 0, 0, 16 , 0, 0, 255, 0, "HolyWater" , "Holy Water" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeBlue , 0, Arrays.asList(new MaterialStack(Hydrogen, 2), new MaterialStack(Oxygen, 1)), Arrays.asList(new TC_AspectStack(TC_Aspects.AQUA, 2), new TC_AspectStack(TC_Aspects.AURAM, 1))); + public static Materials Ice = new Materials( 702, TextureSet.SET_SHINY , 1.0F, 0, 0, 1| 16 , 200, 200, 255, 0, "Ice" , "Ice" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeBlue , 0, Arrays.asList(new MaterialStack(Hydrogen, 2), new MaterialStack(Oxygen, 1)), Collections.singletonList(new TC_AspectStack(TC_Aspects.GELUM, 2))); + public static Materials Ilmenite = new Materials( 918, TextureSet.SET_METALLIC , 1.0F, 0, 3, 1 |8 , 70, 55, 50, 0, "Ilmenite" , "Ilmenite" , 0, 0, -1, 0, false, false, 1, 2, 1, Dyes.dyePurple , 0, Arrays.asList(new MaterialStack(Iron, 1), new MaterialStack(Titanium, 1), new MaterialStack(Oxygen, 3))); + public static Materials Rutile = new Materials( 375, TextureSet.SET_GEM_HORIZONTAL , 1.0F, 0, 2, 1 |8 , 212, 13, 92, 0, "Rutile" , "Rutile" , 0, 0, -1, 0, false, false, 1, 2, 1, Dyes.dyeRed , 0, Arrays.asList(new MaterialStack(Titanium, 1), new MaterialStack(Oxygen, 2))); + public static Materials Bauxite = new Materials( 822, TextureSet.SET_DULL , 1.0F, 0, 1, 1 |8 , 200, 100, 0, 0, "Bauxite" , "Bauxite" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes.dyeBrown , 1, Arrays.asList(new MaterialStack(Rutile, 2), new MaterialStack(Aluminium, 16), new MaterialStack(Hydrogen, 10), new MaterialStack(Oxygen, 11))); + public static Materials Titaniumtetrachloride = new Materials( 376, TextureSet.SET_FLUID , 1.0F, 0, 2, 16 , 212, 13, 92, 0, "Titaniumtetrachloride" , "Titaniumtetrachloride" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeRed , 0, Arrays.asList(new MaterialStack(Titanium, 1), new MaterialStack(Chlorine, 4))); + public static Materials Magnesiumchloride = new Materials( 377, TextureSet.SET_DULL , 1.0F, 0, 2, 1|16 , 212, 13, 92, 0, "Magnesiumchloride" , "Magnesiumchloride" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeRed , 0, Arrays.asList(new MaterialStack(Magnesium, 1), new MaterialStack(Chlorine, 2))); + public static Materials Invar = new Materials( 302, TextureSet.SET_METALLIC , 6.0F, 256, 2, 1|2 |64|128 , 180, 180, 120, 0, "Invar" , "Invar" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeBrown , 2, Arrays.asList(new MaterialStack(Iron, 2), new MaterialStack(Nickel, 1)), Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.GELUM, 1))); + public static Materials Kanthal = new Materials( 312, TextureSet.SET_METALLIC , 6.0F, 64, 2, 1|2 |64 , 194, 210, 223, 0, "Kanthal" , "Kanthal" , 0, 0, 1800, 1800, true, false, 1, 1, 1, Dyes.dyeYellow , 2, Arrays.asList(new MaterialStack(Iron, 1), new MaterialStack(Aluminium, 1), new MaterialStack(Chrome, 1))).disableAutoGeneratedBlastFurnaceRecipes(); + public static Materials Lazurite = new Materials( 524, TextureSet.SET_LAPIS , 1.0F, 0, 1, 1 |4|8 , 100, 120, 255, 0, "Lazurite" , "Lazurite" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes.dyeCyan , 1, Arrays.asList(new MaterialStack(Aluminium, 6), new MaterialStack(Silicon, 6), new MaterialStack(Calcium, 8), new MaterialStack(Sodium, 8))); + public static Materials Magnalium = new Materials( 313, TextureSet.SET_DULL , 6.0F, 256, 2, 1|2 |64|128 , 200, 190, 255, 0, "Magnalium" , "Magnalium" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeLightBlue , 2, Arrays.asList(new MaterialStack(Magnesium, 1), new MaterialStack(Aluminium, 2))); + public static Materials Magnesite = new Materials( 908, TextureSet.SET_METALLIC , 1.0F, 0, 2, 1 |8 , 250, 250, 180, 0, "Magnesite" , "Magnesite" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyePink , 1, Arrays.asList(new MaterialStack(Magnesium, 1), new MaterialStack(Carbon, 1), new MaterialStack(Oxygen, 3))); + public static Materials Magnetite = new Materials( 870, TextureSet.SET_METALLIC , 1.0F, 0, 2, 1 |8 , 30, 30, 30, 0, "Magnetite" , "Magnetite" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeGray , 1, Arrays.asList(new MaterialStack(Iron, 3), new MaterialStack(Oxygen, 4)), Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.MAGNETO, 1))); + public static Materials Molybdenite = new Materials( 942, TextureSet.SET_METALLIC , 1.0F, 0, 2, 1 |8 , 25, 25, 25, 0, "Molybdenite" , "Molybdenite" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeBlue , 1, Arrays.asList(new MaterialStack(Molybdenum, 1), new MaterialStack(Sulfur, 2))); // MoS2 (also source of Re) + public static Materials Nichrome = new Materials( 311, TextureSet.SET_METALLIC , 6.0F, 64, 2, 1|2 |64 , 205, 206, 246, 0, "Nichrome" , "Nichrome" , 0, 0, 2700, 2700, true, false, 1, 1, 1, Dyes.dyeRed , 2, Arrays.asList(new MaterialStack(Nickel, 4), new MaterialStack(Chrome, 1))).disableAutoGeneratedBlastFurnaceRecipes().disableAutoGeneratedVacuumFreezerRecipe(); + public static Materials NiobiumNitride = new Materials( 359, TextureSet.SET_DULL , 1.0F, 0, 2, 1|2 , 29, 41, 29, 0, "NiobiumNitride" , "Niobium Nitride" , 0, 0, 2573, 2573, true, false, 1, 1, 1, Dyes.dyeBlack , 1, Arrays.asList(new MaterialStack(Niobium, 1), new MaterialStack(Nitrogen, 1))); // Anti-Reflective Material + public static Materials NiobiumTitanium = new Materials( 360, TextureSet.SET_DULL , 1.0F, 0, 2, 1|2 , 29, 29, 41, 0, "NiobiumTitanium" , "Niobium-Titanium" , 0, 0, 4500, 4500, true, false, 1, 1, 1, Dyes.dyeBlack , 2, Arrays.asList(new MaterialStack(Niobium, 1), new MaterialStack(Titanium, 1))).disableAutoGeneratedBlastFurnaceRecipes().disableAutoGeneratedVacuumFreezerRecipe(); + public static Materials NitroCarbon = new Materials( 716, TextureSet.SET_FLUID , 1.0F, 0, 1, 16 , 0, 75, 100, 0, "NitroCarbon" , "Nitro-Carbon" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeCyan , 1, Arrays.asList(new MaterialStack(Nitrogen, 1), new MaterialStack(Carbon, 1))); + public static Materials NitrogenDioxide = new Materials( 717, TextureSet.SET_FLUID , 1.0F, 0, 1, 16 , 100, 175, 255, 0, "NitrogenDioxide" , "Nitrogen Dioxide" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeCyan , 1, Arrays.asList(new MaterialStack(Nitrogen, 1), new MaterialStack(Oxygen, 2))); + public static Materials Obsidian = new Materials( 804, TextureSet.SET_DULL , 1.0F, 0, 3, 1|2 , 80, 50, 100, 0, "Obsidian" , "Obsidian" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeBlack , 1, Arrays.asList(new MaterialStack(Magnesium, 1), new MaterialStack(Iron, 1), new MaterialStack(Silicon, 2), new MaterialStack(Oxygen, 8))); + public static Materials Phosphate = new Materials( 833, TextureSet.SET_DULL , 1.0F, 0, 1, 1 |8|16 , 255, 255, 0, 0, "Phosphate" , "Phosphate" , 0, 0, -1, 0, false, false, 2, 1, 1, Dyes.dyeYellow , 1, Arrays.asList(new MaterialStack(Phosphorus, 1), new MaterialStack(Oxygen, 4))); + public static Materials PigIron = new Materials( 307, TextureSet.SET_METALLIC , 6.0F, 384, 2, 1|2 |8 |64 , 200, 180, 180, 0, "PigIron" , "Pig Iron" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes.dyePink , 2, Collections.singletonList(new MaterialStack(Iron, 1))); + public static Materials Plastic = new Materials( 874, TextureSet.SET_DULL , 3.0F, 32, 1, 1|2 |64|128 , 200, 200, 200, 0, "Plastic" , "Polyethylene" , 0, 0, 400, 0, false, false, 1, 1, 1, Dyes.dyeWhite , 0, Arrays.asList(new MaterialStack(Carbon, 1), new MaterialStack(Hydrogen, 2)), Collections.singletonList(new TC_AspectStack(TC_Aspects.MOTUS, 2))); + public static Materials Epoxid = new Materials( 470, TextureSet.SET_DULL , 3.0F, 32, 1, 1|2 |64|128 , 200, 140, 20, 0, "Epoxid" , "Epoxid" , 0, 0, 400, 0, false, false, 1, 1, 1, Dyes.dyeWhite , 0, Arrays.asList(new MaterialStack(Carbon, 21), new MaterialStack(Hydrogen, 24), new MaterialStack(Oxygen, 4)), Collections.singletonList(new TC_AspectStack(TC_Aspects.MOTUS, 2))); + public static Materials Polydimethylsiloxane = new MaterialBuilder(633, TextureSet.SET_FLUID , "Polydimethylsiloxane").addDustItems().setRGB(245, 245, 245).setColor(Dyes.dyeWhite).setMaterialList(new MaterialStack(Carbon, 2), new MaterialStack(Hydrogen, 6), new MaterialStack(Oxygen, 1), new MaterialStack(Silicon, 1)).addElectrolyzerRecipe().constructMaterial(); + public static Materials Silicone = new Materials( 471, TextureSet.SET_DULL , 3.0F, 128, 1, 1|2 |64|128 , 220, 220, 220, 0, "Silicone" , "Silicone Rubber" , 0, 0, 900, 0, false, false, 1, 1, 1, Dyes.dyeWhite , 0, Arrays.asList(new MaterialStack(Carbon, 2), new MaterialStack(Hydrogen, 6), new MaterialStack(Oxygen, 1), new MaterialStack(Silicon, 1)), Collections.singletonList(new TC_AspectStack(TC_Aspects.MOTUS, 2))); + public static Materials Polycaprolactam = new Materials( 472, TextureSet.SET_DULL , 3.0F, 32, 1, 1|2 |64|128 , 50, 50, 50, 0, "Polycaprolactam" , "Polycaprolactam" , 0, 0, 500, 0, false, false, 1, 1, 1, Dyes.dyeWhite , 0, Arrays.asList(new MaterialStack(Carbon, 6), new MaterialStack(Hydrogen, 11), new MaterialStack(Nitrogen, 1), new MaterialStack(Oxygen, 1)), Collections.singletonList(new TC_AspectStack(TC_Aspects.MOTUS, 2))); + public static Materials Polytetrafluoroethylene = new Materials( 473, TextureSet.SET_DULL , 3.0F, 32, 1, 1|2 |64|128 , 100, 100, 100, 0, "Polytetrafluoroethylene" , "Polytetrafluoroethylene" , 0, 0, 1400, 0, false, false, 1, 1, 1, Dyes.dyeWhite , 0, Arrays.asList(new MaterialStack(Carbon, 2), new MaterialStack(Fluorine, 4)), Collections.singletonList(new TC_AspectStack(TC_Aspects.MOTUS, 2))); + public static Materials Powellite = new Materials( 883, TextureSet.SET_DULL , 1.0F, 0, 2, 1 |8 , 255, 255, 0, 0, "Powellite" , "Powellite" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeYellow , 2, Arrays.asList(new MaterialStack(Calcium, 1), new MaterialStack(Molybdenum, 1), new MaterialStack(Oxygen, 4))); + public static Materials Pumice = new Materials( 926, TextureSet.SET_DULL , 1.0F, 0, 2, 1 |8 , 230, 185, 185, 0, "Pumice" , "Pumice" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeGray , 2, Collections.singletonList(new MaterialStack(Stone, 1))); + public static Materials Pyrite = new Materials( 834, TextureSet.SET_ROUGH , 1.0F, 0, 1, 1 |8 , 150, 120, 40, 0, "Pyrite" , "Pyrite" , 0, 0, -1, 0, false, false, 2, 1, 1, Dyes.dyeOrange , 1, Arrays.asList(new MaterialStack(Iron, 1), new MaterialStack(Sulfur, 2))); + public static Materials Pyrolusite = new Materials( 943, TextureSet.SET_DULL , 1.0F, 0, 2, 1 |8 , 150, 150, 170, 0, "Pyrolusite" , "Pyrolusite" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeLightGray , 1, Arrays.asList(new MaterialStack(Manganese, 1), new MaterialStack(Oxygen, 2))); + public static Materials Pyrope = new Materials( 835, TextureSet.SET_METALLIC , 1.0F, 0, 2, 1 |8 , 120, 50, 100, 0, "Pyrope" , "Pyrope" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes.dyePurple , 0, Arrays.asList(new MaterialStack(Aluminium, 2), new MaterialStack(Magnesium, 3), new MaterialStack(Silicon, 3), new MaterialStack(Oxygen, 12))); + public static Materials RockSalt = new Materials( 944, TextureSet.SET_FINE , 1.0F, 0, 1, 1 |8 , 240, 200, 200, 0, "RockSalt" , "Rock Salt" , 0, 0, -1, 0, false, false, 2, 1, 1, Dyes.dyeWhite , 1, Arrays.asList(new MaterialStack(Potassium, 1), new MaterialStack(Chlorine, 1))); + public static Materials Rubber = new Materials( 880, TextureSet.SET_SHINY , 1.5F, 32, 0, 1|2 |64|128 , 0, 0, 0, 0, "Rubber" , "Rubber" , 0, 0, 400, 0, false, false, 1, 1, 1, Dyes.dyeBlack , 0, Arrays.asList(new MaterialStack(Carbon, 5), new MaterialStack(Hydrogen, 8)), Collections.singletonList(new TC_AspectStack(TC_Aspects.MOTUS, 2))); + public static Materials RawRubber = new Materials( 896, TextureSet.SET_DULL , 1.0F, 0, 0, 1 , 204, 199, 137, 0, "RawRubber" , "Raw Rubber" , 0, 0, 400, 0, false, false, 1, 1, 1, Dyes.dyeWhite , 0, Arrays.asList(new MaterialStack(Carbon, 5), new MaterialStack(Hydrogen, 8)), Collections.singletonList(new TC_AspectStack(TC_Aspects.MOTUS, 2))); + public static Materials Ruby = new Materials( 502, TextureSet.SET_RUBY , 7.0F, 256, 2, 1 |4|8 |64 , 255, 100, 100, 127, "Ruby" , "Ruby" , 0, 0, -1, 0, false, true, 5, 1, 1, Dyes.dyeRed , 0, Arrays.asList(new MaterialStack(Chrome, 1), new MaterialStack(Aluminium, 2), new MaterialStack(Oxygen, 3)), Arrays.asList(new TC_AspectStack(TC_Aspects.LUCRUM, 6), new TC_AspectStack(TC_Aspects.VITREUS, 4))).disableAutoGeneratedBlastFurnaceRecipes(); + public static Materials Salt = new Materials( 817, TextureSet.SET_FINE , 1.0F, 0, 1, 1 |8 , 250, 250, 250, 0, "Salt" , "Salt" , 0, 0, -1, 0, false, false, 2, 1, 1, Dyes.dyeWhite , 0, Arrays.asList(new MaterialStack(Sodium, 1), new MaterialStack(Chlorine, 1))); + public static Materials Saltpeter = new Materials( 836, TextureSet.SET_FINE , 1.0F, 0, 1, 1 |8 , 230, 230, 230, 0, "Saltpeter" , "Saltpeter" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes.dyeWhite , 1, Arrays.asList(new MaterialStack(Potassium, 1), new MaterialStack(Nitrogen, 1), new MaterialStack(Oxygen, 3))); + public static Materials Sapphire = new MaterialBuilder(503, TextureSet.SET_GEM_VERTICAL, "Sapphire").setToolSpeed(7.0F).setDurability(256).setToolQuality(2).addDustItems().addGemItems().setTransparent(true).addOreItems().addToolHeadItems().setRGBA(100, 100, 200, 127).setColor(Dyes.dyeBlue).setOreValue(5).setExtraData(0).setMaterialList(new MaterialStack(Aluminium, 2), new MaterialStack(Oxygen, 3)).setAspects(Arrays.asList(new TC_AspectStack(TC_Aspects.LUCRUM, 5), new TC_AspectStack(TC_Aspects.VITREUS, 3))).constructMaterial().disableAutoGeneratedBlastFurnaceRecipes(); + //public static Materials Sapphire = new Materials( 503, TextureSet.SET_GEM_VERTICAL , 7.0F, 256, 2, 1 |4|8 |64 , 100, 100, 200, 127, "Sapphire" , "Sapphire" , 0, 0, -1, 0, false, true, 5, 1, 1, Dyes.dyeBlue , 1, Arrays.asList(new MaterialStack(Aluminium, 2), new MaterialStack(Oxygen, 3)), Arrays.asList(new TC_AspectStack(TC_Aspects.LUCRUM, 5), new TC_AspectStack(TC_Aspects.VITREUS, 3))); + public static Materials Scheelite = new Materials( 910, TextureSet.SET_DULL , 1.0F, 0, 3, 1 |8 , 200, 140, 20, 0, "Scheelite" , "Scheelite" , 0, 0, 2500, 2500, false, false, 4, 1, 1, Dyes.dyeBlack , 0, Arrays.asList(new MaterialStack(Tungsten, 1), new MaterialStack(Calcium, 1), new MaterialStack(Oxygen, 4))); + public static Materials Snow = new Materials( 728, TextureSet.SET_FINE , 1.0F, 0, 0, 1| 16 , 250, 250, 250, 0, "Snow" , "Snow" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeWhite , 0, Arrays.asList(new MaterialStack(Hydrogen, 2), new MaterialStack(Oxygen, 1)), Collections.singletonList(new TC_AspectStack(TC_Aspects.GELUM, 1))); + public static Materials Sodalite = new Materials( 525, TextureSet.SET_LAPIS , 1.0F, 0, 1, 1 |4|8 , 20, 20, 255, 0, "Sodalite" , "Sodalite" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes.dyeBlue , 1, Arrays.asList(new MaterialStack(Aluminium, 3), new MaterialStack(Silicon, 3), new MaterialStack(Sodium, 4), new MaterialStack(Chlorine, 1))); + public static Materials SodiumPersulfate = new Materials( 718, TextureSet.SET_FLUID , 1.0F, 0, 2, 16 , 255, 255, 255, 0, "SodiumPersulfate" , "Sodium Persulfate" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeOrange , 1, Arrays.asList(new MaterialStack(Sodium, 2), new MaterialStack(Sulfur, 2), new MaterialStack(Oxygen, 8))); + public static Materials SodiumSulfide = new Materials( 719, TextureSet.SET_FLUID , 1.0F, 0, 2, 1 , 255, 230, 128, 0, "SodiumSulfide" , "Sodium Sulfide" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeOrange , 1, Arrays.asList(new MaterialStack(Sodium, 2), new MaterialStack(Sulfur, 1))); + public static Materials HydricSulfide = new Materials( 460, TextureSet.SET_FLUID , 1.0F, 0, 2, 16 , 255, 255, 255, 0, "HydricSulfide" , "Hydrogen Sulfide" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeOrange , 1, Arrays.asList(new MaterialStack(Hydrogen, 2), new MaterialStack(Sulfur, 1))); + + public static Materials OilHeavy = new Materials( 730, TextureSet.SET_FLUID , 1.0F, 0, 0, 16 , 10, 10, 10, 0, "OilHeavy" , "Heavy Oil" , 3, 40, -1, 0, false, false, 1, 1, 1, Dyes.dyeBlack ); + public static Materials OilMedium = new Materials( 731, TextureSet.SET_FLUID , 1.0F, 0, 0, 16 , 10, 10, 10, 0, "OilMedium" , "Raw Oil" , 3, 30, -1, 0, false, false, 1, 1, 1, Dyes.dyeBlack ); + public static Materials OilLight = new Materials( 732, TextureSet.SET_FLUID , 1.0F, 0, 0, 16 , 10, 10, 10, 0, "OilLight" , "Light Oil" , 3, 20, -1, 0, false, false, 1, 1, 1, Dyes.dyeBlack ); + + public static Materials NatruralGas = new Materials( 733, TextureSet.SET_FLUID , 1.0F, 0, 1, 16 , 255, 255, 255, 0, "NatruralGas" , "Natural Gas" , 1, 20, -1, 0, false, false, 3, 1, 1, Dyes.dyeWhite ); + public static Materials SulfuricGas = new Materials( 734, TextureSet.SET_FLUID , 1.0F, 0, 1, 16 , 255, 255, 255, 0, "SulfuricGas" , "Sulfuric Gas" , 1, 25, -1, 0, false, false, 3, 1, 1, Dyes.dyeWhite ); + public static Materials Gas = new Materials( 735, TextureSet.SET_FLUID , 1.0F, 0, 1, 16 , 255, 255, 255, 0, "Gas" , "Refinery Gas" , 1, 160, -1, 0, false, false, 3, 1, 1, Dyes.dyeWhite).setCanBeCracked(true); + public static Materials SulfuricNaphtha = new Materials( 736, TextureSet.SET_FLUID , 1.0F, 0, 0, 16 , 255, 255, 0, 0, "SulfuricNaphtha" , "Sulfuric Naphtha" , 1, 40, -1, 0, false, false, 1, 1, 1, Dyes.dyeYellow ); + public static Materials SulfuricLightFuel = new Materials( 737, TextureSet.SET_FLUID , 1.0F, 0, 0, 16 , 255, 255, 0, 0, "SulfuricLightFuel" , "Sulfuric Light Fuel" , 0, 40, -1, 0, false, false, 1, 1, 1, Dyes.dyeYellow ); + public static Materials SulfuricHeavyFuel = new Materials( 738, TextureSet.SET_FLUID , 1.0F, 0, 0, 16 , 255, 255, 0, 0, "SulfuricHeavyFuel" , "Sulfuric Heavy Fuel" , 3, 40, -1, 0, false, false, 1, 1, 1, Dyes.dyeBlack ); + public static Materials Naphtha = new Materials( 739, TextureSet.SET_FLUID , 1.0F, 0, 0, 16 , 255, 255, 0, 0, "Naphtha" , "Naphtha" , 1, 320, -1, 0, false, false, 1, 1, 1, Dyes.dyeYellow).setCanBeCracked(true); + public static Materials LightFuel = new Materials( 740, TextureSet.SET_FLUID , 1.0F, 0, 0, 16 , 255, 255, 0, 0, "LightFuel" , "Light Fuel" , 0, 305, -1, 0, false, false, 1, 1, 1, Dyes.dyeYellow).setCanBeCracked(true); + public static Materials HeavyFuel = new Materials( 741, TextureSet.SET_FLUID , 1.0F, 0, 0, 16 , 255, 255, 0, 0, "HeavyFuel" , "Heavy Fuel" , 3, 240, -1, 0, false, false, 1, 1, 1, Dyes.dyeBlack).setCanBeCracked(true); + public static Materials LPG = new Materials( 742, TextureSet.SET_FLUID , 1.0F, 0, 0, 16 , 255, 255, 0, 0, "LPG" , "LPG" , 1, 320, -1, 0, false, false, 1, 1, 1, Dyes.dyeYellow ); + + public static Materials FluidNaquadahFuel = new MaterialBuilder(600, TextureSet.SET_FLUID , "Naquadah Fuel").setName("FluidNaqudahFuel").addCell().addFluid().setRGB(62, 62, 62).setColor(Dyes.dyeBlack).constructMaterial(); + public static Materials EnrichedNaquadria = new MaterialBuilder(601, TextureSet.SET_FLUID , "Enriched Naquadria").setName("EnrichedNaquadria").addCell().addFluid().setRGB(52, 52, 52).setColor(Dyes.dyeBlack).constructMaterial(); + + public static Materials ReinforceGlass = new MaterialBuilder(602, TextureSet.SET_FLUID , "Reinforced Glass").setName("ReinforcedGlass").setRGB(192, 245, 254).setColor(Dyes.dyeWhite).setMeltingPoint(2000).constructMaterial().disableAutoGeneratedRecycleRecipes(); + public static Materials BioMediumRaw = new MaterialBuilder(603, TextureSet.SET_FLUID , "Raw Bio Catalyst Medium").setName("BioMediumRaw").addCell().addFluid().setRGB(97, 147, 46).setColor(Dyes.dyeLime).constructMaterial(); + public static Materials BioMediumSterilized = new MaterialBuilder(604, TextureSet.SET_FLUID , "Sterilized Bio Catalyst Medium").setName("BiohMediumSterilized").addCell().addFluid().setRGB(162, 253, 53).setColor(Dyes.dyeLime).constructMaterial(); + + public static Materials Chlorobenzene = new MaterialBuilder(605, TextureSet.SET_FLUID , "Chlorobenzene").addCell().addFluid().setRGB(0, 50, 65).setColor(Dyes.dyeGray).setMaterialList(new MaterialStack(Carbon, 6), new MaterialStack(Hydrogen, 5), new MaterialStack(Chlorine, 1)).addElectrolyzerRecipe().constructMaterial(); + public static Materials DilutedHydrochloricAcid = new MaterialBuilder(606, TextureSet.SET_FLUID , "Diluted Hydrochloric Acid").setName("DilutedHydrochloricAcid_GT5U").addCell().addFluid().setRGB(153, 167, 163).setColor(Dyes.dyeLightGray).setMaterialList(new MaterialStack(Hydrogen, 1), new MaterialStack(Chlorine, 1)).constructMaterial(); + public static Materials Pyrochlore = new MaterialBuilder(607, TextureSet.SET_METALLIC , "Pyrochlore").addDustItems().addOreItems().setRGB(43, 17, 0).setColor(Dyes.dyeBlack).setMaterialList(new MaterialStack(Calcium, 2), new MaterialStack(Niobium, 2), new MaterialStack(Oxygen, 7)).addElectrolyzerRecipe().constructMaterial(); + + public static Materials GrowthMediumRaw = new MaterialBuilder(608, TextureSet.SET_FLUID , "Raw Growth Catalyst Medium").setName("GrowthMediumRaw").addCell().addFluid().setRGB(211, 141, 95).setColor(Dyes.dyeOrange).constructMaterial(); + public static Materials GrowthMediumSterilized = new MaterialBuilder(609, TextureSet.SET_FLUID , "Growth Catalyst Medium").setName("GrowthMediumSterilized").addCell().addFluid().setRGB(222, 170, 135).setColor(Dyes.dyeOrange).constructMaterial(); + + public static Materials FerriteMixture = new MaterialBuilder(612, TextureSet.SET_METALLIC , "Ferrite Mixture").addDustItems().setRGB(180, 180, 180).setColor(Dyes.dyeGray).setMaterialList(new MaterialStack(Nickel, 1), new MaterialStack(Zinc, 1), new MaterialStack(Iron, 4)).constructMaterial(); + public static Materials NickelZincFerrite = new MaterialBuilder(613, TextureSet.SET_ROUGH , "Nickel-Zinc Ferrite").addDustItems().addMetalItems().addToolHeadItems().addGearItems().setToolSpeed(3.0f).setDurability(32).setRGB(60, 60, 60).setColor(Dyes.dyeBlack).setBlastFurnaceRequired(true).setBlastFurnaceTemp(1500).setMaterialList(new MaterialStack(Nickel, 1), new MaterialStack(Zinc, 1), new MaterialStack(Iron, 4), new MaterialStack(Oxygen, 8)).constructMaterial(); + + public static Materials Massicot = new MaterialBuilder(614, TextureSet.SET_DULL , "Massicot").addDustItems().setRGB(255, 221, 85).setColor(Dyes.dyeYellow).setMaterialList(new MaterialStack(Lead, 1), new MaterialStack(Oxygen, 1)).addElectrolyzerRecipe().constructMaterial(); + public static Materials ArsenicTrioxide = new MaterialBuilder(615, TextureSet.SET_SHINY , "Arsenic Trioxide").addDustItems().setRGB(255, 255, 255).setColor(Dyes.dyeGreen).setMaterialList(new MaterialStack(Arsenic, 2), new MaterialStack(Oxygen, 3)).addElectrolyzerRecipe().constructMaterial(); + public static Materials CobaltOxide = new MaterialBuilder(616, TextureSet.SET_DULL , "Cobalt Oxide").addDustItems().setRGB(102, 128, 0).setColor(Dyes.dyeGreen).setMaterialList(new MaterialStack(Cobalt, 1), new MaterialStack(Oxygen, 1)).addElectrolyzerRecipe().constructMaterial(); + public static Materials Zincite = new MaterialBuilder(617, TextureSet.SET_DULL , "Zincite").addDustItems().setRGB(255, 255, 245).setColor(Dyes.dyeWhite).setMaterialList(new MaterialStack(Zinc, 1), new MaterialStack(Oxygen, 1)).addElectrolyzerRecipe().constructMaterial(); + public static Materials AntimonyTrioxide = new MaterialBuilder(618, TextureSet.SET_DULL , "Antimony Trioxide").addDustItems().setRGB(230, 230, 240).setColor(Dyes.dyeWhite).setMaterialList(new MaterialStack(Antimony, 2), new MaterialStack(Oxygen, 3)).addElectrolyzerRecipe().constructMaterial(); + public static Materials CupricOxide = new MaterialBuilder(619, TextureSet.SET_DULL , "Cupric Oxide").addDustItems().setRGB(15, 15, 15).setColor(Dyes.dyeBlack).setMeltingPoint(1599).setMaterialList(new MaterialStack(Copper, 1), new MaterialStack(Oxygen, 1)).addElectrolyzerRecipe().constructMaterial(); + public static Materials Ferrosilite = new MaterialBuilder(620, TextureSet.SET_DULL , "Ferrosilite").addDustItems().setRGB(151, 99, 42).setColor(Dyes.dyeBrown).setMaterialList(new MaterialStack(Iron, 1), new MaterialStack(Silicon, 1), new MaterialStack(Oxygen, 3)).addElectrolyzerRecipe().constructMaterial(); + + public static Materials Magnesia = new MaterialBuilder(621, TextureSet.SET_DULL , "Magnesia").addDustItems().setRGB(255, 225, 225).setColor(Dyes.dyeWhite).setMaterialList(new MaterialStack(Magnesium, 1), new MaterialStack(Oxygen, 1)).constructMaterial(); + public static Materials Quicklime = new MaterialBuilder(622, TextureSet.SET_DULL , "Quicklime").addDustItems().setRGB(240, 240, 240).setColor(Dyes.dyeWhite).setMaterialList(new MaterialStack(Calcium, 1), new MaterialStack(Oxygen, 1)).addElectrolyzerRecipe().constructMaterial(); + public static Materials Potash = new MaterialBuilder(623, TextureSet.SET_DULL , "Potash").addDustItems().setRGB(120, 66, 55).setColor(Dyes.dyeBrown).setMaterialList(new MaterialStack(Potassium, 2), new MaterialStack(Oxygen, 1)).addElectrolyzerRecipe().constructMaterial(); + public static Materials SodaAsh = new MaterialBuilder(624, TextureSet.SET_DULL , "Soda Ash").addDustItems().setRGB(220, 220, 255).setColor(Dyes.dyeWhite).setMaterialList(new MaterialStack(Sodium, 2), new MaterialStack(Carbon, 1), new MaterialStack(Oxygen, 3)).addElectrolyzerRecipe().constructMaterial(); + + public static Materials BioDiesel = new MaterialBuilder(627, TextureSet.SET_FLUID , "Bio Diesel").addCell().addFluid().setRGB(255, 128, 0).setColor(Dyes.dyeOrange).setFuelType(MaterialBuilder.DIESEL).setFuelPower(320).constructMaterial(); + public static Materials NitrationMixture = new MaterialBuilder(628, TextureSet.SET_FLUID , "Nitration Mixture").addCell().setRGB(230, 226, 171).setColor(Dyes.dyeBrown).constructMaterial(); + public static Materials Glycerol = new MaterialBuilder(629, TextureSet.SET_FLUID , "Glycerol").addCell().addFluid().setRGB(135, 222, 135).setColor(Dyes.dyeLime).setFuelType(MaterialBuilder.SEMIFLUID).setFuelPower(164).setMaterialList(new MaterialStack(Carbon, 3), new MaterialStack(Hydrogen, 8), new MaterialStack(Oxygen, 3)).addElectrolyzerRecipe().constructMaterial(); + public static Materials SodiumBisulfate = new MaterialBuilder(630, TextureSet.SET_FLUID , "Sodium Bisulfate").addDustItems().setRGB(0, 68, 85).setColor(Dyes.dyeBlue).setMaterialList(new MaterialStack(Sodium, 1), new MaterialStack(Hydrogen, 1), new MaterialStack(Sulfur, 1), new MaterialStack(Oxygen, 4)).constructMaterial(); + public static Materials PolyphenyleneSulfide = new MaterialBuilder(631, TextureSet.SET_DULL , "Polyphenylene Sulfide").addDustItems().addMetalItems().addToolHeadItems().addGearItems().setToolSpeed(3.0f).setDurability(32).setToolQuality(1).setRGB(170, 136, 0).setColor(Dyes.dyeBrown).setMaterialList(new MaterialStack(Carbon, 6), new MaterialStack(Hydrogen, 4), new MaterialStack(Sulfur, 1)).constructMaterial(); + public static Materials Dichlorobenzene = new MaterialBuilder(632, TextureSet.SET_FLUID , "Dichlorobenzene").addCell().addFluid().setRGB(0, 68, 85).setColor(Dyes.dyeBlue).setMaterialList(new MaterialStack(Carbon, 6), new MaterialStack(Hydrogen, 4), new MaterialStack(Chlorine, 2)).addElectrolyzerRecipe().constructMaterial(); + public static Materials Polystyrene = new MaterialBuilder(636, TextureSet.SET_DULL , "Polystyrene").addDustItems().addMetalItems().addToolHeadItems().addGearItems().setToolSpeed(3.0f).setDurability(32).setToolQuality(1).setRGB(190, 180, 170).setColor(Dyes.dyeLightGray).setMaterialList(new MaterialStack(Carbon, 8), new MaterialStack(Hydrogen, 8)).constructMaterial(); + public static Materials Styrene = new MaterialBuilder(637, TextureSet.SET_FLUID , "Styrene").addCell().addFluid().setRGB(210, 200, 190).setColor(Dyes.dyeBlack).setMaterialList(new MaterialStack(Carbon, 8), new MaterialStack(Hydrogen, 8)).addElectrolyzerRecipe().constructMaterial(); + public static Materials Isoprene = new MaterialBuilder(638, TextureSet.SET_FLUID , "Isoprene").addCell().addFluid().setRGB(20, 20, 20).setColor(Dyes.dyeBlack).setMaterialList(new MaterialStack(Carbon, 5), new MaterialStack(Hydrogen, 8)).addElectrolyzerRecipe().constructMaterial(); + public static Materials Tetranitromethane = new MaterialBuilder(639, TextureSet.SET_FLUID , "Tetranitromethane").addCell().addFluid().setRGB(15, 40, 40).setColor(Dyes.dyeBlack).setMaterialList(new MaterialStack(Carbon, 1), new MaterialStack(Nitrogen, 4), new MaterialStack(Oxygen, 8)).addElectrolyzerRecipe().constructMaterial(); + public static Materials Ethenone = new MaterialBuilder(641, TextureSet.SET_FLUID , "Ethenone").addCell().addGas().setRGB(20, 20, 70).setColor(Dyes.dyeBlack).setMaterialList(new MaterialStack(Carbon, 2), new MaterialStack(Hydrogen, 2), new MaterialStack(Oxygen, 1)).addElectrolyzerRecipe().constructMaterial(); + public static Materials Ethane = new MaterialBuilder(642, TextureSet.SET_FLUID , "Ethane").addCell().addGas().setRGB(200, 200, 255).setColor(Dyes.dyeLightBlue).setFuelType(MaterialBuilder.GAS).setFuelPower(168).setMaterialList(new MaterialStack(Carbon, 2), new MaterialStack(Hydrogen, 6)).addElectrolyzerRecipe().setCanBeCracked(true).constructMaterial(); + public static Materials Propane = new MaterialBuilder(643, TextureSet.SET_FLUID , "Propane").addCell().addGas().setRGB(250, 226, 80).setColor(Dyes.dyeYellow).setFuelType(MaterialBuilder.GAS).setFuelPower(232).setMaterialList(new MaterialStack(Carbon, 3), new MaterialStack(Hydrogen, 8)).addElectrolyzerRecipe().setCanBeCracked(true).constructMaterial(); + public static Materials Butane = new MaterialBuilder(644, TextureSet.SET_FLUID , "Butane").addCell().addGas().setRGB(182, 55, 30).setColor(Dyes.dyeOrange).setFuelType(MaterialBuilder.GAS).setFuelPower(296).setMaterialList(new MaterialStack(Carbon, 4), new MaterialStack(Hydrogen, 10)).addElectrolyzerRecipe().setCanBeCracked(true).constructMaterial(); + public static Materials Butene = new MaterialBuilder(645, TextureSet.SET_FLUID , "Butene").addCell().addGas().setRGB(207, 80, 5).setColor(Dyes.dyeOrange).setFuelType(MaterialBuilder.GAS).setFuelPower(256).setMaterialList(new MaterialStack(Carbon, 4), new MaterialStack(Hydrogen, 8)).addElectrolyzerRecipe().setCanBeCracked(true).constructMaterial(); + public static Materials Butadiene = new MaterialBuilder(646, TextureSet.SET_FLUID , "Butadiene").addCell().addGas().setRGB(232, 105, 0).setColor(Dyes.dyeOrange).setFuelType(MaterialBuilder.GAS).setFuelPower(206).setMaterialList(new MaterialStack(Carbon, 4), new MaterialStack(Hydrogen, 6)).addElectrolyzerRecipe().setCanBeCracked(true).constructMaterial(); + public static Materials RawStyreneButadieneRubber = new MaterialBuilder(634, TextureSet.SET_SHINY , "Raw Styrene-Butadiene Rubber").addDustItems().setRGB(84, 64, 61).setColor(Dyes.dyeGray).setMaterialList(new MaterialStack(Styrene, 1), new MaterialStack(Butadiene, 3)).constructMaterial(); + public static Materials StyreneButadieneRubber = new MaterialBuilder(635, TextureSet.SET_SHINY , "Styrene-Butadiene Rubber").addDustItems().addMetalItems().addToolHeadItems().addGearItems().setToolSpeed(3.0f).setDurability(128).setToolQuality(1).setRGB(33, 26, 24).setColor(Dyes.dyeBlack).setMaterialList(new MaterialStack(Styrene, 1), new MaterialStack(Butadiene, 3)).constructMaterial(); + public static Materials Toluene = new MaterialBuilder(647, TextureSet.SET_FLUID , "Toluene").addCell().setRGB(80, 29, 5).setColor(Dyes.dyeBrown).setFuelType(MaterialBuilder.GAS).setFuelPower(328).setMaterialList(new MaterialStack(Carbon, 7), new MaterialStack(Hydrogen, 8)).addElectrolyzerRecipe().constructMaterial(); + public static Materials Epichlorohydrin = new MaterialBuilder(648, TextureSet.SET_FLUID , "Epichlorohydrin").addCell().setRGB(80, 29, 5).setColor(Dyes.dyeBrown).setMaterialList(new MaterialStack(Carbon, 3), new MaterialStack(Hydrogen, 5), new MaterialStack(Chlorine, 1), new MaterialStack(Oxygen, 1)).addElectrolyzerRecipe().constructMaterial(); + public static Materials PolyvinylChloride = new MaterialBuilder(649, TextureSet.SET_DULL , "Polyvinyl Chloride").addDustItems().addMetalItems().addToolHeadItems().addGearItems().setToolSpeed(3.0f).setDurability(32).setToolQuality(1).setRGB(215, 230, 230).setColor(Dyes.dyeLightGray).setMaterialList(new MaterialStack(Carbon, 2), new MaterialStack(Hydrogen, 3), new MaterialStack(Chlorine, 1)).constructMaterial(); + public static Materials VinylChloride = new MaterialBuilder(650, TextureSet.SET_FLUID , "Vinyl Chloride").addCell().addGas().setRGB(225, 240, 240).setColor(Dyes.dyeLightGray).setMaterialList(new MaterialStack(Carbon, 2), new MaterialStack(Hydrogen, 3), new MaterialStack(Chlorine, 1)).addElectrolyzerRecipe().constructMaterial(); + public static Materials SulfurDioxide = new MaterialBuilder(651, TextureSet.SET_FLUID , "Sulfur Dioxide").addCell().addGas().setRGB(200, 200, 25).setColor(Dyes.dyeYellow).setMaterialList(new MaterialStack(Sulfur, 1), new MaterialStack(Oxygen, 2)).constructMaterial(); + public static Materials SulfurTrioxide = new MaterialBuilder(652, TextureSet.SET_FLUID , "Sulfur Trioxide").addCell().addGas().setGasTemperature(344).setRGB(160, 160, 20).setColor(Dyes.dyeYellow).setMaterialList(new MaterialStack(Sulfur, 1), new MaterialStack(Oxygen, 3)).addElectrolyzerRecipe().constructMaterial(); + public static Materials NitricAcid = new MaterialBuilder(653, TextureSet.SET_FLUID , "Nitric Acid").addCell().addFluid().setRGB(230, 226, 171).setMaterialList(new MaterialStack(Hydrogen, 1), new MaterialStack(Nitrogen, 1), new MaterialStack(Oxygen, 3)).addElectrolyzerRecipe().constructMaterial(); + public static Materials Dimethylhydrazine = new MaterialBuilder(654, TextureSet.SET_FLUID , "1,1-Dimethylhydrazine").addCell().addFluid().setRGB(0, 0, 85).setColor(Dyes.dyeBlue).setMaterialList(new MaterialStack(Carbon, 2), new MaterialStack(Hydrogen, 8), new MaterialStack(Nitrogen, 2)).addElectrolyzerRecipe().constructMaterial(); + public static Materials Chloramine = new MaterialBuilder(655, TextureSet.SET_FLUID , "Chloramine").addCell().addFluid().setRGB(63, 159, 128).setColor(Dyes.dyeCyan).setMaterialList(new MaterialStack(Nitrogen, 1), new MaterialStack(Hydrogen, 2), new MaterialStack(Chlorine, 1)).addElectrolyzerRecipe().constructMaterial(); + public static Materials Dimethylamine = new MaterialBuilder(656, TextureSet.SET_FLUID , "Dimethylamine").addCell().addGas().setRGB(85, 68, 105).setColor(Dyes.dyeGray).setMaterialList(new MaterialStack(Carbon, 2), new MaterialStack(Hydrogen, 7), new MaterialStack(Nitrogen, 1)).addElectrolyzerRecipe().constructMaterial(); + public static Materials DinitrogenTetroxide = new MaterialBuilder(657, TextureSet.SET_FLUID , "Dinitrogen Tetroxide").addCell().addGas().setRGB(0, 65, 132).setColor(Dyes.dyeBlue).setMaterialList(new MaterialStack(Nitrogen, 2), new MaterialStack(Oxygen, 4)).addElectrolyzerRecipe().constructMaterial(); + public static Materials NitricOxide = new MaterialBuilder(658, TextureSet.SET_FLUID , "Nitric Oxide").addCell().addGas().setRGB(125, 200, 240).setColor(Dyes.dyeCyan).setMaterialList(new MaterialStack(Nitrogen, 1), new MaterialStack(Oxygen, 1)).addElectrolyzerRecipe().constructMaterial(); + public static Materials Ammonia = new MaterialBuilder(659, TextureSet.SET_FLUID , "Ammonia").addCell().addGas().setRGB(63, 52, 128).setColor(Dyes.dyeBlue).setMaterialList(new MaterialStack(Nitrogen, 1), new MaterialStack(Hydrogen, 3)).addElectrolyzerRecipe().constructMaterial(); + public static Materials Dimethyldichlorosilane = new MaterialBuilder(663, TextureSet.SET_FLUID , "Dimethyldichlorosilane").addCell().addFluid().setRGB(68, 22, 80).setColor(Dyes.dyePurple).setMaterialList(new MaterialStack(Carbon, 2), new MaterialStack(Hydrogen, 6), new MaterialStack(Chlorine, 2), new MaterialStack(Silicon, 1)).addElectrolyzerRecipe().constructMaterial(); + public static Materials Chloromethane = new MaterialBuilder(664, TextureSet.SET_FLUID , "Chloromethane").addCell().addGas().setRGB(200, 44, 160).setColor(Dyes.dyeMagenta).setMaterialList(new MaterialStack(Carbon, 1), new MaterialStack(Hydrogen, 3), new MaterialStack(Chlorine, 1)).addElectrolyzerRecipe().constructMaterial(); + public static Materials PhosphorousPentoxide = new MaterialBuilder(665, TextureSet.SET_FLUID , "Phosphorous Pentoxide").addCell().addDustItems().setRGB(220, 220, 0).setColor(Dyes.dyeYellow).setMaterialList(new MaterialStack(Phosphorus, 4), new MaterialStack(Oxygen, 10)).addElectrolyzerRecipe().constructMaterial(); + public static Materials Tetrafluoroethylene = new MaterialBuilder(666, TextureSet.SET_FLUID , "Tetrafluoroethylene").addCell().addGas().setRGB(125, 125, 125).setColor(Dyes.dyeGray).setMaterialList(new MaterialStack(Carbon, 2), new MaterialStack(Fluorine, 4)).addElectrolyzerRecipe().constructMaterial(); + public static Materials HydrofluoricAcid = new MaterialBuilder(667, TextureSet.SET_FLUID , "Hydrofluoric Acid").setName("HydrofluoricAcid_GT5U").addCell().addFluid().setRGB(0, 136, 170).setColor(Dyes.dyeLightBlue).setMaterialList(new MaterialStack(Hydrogen, 1), new MaterialStack(Fluorine, 1)).addElectrolyzerRecipe().constructMaterial(); + public static Materials Chloroform = new MaterialBuilder(668, TextureSet.SET_FLUID , "Chloroform").addCell().addFluid().setRGB(137, 44, 160).setColor(Dyes.dyePurple).setMaterialList(new MaterialStack(Carbon, 1), new MaterialStack(Hydrogen, 1), new MaterialStack(Chlorine, 3)).addElectrolyzerRecipe().constructMaterial(); + public static Materials BisphenolA = new MaterialBuilder(669, TextureSet.SET_FLUID , "Bisphenol A").addCell().setRGB(212, 170, 0).setColor(Dyes.dyeBrown).setMaterialList(new MaterialStack(Carbon, 15), new MaterialStack(Hydrogen, 16), new MaterialStack(Oxygen, 2)).addElectrolyzerRecipe().constructMaterial(); + public static Materials AceticAcid = new MaterialBuilder(670, TextureSet.SET_FLUID , "Acetic Acid").addCell().addFluid().setRGB(200, 180, 160).setColor(Dyes.dyeWhite).setMaterialList(new MaterialStack(Carbon, 2), new MaterialStack(Hydrogen, 4), new MaterialStack(Oxygen, 2)).addElectrolyzerRecipe().constructMaterial(); + public static Materials CalciumAcetateSolution = new MaterialBuilder(671, TextureSet.SET_RUBY , "Calcium Acetate Solution").addCell().addFluid().setRGB(220, 200, 180).setColor(Dyes.dyeCyan).setMaterialList(new MaterialStack(Calcium, 1), new MaterialStack(Carbon, 4), new MaterialStack(Oxygen, 4), new MaterialStack(Hydrogen, 6)).addElectrolyzerRecipe().constructMaterial(); + public static Materials Acetone = new MaterialBuilder(672, TextureSet.SET_FLUID , "Acetone").addCell().addFluid().setRGB(175, 175, 175).setColor(Dyes.dyeWhite).setMaterialList(new MaterialStack(Carbon, 3), new MaterialStack(Hydrogen, 6), new MaterialStack(Oxygen, 1)).addElectrolyzerRecipe().constructMaterial(); + public static Materials Methanol = new MaterialBuilder(673, TextureSet.SET_FLUID , "Methanol").addCell().addFluid().setRGB(170, 136, 0).setColor(Dyes.dyeBrown).setFuelPower(84).setMaterialList(new MaterialStack(Carbon, 1), new MaterialStack(Hydrogen, 4), new MaterialStack(Oxygen, 1)).addElectrolyzerRecipe().constructMaterial(); + public static Materials CarbonMonoxide = new MaterialBuilder(674, TextureSet.SET_FLUID , "Carbon Monoxide").addCell().addGas().setRGB(14, 72, 128).setColor(Dyes.dyeBrown).setFuelType(MaterialBuilder.GAS).setFuelPower(24).setMaterialList(new MaterialStack(Carbon, 1), new MaterialStack(Oxygen, 1)).addElectrolyzerRecipe().constructMaterial(); + public static Materials MetalMixture = new MaterialBuilder(676, TextureSet.SET_METALLIC , "Metal Mixture").addDustItems().setRGB(80, 45, 22).setColor(Dyes.dyeBrown).constructMaterial(); + public static Materials Ethylene = new MaterialBuilder(677, TextureSet.SET_FLUID , "Ethylene").addCell().addGas().setRGB(225, 225, 225).setColor(Dyes.dyeWhite).setFuelType(MaterialBuilder.GAS).setFuelPower(128).setMaterialList(new MaterialStack(Carbon, 2), new MaterialStack(Hydrogen, 4)).addElectrolyzerRecipe().setCanBeCracked(true).constructMaterial(); + public static Materials Propene = new MaterialBuilder(678, TextureSet.SET_FLUID , "Propene").addCell().addGas().setRGB(255, 221, 85).setColor(Dyes.dyeYellow).setFuelType(MaterialBuilder.GAS).setFuelPower(192).setMaterialList(new MaterialStack(Carbon, 3), new MaterialStack(Hydrogen, 6)).addElectrolyzerRecipe().setCanBeCracked(true).constructMaterial(); + public static Materials VinylAcetate = new MaterialBuilder(679, TextureSet.SET_FLUID , "Vinyl Acetate").addCell().addFluid().setRGB(255, 179, 128).setColor(Dyes.dyeOrange).setMaterialList(new MaterialStack(Carbon, 4), new MaterialStack(Hydrogen, 6), new MaterialStack(Oxygen, 2)).addElectrolyzerRecipe().constructMaterial(); + public static Materials PolyvinylAcetate = new MaterialBuilder(680, TextureSet.SET_FLUID , "Polyvinyl Acetate").addCell().addFluid().setRGB(255, 153, 85).setColor(Dyes.dyeOrange).setMaterialList(new MaterialStack(Carbon, 4), new MaterialStack(Hydrogen, 6), new MaterialStack(Oxygen, 2)).constructMaterial(); + public static Materials MethylAcetate = new MaterialBuilder(681, TextureSet.SET_FLUID , "Methyl Acetate").addCell().addFluid().setRGB(238, 198, 175).setColor(Dyes.dyeOrange).setMaterialList(new MaterialStack(Carbon, 3), new MaterialStack(Hydrogen, 6), new MaterialStack(Oxygen, 2)).addElectrolyzerRecipe().constructMaterial(); + public static Materials AllylChloride = new MaterialBuilder(682, TextureSet.SET_FLUID , "Allyl Chloride").addCell().addFluid().setRGB(135, 222, 170).setColor(Dyes.dyeCyan).setMaterialList(new MaterialStack(Carbon, 3), new MaterialStack(Hydrogen, 5), new MaterialStack(Chlorine, 1)).addElectrolyzerRecipe().constructMaterial(); + public static Materials HydrochloricAcid = new MaterialBuilder(683, TextureSet.SET_FLUID , "Hydrochloric Acid").setName("HydrochloricAcid_GT5U").addCell().addFluid().setRGB(183, 200, 196).setColor(Dyes.dyeLightGray).setMaterialList(new MaterialStack(Hydrogen, 1), new MaterialStack(Chlorine, 1)).constructMaterial(); + public static Materials HypochlorousAcid = new MaterialBuilder(684, TextureSet.SET_FLUID , "Hypochlorous Acid").addCell().addFluid().setRGB(111, 138, 145).setColor(Dyes.dyeGray).setMaterialList(new MaterialStack(Hydrogen, 1), new MaterialStack(Chlorine, 1), new MaterialStack(Oxygen, 1)).addElectrolyzerRecipe().constructMaterial(); + public static Materials SodiumOxide = new MaterialBuilder(744, TextureSet.SET_DULL , "Sodium Oxide").setName("SodiumOxide").addDustItems().setRGB(255, 255, 235).setColor(Dyes.dyeWhite).setMaterialList(new MaterialStack(Sodium, 2), new MaterialStack(Oxygen, 1)).addElectrolyzerRecipe().constructMaterial(); + public static Materials SodiumHydroxide = new MaterialBuilder(685, TextureSet.SET_DULL , "Sodium Hydroxide").setName("SodiumHydroxide_GT5U").addDustItems().setRGB(0, 51, 128).setColor(Dyes.dyeBlue).setMaterialList(new MaterialStack(Sodium, 1), new MaterialStack(Oxygen, 1), new MaterialStack(Hydrogen, 1)).constructMaterial(); + public static Materials Benzene = new MaterialBuilder(686, TextureSet.SET_FLUID , "Benzene").addCell().addFluid().setRGB(26, 26, 26).setColor(Dyes.dyeGray).setFuelType(MaterialBuilder.GAS).setFuelPower(360).setMaterialList(new MaterialStack(Carbon, 6), new MaterialStack(Hydrogen, 6)).addElectrolyzerRecipe().constructMaterial(); + public static Materials Phenol = new MaterialBuilder(687, TextureSet.SET_FLUID , "Phenol").addCell().addFluid().setRGB(120, 68, 33).setColor(Dyes.dyeBrown).setFuelType(MaterialBuilder.GAS).setFuelPower(288).setMaterialList(new MaterialStack(Carbon, 6), new MaterialStack(Hydrogen, 6), new MaterialStack(Oxygen, 1)).addElectrolyzerRecipe().constructMaterial(); + public static Materials Cumene = new MaterialBuilder(688, TextureSet.SET_FLUID , "Isopropylbenzene").addCell().addFluid().setRGB(85, 34, 0).setColor(Dyes.dyeBrown).setMaterialList(new MaterialStack(Carbon, 9), new MaterialStack(Hydrogen, 12)).addElectrolyzerRecipe().constructMaterial(); + public static Materials PhosphoricAcid = new MaterialBuilder(689, TextureSet.SET_FLUID , "Phosphoric Acid").setName("PhosphoricAcid_GT5U").addCell().addFluid().setRGB(220, 220, 0).setColor(Dyes.dyeYellow).setMaterialList(new MaterialStack(Hydrogen, 3), new MaterialStack(Phosphorus, 1), new MaterialStack(Oxygen, 4)).addElectrolyzerRecipe().constructMaterial(); + public static Materials SaltWater = new MaterialBuilder(692, TextureSet.SET_FLUID , "Salt Water").addCell().addFluid().setRGB(0, 0, 200).setColor(Dyes.dyeBlue).constructMaterial(); + public static Materials IronIIIChloride = new MaterialBuilder(693, TextureSet.SET_FLUID , "Iron III Chloride").setName("IronIIIChloride").addCell().addFluid().setRGB(22, 21, 14).setColor(Dyes.dyeBlack).setMaterialList(new MaterialStack(Chlorine, 3), new MaterialStack(Iron, 1)).addElectrolyzerRecipe().constructMaterial(); + public static Materials LifeEssence = new MaterialBuilder(694, TextureSet.SET_FLUID , "Life").setName("lifeessence").addCell().addFluid().setFuelPower(100).setFuelType(5).setRGB(110, 3, 3).setColor(Dyes.dyeRed).setMaterialList().constructMaterial(); + + //Roasted Ore Dust + public static Materials RoastedCopper = new MaterialBuilder(546, TextureSet.SET_DULL , "Roasted Copper").setName("RoastedCopper").addDustItems().setRGB(77, 18, 18).constructMaterial(); + public static Materials RoastedAntimony = new MaterialBuilder(547, TextureSet.SET_DULL , "Roasted Antimony").setName("RoastedAntimony").addDustItems().setRGB(196, 178, 194).constructMaterial(); + public static Materials RoastedIron = new MaterialBuilder(548, TextureSet.SET_DULL , "Roasted Iron").setName("RoastedIron").addDustItems().setRGB(148, 98, 98).addOreItems().constructMaterial(); + public static Materials RoastedNickel = new MaterialBuilder(549, TextureSet.SET_METALLIC, "Roasted Nickel").setName("RoastedNickel").addDustItems().setRGB(70, 140, 45).addOreItems().constructMaterial(); + public static Materials RoastedZinc = new MaterialBuilder(550, TextureSet.SET_DULL , "Roasted Zinc").setName("RoastedZinc").addDustItems().setRGB(209, 209, 209).constructMaterial(); + public static Materials RoastedCobalt = new MaterialBuilder(551, TextureSet.SET_METALLIC, "Roasted Cobalt").setName("RoastedCobalt").addDustItems().setRGB(8, 64, 9).constructMaterial(); + public static Materials RoastedArsenic = new MaterialBuilder(552, TextureSet.SET_SHINY , "Roasted Arsenic").setName("RoastedArsenic").addDustItems().setRGB(240, 240, 240).constructMaterial(); + public static Materials RoastedLead = new MaterialBuilder(553, TextureSet.SET_SHINY , "Roasted Lead").setName("RoastedLead").addDustItems().setRGB(168, 149, 43).constructMaterial(); + + //Silicon Line + public static Materials SiliconSG = new Materials( 856, TextureSet.SET_METALLIC , 1.0F, 0, 2, 1|2 |8 |32 , 80, 80, 100, 0, "SiliconSolarGrade" , "Silicon Solar Grade (Poly SI)" , 0, 0, 2273, 2273, true, false, 1, 1, 1, Dyes.dyeBlack , Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 4), new TC_AspectStack(TC_Aspects.TENEBRAE, 2))).disableAutoGeneratedBlastFurnaceRecipes(); + public static Materials CalciumDisilicide = new Materials( 971, TextureSet.SET_METALLIC , 1.0F, 0, 2, 1 |8 , 180, 180, 180, 0, "CalciumDisilicide" , "Calcium Disilicide" , 0, 0, 1313, -1, false, false, 1, 1, 1, Dyes.dyeGray ,1 , Arrays.asList(new MaterialStack(Calcium, 1), new MaterialStack(Silicon, 2)), Arrays.asList(new TC_AspectStack(TC_Aspects.TERRA, 1), new TC_AspectStack(TC_Aspects.ORDO, 1)));//CaSi2 + public static Materials SiliconTetrafluoride = new MaterialBuilder( 967, TextureSet.SET_FLUID , "Silicon Tetrafluoride" ).setName("SiliconTetrafluoride").addCell().addGas().setTransparent(true).setRGB(200, 200, 200).setColor(Dyes.dyeWhite).setMeltingPoint(178).setMaterialList(new MaterialStack(Silicon, 1), new MaterialStack(Fluorine, 4)).setAspects(Arrays.asList(new TC_AspectStack(TC_Aspects.AQUA, 1), new TC_AspectStack(TC_Aspects.VENENUM, 1))).constructMaterial();//SIF4 + public static Materials SiliconTetrachloride = new MaterialBuilder( 968, TextureSet.SET_FLUID , "Silicon Tetrachloride").setName("SiliconTetrachloride").addCell().addFluid().setRGB(220, 220, 220).setColor(Dyes.dyeWhite).setMeltingPoint(204).setMaterialList(new MaterialStack(Silicon, 1), new MaterialStack(Chlorine, 4)).setAspects(Arrays.asList(new TC_AspectStack(TC_Aspects.AQUA, 1), new TC_AspectStack(TC_Aspects.VENENUM, 1))).constructMaterial();//SICL4 + public static Materials Trichlorosilane = new MaterialBuilder( 972, TextureSet.SET_FLUID , "Trichlorosilane" ).setName("Trichlorosilane" ).addCell().addFluid().setRGB( 255, 255, 255).setColor(Dyes.dyeWhite).setMeltingPoint(139).setMaterialList(new MaterialStack(Hydrogen, 1), new MaterialStack(Silicon, 1), new MaterialStack(Chlorine, 3)).setAspects(Arrays.asList(new TC_AspectStack(TC_Aspects.AQUA, 1), new TC_AspectStack(TC_Aspects.VENENUM, 1))).constructMaterial();//HSICL3 + public static Materials Hexachlorodisilane = new MaterialBuilder( 973, TextureSet.SET_FLUID , "Hexachlorodisilane").setName("Hexachlorodisilane" ).addCell().addFluid().setRGB( 255, 255, 255).setColor(Dyes.dyeWhite).setMeltingPoint(272).setExtraData(1).setMaterialList(new MaterialStack(Silicon, 2), new MaterialStack(Chlorine, 6)).setAspects(Collections.singletonList(new TC_AspectStack(TC_Aspects.AQUA, 1))).constructMaterial();//SI2CL6 + public static Materials Dichlorosilane = new MaterialBuilder( 799, TextureSet.SET_FLUID , "Dichlorosilane").setName("Dichlorosilane").addCell().addGas().setTransparent(true).setRGB( 255, 255, 255).setColor(Dyes.dyeWhite).setMeltingPoint(151).setExtraData(1).setMaterialList(new MaterialStack(Silicon, 1), new MaterialStack(Hydrogen, 2), new MaterialStack(Chlorine, 2)).setAspects(Arrays.asList(new TC_AspectStack(TC_Aspects.AQUA, 1), new TC_AspectStack(TC_Aspects.VENENUM, 1))).constructMaterial();//SIH2CL2 + public static Materials Silane = new MaterialBuilder( 798, TextureSet.SET_FLUID , "Silane").setName( "Silane").addCell().addGas().setRGB( 255, 255, 255).setColor(Dyes.dyeWhite).setMeltingPoint(88).setMaterialList(new MaterialStack(Silicon, 1), new MaterialStack(Hydrogen, 4)).setAspects(Collections.singletonList(new TC_AspectStack(TC_Aspects.AQUA, 1))).constructMaterial();//SIH4 + public static Materials Calciumhydride = new Materials( 797, TextureSet.SET_METALLIC , 1.0F, 0, 2, 1 |8 , 220, 220, 220, 0, "CalciumHydride" , "Calcium Hydride" , 0, 0, 1089, -1, false, false, 1, 1, 1, Dyes.dyeGray ,1 , Arrays.asList(new MaterialStack(Calcium, 1), new MaterialStack(Hydrogen, 2)), Arrays.asList(new TC_AspectStack(TC_Aspects.TERRA, 1), new TC_AspectStack(TC_Aspects.ORDO, 1)));//CaH2 + public static Materials AluminiumFluoride = new Materials( 969, TextureSet.SET_METALLIC , 1.0F, 0, 2, 1 |8 , 255, 255, 255, 0, "Aluminiumfluoride" , "Aluminium Fluoride" , 0, 0, 1533, -1, false, false, 1, 1, 1, Dyes.dyeWhite ,1 , Arrays.asList(new MaterialStack(Aluminium, 1), new MaterialStack(Fluorine, 3)), Arrays.asList(new TC_AspectStack(TC_Aspects.TERRA, 1), new TC_AspectStack(TC_Aspects.ORDO, 1)));//ALF3 + + public static Materials SolderingAlloy = new Materials( 314, TextureSet.SET_DULL , 1.0F, 0, 1, 1|2 , 220, 220, 230, 0, "SolderingAlloy" , "Soldering Alloy" , 0, 0, 400, 400, false, false, 1, 1, 1, Dyes.dyeWhite , 2, Arrays.asList(new MaterialStack(Tin, 9), new MaterialStack(Antimony, 1))); + public static Materials GalliumArsenide = new Materials( 980, TextureSet.SET_DULL , 1.0F, 0, 1, 1|2 , 160, 160, 160, 0, "GalliumArsenide" , "Gallium Arsenide" , 0, 0, -1, 1200, true, false, 1, 1, 1, Dyes.dyeGray , 2, Arrays.asList(new MaterialStack(Arsenic, 1), new MaterialStack(Gallium, 1))); + public static Materials IndiumGalliumPhosphide = new Materials( 981, TextureSet.SET_DULL , 1.0F, 0, 1, 1|2 , 160, 140, 190, 0, "IndiumGalliumPhosphide" , "Indium Gallium Phosphide" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeLightGray , 2, Arrays.asList(new MaterialStack(Indium, 1), new MaterialStack(Gallium, 1), new MaterialStack(Phosphorus, 1))); + public static Materials Spessartine = new Materials( 838, TextureSet.SET_DULL , 1.0F, 0, 2, 1 |8 , 255, 100, 100, 0, "Spessartine" , "Spessartine" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes.dyeRed , 0, Arrays.asList(new MaterialStack(Aluminium, 2), new MaterialStack(Manganese, 3), new MaterialStack(Silicon, 3), new MaterialStack(Oxygen, 12))); + public static Materials Sphalerite = new Materials( 839, TextureSet.SET_DULL , 1.0F, 0, 1, 1 |8 , 255, 255, 255, 0, "Sphalerite" , "Sphalerite" , 0, 0, -1, 0, false, false, 2, 1, 1, Dyes.dyeYellow , 1, Arrays.asList(new MaterialStack(Zinc, 1), new MaterialStack(Sulfur, 1))); + public static Materials StainlessSteel = new Materials( 306, TextureSet.SET_SHINY , 7.0F, 480, 4, 1|2 |64|128 , 200, 200, 220, 0, "StainlessSteel" , "Stainless Steel" , 0, 0, -1, 1700, true, false, 1, 1, 1, Dyes.dyeWhite , 1, Arrays.asList(new MaterialStack(Iron, 6), new MaterialStack(Chrome, 1), new MaterialStack(Manganese, 1), new MaterialStack(Nickel, 1))); + public static Materials Steel = new Materials( 305, TextureSet.SET_METALLIC , 6.0F, 512, 3, 1|2 |64|128 , 128, 128, 128, 0, "Steel" , "Steel" , 0, 0, 1811, 1000, true, false, 4, 51, 50, Dyes.dyeGray , 1, Arrays.asList(new MaterialStack(Iron, 50), new MaterialStack(Carbon, 1)), Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.ORDO, 1))); + public static Materials Stibnite = new Materials( 945, TextureSet.SET_METALLIC , 1.0F, 0, 2, 1 |8 , 70, 70, 70, 0, "Stibnite" , "Stibnite" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeWhite , 2, Arrays.asList(new MaterialStack(Antimony, 2), new MaterialStack(Sulfur, 3))); + public static Materials SulfuricAcid = new Materials( 720, TextureSet.SET_FLUID , 1.0F, 0, 2, 16 , 255, 128, 0, 0, "SulfuricAcid" , "Sulfuric Acid" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeOrange , 1, Arrays.asList(new MaterialStack(Hydrogen, 2), new MaterialStack(Sulfur, 1), new MaterialStack(Oxygen, 4))); + public static Materials Tanzanite = new Materials( 508, TextureSet.SET_GEM_VERTICAL , 7.0F, 256, 2, 1 |4|8 |64 , 64, 0, 200, 127, "Tanzanite" , "Tanzanite" , 0, 0, -1, 0, false, true, 5, 1, 1, Dyes.dyePurple , 0, Arrays.asList(new MaterialStack(Calcium, 2), new MaterialStack(Aluminium, 3), new MaterialStack(Silicon, 3), new MaterialStack(Hydrogen, 1), new MaterialStack(Oxygen, 13)), Arrays.asList(new TC_AspectStack(TC_Aspects.LUCRUM, 5), new TC_AspectStack(TC_Aspects.VITREUS, 3))); + public static Materials Tetrahedrite = new Materials( 840, TextureSet.SET_DULL , 1.0F, 0, 2, 1 |8 , 200, 32, 0, 0, "Tetrahedrite" , "Tetrahedrite" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes.dyeRed , 2, Arrays.asList(new MaterialStack(Copper, 3), new MaterialStack(Antimony, 1), new MaterialStack(Sulfur, 3), new MaterialStack(Iron, 1))); //Cu3SbS3 + x(Fe,Zn)6Sb2S9 + public static Materials TinAlloy = new Materials( 363, TextureSet.SET_METALLIC , 6.5F, 96, 2, 1|2 |64|128 , 200, 200, 200, 0, "TinAlloy" , "Tin Alloy" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeWhite , 2, Arrays.asList(new MaterialStack(Tin, 1), new MaterialStack(Iron, 1)), Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.INSTRUMENTUM, 1))); + public static Materials Topaz = new Materials( 507, TextureSet.SET_GEM_HORIZONTAL , 7.0F, 256, 3, 1 |4|8 |64 , 255, 128, 0, 127, "Topaz" , "Topaz" , 0, 0, -1, 0, false, true, 5, 1, 1, Dyes.dyeOrange , 0, Arrays.asList(new MaterialStack(Aluminium, 2), new MaterialStack(Silicon, 1), new MaterialStack(Fluorine, 2), new MaterialStack(Hydrogen, 2), new MaterialStack(Oxygen, 6)), Arrays.asList(new TC_AspectStack(TC_Aspects.LUCRUM, 6), new TC_AspectStack(TC_Aspects.VITREUS, 4))); + public static Materials Tungstate = new Materials( 841, TextureSet.SET_DULL , 1.0F, 0, 3, 1 |8 , 55, 50, 35, 0, "Tungstate" , "Tungstate" , 0, 0, 2500, 2500, true, false, 4, 1, 1, Dyes.dyeBlack , 0, Arrays.asList(new MaterialStack(Tungsten, 1), new MaterialStack(Lithium, 2), new MaterialStack(Oxygen, 4))); + public static Materials Ultimet = new Materials( 344, TextureSet.SET_SHINY , 9.0F, 2048, 4, 1|2 |64|128 , 180, 180, 230, 0, "Ultimet" , "Ultimet" , 0, 0, 2700, 2700, true, false, 1, 1, 1, Dyes.dyeLightBlue , 1, Arrays.asList(new MaterialStack(Cobalt, 5), new MaterialStack(Chrome, 2), new MaterialStack(Nickel, 1), new MaterialStack(Molybdenum, 1))); // 54% Cobalt, 26% Chromium, 9% Nickel, 5% Molybdenum, 3% Iron, 2% Tungsten, 0.8% Manganese, 0.3% Silicon, 0.08% Nitrogen and 0.06% Carbon + public static Materials Uraninite = new Materials( 922, TextureSet.SET_METALLIC , 1.0F, 0, 3, 1 |8 , 35, 35, 35, 0, "Uraninite" , "Uraninite" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeLime , 2, Arrays.asList(new MaterialStack(Uranium, 1), new MaterialStack(Oxygen, 2))); + public static Materials Uvarovite = new Materials( 842, TextureSet.SET_DIAMOND , 1.0F, 0, 2, 1 |8 , 180, 255, 180, 0, "Uvarovite" , "Uvarovite" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes.dyeGreen , 1, Arrays.asList(new MaterialStack(Calcium, 3), new MaterialStack(Chrome, 2), new MaterialStack(Silicon, 3), new MaterialStack(Oxygen, 12))); + public static Materials VanadiumGallium = new Materials( 357, TextureSet.SET_SHINY , 1.0F, 0, 2, 1|2 |128 , 128, 128, 140, 0, "VanadiumGallium" , "Vanadium-Gallium" , 0, 0, 4500, 4500, true, false, 1, 1, 1, Dyes.dyeGray , 2, Arrays.asList(new MaterialStack(Vanadium, 3), new MaterialStack(Gallium, 1))).disableAutoGeneratedBlastFurnaceRecipes().disableAutoGeneratedVacuumFreezerRecipe(); + public static Materials Wood = new Materials( 809, TextureSet.SET_WOOD , 2.0F, 16, 0, 1|2 |64|128 , 100, 50, 0, 0, "Wood" , "Wood" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeBrown , 0, Arrays.asList(new MaterialStack(Carbon, 1), new MaterialStack(Oxygen, 1), new MaterialStack(Hydrogen, 1)), Collections.singletonList(new TC_AspectStack(TC_Aspects.ARBOR, 2))); + public static Materials WroughtIron = new Materials( 304, TextureSet.SET_METALLIC , 6.0F, 384, 2, 1|2 |64|128 , 200, 180, 180, 0, "WroughtIron" , "Wrought Iron" , 0, 0, 1811, 0, false, false, 3, 1, 1, Dyes.dyeLightGray , 2, Collections.singletonList(new MaterialStack(Iron, 1))); + public static Materials Wulfenite = new Materials( 882, TextureSet.SET_DULL , 1.0F, 0, 3, 1 |8 , 255, 128, 0, 0, "Wulfenite" , "Wulfenite" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeOrange , 2, Arrays.asList(new MaterialStack(Lead, 1), new MaterialStack(Molybdenum, 1), new MaterialStack(Oxygen, 4))); + public static Materials YellowLimonite = new Materials( 931, TextureSet.SET_METALLIC , 1.0F, 0, 2, 1 |8 , 200, 200, 0, 0, "YellowLimonite" , "Yellow Limonite" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeYellow , 2, Arrays.asList(new MaterialStack(Iron, 1), new MaterialStack(Hydrogen, 1), new MaterialStack(Oxygen, 2))); // FeO(OH) + a bit of Ni and Co + public static Materials YttriumBariumCuprate = new Materials( 358, TextureSet.SET_METALLIC , 1.0F, 0, 2, 1|2 , 80, 64, 70, 0, "YttriumBariumCuprate" , "Yttrium Barium Cuprate" , 0, 0, 4500, 4500, true, false, 1, 1, 1, Dyes.dyeGray , 0, Arrays.asList(new MaterialStack(Yttrium, 1), new MaterialStack(Barium, 2), new MaterialStack(Copper, 3), new MaterialStack(Oxygen, 7))).disableAutoGeneratedVacuumFreezerRecipe(); + + /** + * Second Degree Compounds + */ + public static Materials WoodSealed = new Materials( 889, TextureSet.SET_WOOD , 3.0F, 24, 0, 1|2 |64|128 , 80, 40, 0, 0, "WoodSealed" , "Sealed Wood" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeBrown , 0, Collections.singletonList(new MaterialStack(Wood, 1)), Arrays.asList(new TC_AspectStack(TC_Aspects.ARBOR, 2), new TC_AspectStack(TC_Aspects.FABRICO, 1))); + public static Materials LiveRoot = new Materials( 832, TextureSet.SET_WOOD , 1.0F, 0, 1, 1 , 220, 200, 0, 0, "LiveRoot" , "Liveroot" , 5, 16, -1, 0, false, false, 2, 4, 3, Dyes.dyeBrown , 2, Arrays.asList(new MaterialStack(Wood, 3), new MaterialStack(Magic, 1)), Arrays.asList(new TC_AspectStack(TC_Aspects.ARBOR, 2), new TC_AspectStack(TC_Aspects.VICTUS, 2), new TC_AspectStack(TC_Aspects.PRAECANTATIO, 1))); + public static Materials IronWood = new Materials( 338, TextureSet.SET_WOOD , 6.0F, 384, 2, 1|2 |64|128 , 150, 140, 110, 0, "IronWood" , "Ironwood" , 5, 8, -1, 0, false, false, 2, 19, 18, Dyes.dyeBrown , 2, Arrays.asList(new MaterialStack(Iron, 9), new MaterialStack(LiveRoot, 9), new MaterialStack(Gold, 1))); + public static Materials Glass = new Materials( 890, TextureSet.SET_GLASS , 1.0F, 4, 0, 1 |4 , 250, 250, 250, 220, "Glass" , "Glass" , 0, 0, 1500, 0, false, true, 1, 1, 1, Dyes.dyeWhite , 2, Collections.singletonList(new MaterialStack(SiliconDioxide, 1)), Collections.singletonList(new TC_AspectStack(TC_Aspects.VITREUS, 2))); + public static Materials BorosilicateGlass = new MaterialBuilder(611, TextureSet.SET_GLASS , "Borosilicate Glass").addDustItems().addMetalItems().setRGB(230, 243, 230).setColor(Dyes.dyeWhite).setMaterialList(new MaterialStack(Boron, 1), new MaterialStack(Glass, 7)).addCentrifugeRecipe().constructMaterial(); + public static Materials Perlite = new Materials( 925, TextureSet.SET_DULL , 1.0F, 0, 1, 1 |8 , 30, 20, 30, 0, "Perlite" , "Perlite" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeBlack , 2, Arrays.asList(new MaterialStack(Obsidian, 2), new MaterialStack(Water, 1))); + public static Materials Borax = new Materials( 941, TextureSet.SET_FINE , 1.0F, 0, 1, 1 |8 , 250, 250, 250, 0, "Borax" , "Borax" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeWhite , 1, Arrays.asList(new MaterialStack(Sodium, 2), new MaterialStack(Boron, 4), new MaterialStack(Oxygen, 7), new MaterialStack(Water, 10))); + public static Materials Lignite = new Materials( 538, TextureSet.SET_LIGNITE , 1.0F, 0, 0, 1 |4|8 , 100, 70, 70, 0, "Lignite" , "Lignite Coal" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeBlack , 1, Arrays.asList(new MaterialStack(Carbon, 3), new MaterialStack(Water, 1))); + public static Materials Olivine = new Materials( 505, TextureSet.SET_RUBY , 7.0F, 256, 2, 1 |4|8 |64 , 150, 255, 150, 127, "Olivine" , "Olivine" , 0, 0, -1, 0, false, true, 5, 1, 1, Dyes.dyeLime , 1, Arrays.asList(new MaterialStack(Magnesium, 2), new MaterialStack(Iron, 1), new MaterialStack(SiliconDioxide, 2)), Arrays.asList(new TC_AspectStack(TC_Aspects.LUCRUM, 4), new TC_AspectStack(TC_Aspects.VITREUS, 2))); + public static Materials Opal = new Materials( 510, TextureSet.SET_OPAL , 7.0F, 256, 2, 1 |4|8 |64 , 0, 0, 255, 0, "Opal" , "Opal" , 0, 0, -1, 0, false, true, 3, 1, 1, Dyes.dyeBlue , 1, Collections.singletonList(new MaterialStack(SiliconDioxide, 1)), Arrays.asList(new TC_AspectStack(TC_Aspects.LUCRUM, 5), new TC_AspectStack(TC_Aspects.VITREUS, 3))); + public static Materials Amethyst = new Materials( 509, TextureSet.SET_FLINT , 7.0F, 256, 3, 1 |4|8 |64 , 210, 50, 210, 127, "Amethyst" , "Amethyst" , 0, 0, -1, 0, false, true, 3, 1, 1, Dyes.dyePink , 1, Arrays.asList(new MaterialStack(SiliconDioxide, 4), new MaterialStack(Iron, 1)), Arrays.asList(new TC_AspectStack(TC_Aspects.LUCRUM, 6), new TC_AspectStack(TC_Aspects.VITREUS, 4))); + public static Materials Redstone = new Materials( 810, TextureSet.SET_ROUGH , 1.0F, 0, 2, 1 |8 , 200, 0, 0, 0, "Redstone" , "Redstone" , 0, 0, 500, 0, false, false, 3, 1, 1, Dyes.dyeRed , 2, Arrays.asList(new MaterialStack(Silicon, 1), new MaterialStack(Pyrite, 5), new MaterialStack(Ruby, 1), new MaterialStack(Mercury, 3)), Arrays.asList(new TC_AspectStack(TC_Aspects.MACHINA, 1), new TC_AspectStack(TC_Aspects.POTENTIA, 2))); + public static Materials Lapis = new Materials( 526, TextureSet.SET_LAPIS , 1.0F, 0, 1, 1 |4|8 , 70, 70, 220, 0, "Lapis" , "Lapis" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes.dyeBlue , 2, Arrays.asList(new MaterialStack(Lazurite, 12), new MaterialStack(Sodalite, 2), new MaterialStack(Pyrite, 1), new MaterialStack(Calcite, 1)), Collections.singletonList(new TC_AspectStack(TC_Aspects.SENSUS, 1))); + public static Materials Blaze = new Materials( 801, TextureSet.SET_POWDER , 2.0F, 16, 1, 1 |64 , 255, 200, 0, 0, "Blaze" , "Blaze" , 0, 0, 6400, 0, false, false, 2, 3, 2, Dyes.dyeYellow , 2, Arrays.asList(new MaterialStack(DarkAsh, 1), new MaterialStack(Sulfur, 1), new MaterialStack(Magic, 1)), Arrays.asList(new TC_AspectStack(TC_Aspects.PRAECANTATIO, 2), new TC_AspectStack(TC_Aspects.IGNIS, 4))); + public static Materials EnderPearl = new Materials( 532, TextureSet.SET_SHINY , 1.0F, 16, 1, 1 |4 , 108, 220, 200, 0, "EnderPearl" , "Enderpearl" , 0, 0, -1, 0, false, false, 1, 16, 10, Dyes.dyeGreen , 1, Arrays.asList(new MaterialStack(Beryllium, 1), new MaterialStack(Potassium, 4), new MaterialStack(Nitrogen, 5), new MaterialStack(Magic, 6)), Arrays.asList(new TC_AspectStack(TC_Aspects.ALIENIS, 4), new TC_AspectStack(TC_Aspects.ITER, 4), new TC_AspectStack(TC_Aspects.PRAECANTATIO, 2))); + public static Materials EnderEye = new Materials( 533, TextureSet.SET_SHINY , 1.0F, 16, 1, 1 |4 , 160, 250, 230, 0, "EnderEye" , "Endereye" , 5, 10, -1, 0, false, false, 1, 2, 1, Dyes.dyeGreen , 2, Arrays.asList(new MaterialStack(EnderPearl, 1), new MaterialStack(Blaze, 1)), Arrays.asList(new TC_AspectStack(TC_Aspects.SENSUS, 4), new TC_AspectStack(TC_Aspects.ALIENIS, 4), new TC_AspectStack(TC_Aspects.ITER, 4), new TC_AspectStack(TC_Aspects.PRAECANTATIO, 3), new TC_AspectStack(TC_Aspects.IGNIS, 2))); + public static Materials Flint = new Materials( 802, TextureSet.SET_FLINT , 2.5F, 128, 1, 1 |64 , 0, 32, 64, 0, "Flint" , "Flint" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeGray , 2, Collections.singletonList(new MaterialStack(SiliconDioxide, 1)), Arrays.asList(new TC_AspectStack(TC_Aspects.TERRA, 1), new TC_AspectStack(TC_Aspects.INSTRUMENTUM, 1))); + public static Materials Diatomite = new Materials( 948, TextureSet.SET_DULL , 1.0F, 0, 1, 1 |8 , 225, 225, 225, 0, "Diatomite" , "Diatomite" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeGray , 2, Arrays.asList(new MaterialStack(Flint, 8), new MaterialStack(BandedIron, 1), new MaterialStack(Sapphire, 1))); + public static Materials VolcanicAsh = new Materials( 940, TextureSet.SET_FLINT , 1.0F, 0, 0, 1 , 60, 50, 50, 0, "VolcanicAsh" , "Volcanic Ashes" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeBlack , 2, Arrays.asList(new MaterialStack(Flint, 6), new MaterialStack(Iron, 1), new MaterialStack(Magnesium, 1))); + public static Materials Niter = new Materials( 531, TextureSet.SET_FLINT , 1.0F, 0, 1, 1 |4|8 , 255, 200, 200, 0, "Niter" , "Niter" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyePink , 2, Collections.singletonList(new MaterialStack(Saltpeter, 1))); + public static Materials Pyrotheum = new Materials( 843, TextureSet.SET_FIERY , 1.0F, 0, 1, 1 , 255, 128, 0, 0, "Pyrotheum" , "Pyrotheum" , 2, 62, -1, 0, false, false, 2, 3, 1, Dyes.dyeYellow , 2, Arrays.asList(new MaterialStack(Coal, 1), new MaterialStack(Redstone, 1), new MaterialStack(Blaze, 1)), Arrays.asList(new TC_AspectStack(TC_Aspects.PRAECANTATIO, 2), new TC_AspectStack(TC_Aspects.IGNIS, 1))); + public static Materials Cryotheum = new Materials( 898, TextureSet.SET_SHINY , 1.0F, 0, 1, 1 , 0, 148, 203, 0, "Cryotheum" , "Cryotheum" , 2, 62, -1, 0, false, false, 2, 3, 1, Dyes.dyeLightBlue , 2, Arrays.asList(new MaterialStack(Saltpeter, 1), new MaterialStack(Redstone, 1), new MaterialStack(Snow, 1), new MaterialStack(Blizz, 1)), Arrays.asList(new TC_AspectStack(TC_Aspects.PRAECANTATIO, 2), new TC_AspectStack(TC_Aspects.ELECTRUM, 1), new TC_AspectStack(TC_Aspects.GELUM, 1))); + public static Materials HydratedCoal = new Materials( 818, TextureSet.SET_ROUGH , 1.0F, 0, 1, 1 , 70, 70, 100, 0, "HydratedCoal" , "Hydrated Coal" , 0, 0, -1, 0, false, false, 1, 9, 8, Dyes.dyeBlack , 2, Arrays.asList(new MaterialStack(Coal, 8), new MaterialStack(Water, 1))); + public static Materials Apatite = new Materials( 530, TextureSet.SET_DIAMOND , 1.0F, 0, 1, 1 |4|8 , 200, 200, 255, 0, "Apatite" , "Apatite" , 0, 0, -1, 0, false, false, 2, 1, 1, Dyes.dyeCyan , 1, Arrays.asList(new MaterialStack(Calcium, 5), new MaterialStack(Phosphate, 3), new MaterialStack(Chlorine, 1)), Collections.singletonList(new TC_AspectStack(TC_Aspects.MESSIS, 2))); + public static Materials Alumite = new Materials( 400, TextureSet.SET_METALLIC , 5.0F, 768, 2, 1|2 |64|128 , 255, 105, 180, 0, "Alumite" , "Alumite" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyePink , 2, Arrays.asList(new MaterialStack(Aluminium, 5), new MaterialStack(Steel, 2), new MaterialStack(Obsidian, 2)), Collections.singletonList(new TC_AspectStack(TC_Aspects.STRONTIO, 2))); + public static Materials Manyullyn = new Materials( 386, TextureSet.SET_SHINY , 25.0F, 2048, 5, 1|2 |8 |64|128 , 154, 76, 185, 0, "Manyullyn" , "Manyullyn" , 0, 0, 3600, 3600, true, false, 1, 1, 1, Dyes.dyePurple , 2, Arrays.asList(new MaterialStack(Cobalt, 1), new MaterialStack(Ardite, 1)), Collections.singletonList(new TC_AspectStack(TC_Aspects.STRONTIO, 2))).disableAutoGeneratedBlastFurnaceRecipes(); + public static Materials Steeleaf = new Materials( 339, TextureSet.SET_LEAF , 8.0F, 768, 3, 1|2 |64|128 , 50, 127, 50, 0, "Steeleaf" , "Steeleaf" , 5, 24, -1, 0, false, false, 4, 1, 1, Dyes.dyeGreen , 2, Arrays.asList(new MaterialStack(Steel, 1), new MaterialStack(Magic, 1)), Arrays.asList(new TC_AspectStack(TC_Aspects.HERBA, 2), new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.PRAECANTATIO, 1))); + public static Materials Knightmetal = new Materials( 362, TextureSet.SET_METALLIC , 8.0F, 1024, 3, 1|2 |64|128 , 210, 240, 200, 0, "Knightmetal" , "Knightmetal" , 5, 24, -1, 0, false, false, 4, 1, 1, Dyes.dyeLime , 2, Arrays.asList(new MaterialStack(Steel, 2), new MaterialStack(Magic, 1)), Arrays.asList(new TC_AspectStack(TC_Aspects.LUCRUM, 1), new TC_AspectStack(TC_Aspects.METALLUM, 2))); + public static Materials SterlingSilver = new Materials( 350, TextureSet.SET_SHINY , 13.0F, 128, 2, 1|2 |64|128 , 250, 220, 225, 0, "SterlingSilver" , "Sterling Silver" , 0, 0, -1, 1700, true, false, 4, 1, 1, Dyes.dyeWhite , 2, Arrays.asList(new MaterialStack(Copper, 1), new MaterialStack(Silver, 4))); + public static Materials RoseGold = new Materials( 351, TextureSet.SET_SHINY , 14.0F, 128, 2, 1|2 |64|128 , 255, 230, 30, 0, "RoseGold" , "Rose Gold" , 0, 0, -1, 1600, true, false, 4, 1, 1, Dyes.dyeOrange , 2, Arrays.asList(new MaterialStack(Copper, 1), new MaterialStack(Gold, 4))); + public static Materials BlackBronze = new Materials( 352, TextureSet.SET_DULL , 12.0F, 256, 2, 1|2 |64|128 , 100, 50, 125, 0, "BlackBronze" , "Black Bronze" , 0, 0, -1, 2000, true, false, 4, 1, 1, Dyes.dyePurple , 2, Arrays.asList(new MaterialStack(Gold, 1), new MaterialStack(Silver, 1), new MaterialStack(Copper, 3))); + public static Materials BismuthBronze = new Materials( 353, TextureSet.SET_DULL , 8.0F, 256, 2, 1|2 |64|128 , 100, 125, 125, 0, "BismuthBronze" , "Bismuth Bronze" , 0, 0, -1, 1100, true, false, 4, 1, 1, Dyes.dyeCyan , 2, Arrays.asList(new MaterialStack(Bismuth, 1), new MaterialStack(Zinc, 1), new MaterialStack(Copper, 3))); + public static Materials BlackSteel = new Materials( 334, TextureSet.SET_METALLIC , 6.5F, 768, 3, 1|2 |64|128 , 100, 100, 100, 0, "BlackSteel" , "Black Steel" , 0, 0, -1, 1200, true, false, 4, 1, 1, Dyes.dyeBlack , 2, Arrays.asList(new MaterialStack(Nickel, 1), new MaterialStack(BlackBronze, 1), new MaterialStack(Steel, 3))).disableAutoGeneratedBlastFurnaceRecipes(); + public static Materials RedSteel = new Materials( 348, TextureSet.SET_METALLIC , 7.0F, 896, 4, 1|2 |64|128 , 140, 100, 100, 0, "RedSteel" , "Red Steel" , 0, 0, -1, 1300, true, false, 4, 1, 1, Dyes.dyeRed , 2, Arrays.asList(new MaterialStack(SterlingSilver, 1), new MaterialStack(BismuthBronze, 1), new MaterialStack(Steel, 2), new MaterialStack(BlackSteel, 4))); + public static Materials BlueSteel = new Materials( 349, TextureSet.SET_METALLIC , 7.5F, 1024, 4, 1|2 |64|128 , 100, 100, 140, 0, "BlueSteel" , "Blue Steel" , 0, 0, -1, 1400, true, false, 4, 1, 1, Dyes.dyeBlue , 2, Arrays.asList(new MaterialStack(RoseGold, 1), new MaterialStack(Brass, 1), new MaterialStack(Steel, 2), new MaterialStack(BlackSteel, 4))); + public static Materials DamascusSteel = new Materials( 335, TextureSet.SET_METALLIC , 8.0F, 1280, 3, 1|2 |64|128 , 110, 110, 110, 0, "DamascusSteel" , "Damascus Steel" , 0, 0, 2000, 1500, true, false, 4, 1, 1, Dyes.dyeGray , 2, Collections.singletonList(new MaterialStack(Steel, 1))).disableAutoGeneratedBlastFurnaceRecipes(); + public static Materials TungstenSteel = new Materials( 316, TextureSet.SET_METALLIC , 8.0F, 2560, 4, 1|2 |64|128 , 100, 100, 160, 0, "TungstenSteel" , "Tungstensteel" , 0, 0, 4000, 4000, true, false, 4, 1, 1, Dyes.dyeBlue , 2, Arrays.asList(new MaterialStack(Steel, 1), new MaterialStack(Tungsten, 1))).disableAutoGeneratedBlastFurnaceRecipes().disableAutoGeneratedVacuumFreezerRecipe(); + public static Materials NitroCoalFuel = new Materials( -1, TextureSet.SET_FLUID , 1.0F, 0, 2, 16 , 50, 70, 50, 0, "NitroCoalFuel" , "Nitro-Coalfuel" , 0, 48, -1, 0, false, false, 1, 1, 1, Dyes.dyeBlack , 0, Arrays.asList(new MaterialStack(Glyceryl, 1), new MaterialStack(CoalFuel, 4))); + public static Materials NitroFuel = new Materials( 709, TextureSet.SET_FLUID , 1.0F, 0, 2, 16 , 200, 255, 0, 0, "NitroFuel" , "Cetane-Boosted Diesel" , 0, 1000, -1, 0, false, false, 1, 1, 1, Dyes.dyeLime ); + public static Materials RedAlloy = new Materials( 308, TextureSet.SET_DULL , 1.0F, 0, 0, 1|2 , 200, 0, 0, 0, "RedAlloy" , "Red Alloy" , 0, 0, 500, 0, false, false, 3, 5, 1, Dyes.dyeRed , 2, Arrays.asList(new MaterialStack(Copper, 1), new MaterialStack(Redstone, 4)), Collections.singletonList(new TC_AspectStack(TC_Aspects.MACHINA, 3))); + public static Materials CobaltBrass = new Materials( 343, TextureSet.SET_METALLIC , 8.0F, 256, 2, 1|2 |64|128 , 180, 180, 160, 0, "CobaltBrass" , "Cobalt Brass" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes.dyeOrange , 2, Arrays.asList(new MaterialStack(Brass, 7), new MaterialStack(Aluminium, 1), new MaterialStack(Cobalt, 1))); + public static Materials TricalciumPhosphate = new Materials( 534, TextureSet.SET_FLINT , 1.0F, 0, 2, 1|4|8|16 , 255, 255, 0, 0, "TricalciumPhosphate" , "Tricalcium Phosphate" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes.dyeYellow , 2, Arrays.asList(new MaterialStack(Calcium, 3), new MaterialStack(Phosphate, 2))); + public static Materials Basalt = new Materials( 844, TextureSet.SET_ROUGH , 1.0F, 64, 1, 1 |64|128 , 30, 20, 20, 0, "Basalt" , "Basalt" , 0, 0, -1, 0, false, false, 2, 1, 1, Dyes.dyeBlack , 2, Arrays.asList(new MaterialStack(Olivine, 1), new MaterialStack(Calcite, 3), new MaterialStack(Flint, 8), new MaterialStack(DarkAsh, 4)), Collections.singletonList(new TC_AspectStack(TC_Aspects.TENEBRAE, 1))).disableAutoGeneratedRecycleRecipes(); + public static Materials GarnetRed = new Materials( 527, TextureSet.SET_RUBY , 7.0F, 128, 2, 1 |4|8 |64 , 200, 80, 80, 127, "GarnetRed" , "Red Garnet" , 0, 0, -1, 0, false, true, 4, 1, 1, Dyes.dyeRed , 2, Arrays.asList(new MaterialStack(Pyrope, 3), new MaterialStack(Almandine, 5), new MaterialStack(Spessartine, 8)), Collections.singletonList(new TC_AspectStack(TC_Aspects.VITREUS, 3))); + public static Materials GarnetYellow = new Materials( 528, TextureSet.SET_RUBY , 7.0F, 128, 2, 1 |4|8 |64 , 200, 200, 80, 127, "GarnetYellow" , "Yellow Garnet" , 0, 0, -1, 0, false, true, 4, 1, 1, Dyes.dyeYellow , 2, Arrays.asList(new MaterialStack(Andradite, 5), new MaterialStack(Grossular, 8), new MaterialStack(Uvarovite, 3)), Collections.singletonList(new TC_AspectStack(TC_Aspects.VITREUS, 3))); + public static Materials Marble = new Materials( 845, TextureSet.SET_FINE , 1.0F, 16, 1, 1 |64|128 , 200, 200, 200, 0, "Marble" , "Marble" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeWhite , 2, Arrays.asList(new MaterialStack(Magnesium, 1), new MaterialStack(Calcite, 7)), Collections.singletonList(new TC_AspectStack(TC_Aspects.PERFODIO, 1))); + public static Materials Sugar = new Materials( 803, TextureSet.SET_FINE , 1.0F, 0, 1, 1 , 250, 250, 250, 0, "Sugar" , "Sugar" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeWhite , 1, Arrays.asList(new MaterialStack(Carbon, 2), new MaterialStack(Water, 5), new MaterialStack(Oxygen, 25)), Arrays.asList(new TC_AspectStack(TC_Aspects.HERBA, 1), new TC_AspectStack(TC_Aspects.AQUA, 1), new TC_AspectStack(TC_Aspects.AER, 1))); + public static Materials Thaumium = new Materials( 330, TextureSet.SET_METALLIC , 12.0F, 256, 3, 1|2 |64|128 , 150, 100, 200, 0, "Thaumium" , "Thaumium" , 0, 0, -1, 0, false, false, 5, 2, 1, Dyes.dyePurple , 0, Arrays.asList(new MaterialStack(Iron, 1), new MaterialStack(Magic, 1)), Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.PRAECANTATIO, 1))); + public static Materials Vinteum = new Materials( 529, TextureSet.SET_METALLIC , 10.0F, 128, 3, 1|2|4|8 |64|128 , 100, 200, 255, 0, "Vinteum" , "Vinteum" , 5, 32, -1, 0, false, false, 4, 1, 1, Dyes.dyeLightBlue , 2, Collections.singletonList(new MaterialStack(Thaumium, 1)), Arrays.asList(new TC_AspectStack(TC_Aspects.VITREUS, 2), new TC_AspectStack(TC_Aspects.PRAECANTATIO, 1))); + public static Materials Vis = new Materials( -1, TextureSet.SET_SHINY , 1.0F, 0, 3, 0 , 128, 0, 255, 0, "Vis" , "Vis" , 5, 32, -1, 0, false, false, 1, 1, 1, Dyes.dyePurple , 2, Collections.singletonList(new MaterialStack(Magic, 1)), Arrays.asList(new TC_AspectStack(TC_Aspects.AURAM, 2), new TC_AspectStack(TC_Aspects.PRAECANTATIO, 1))); + public static Materials Redrock = new Materials( 846, TextureSet.SET_ROUGH , 1.0F, 0, 1, 1 , 255, 80, 50, 0, "Redrock" , "Redrock" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeRed , 2, Arrays.asList(new MaterialStack(Calcite, 2), new MaterialStack(Flint, 1), new MaterialStack(Clay, 1))); + public static Materials PotassiumFeldspar = new Materials( 847, TextureSet.SET_FINE , 1.0F, 0, 1, 1 , 120, 40, 40, 0, "PotassiumFeldspar" , "Potassium Feldspar" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyePink , 0, Arrays.asList(new MaterialStack(Potassium, 1), new MaterialStack(Aluminium, 1), new MaterialStack(Silicon, 3), new MaterialStack(Oxygen, 8))); + public static Materials Biotite = new Materials( 848, TextureSet.SET_METALLIC , 1.0F, 0, 1, 1 , 20, 30, 20, 0, "Biotite" , "Biotite" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeGray , 0, Arrays.asList(new MaterialStack(Potassium, 1), new MaterialStack(Magnesium, 3), new MaterialStack(Aluminium, 3), new MaterialStack(Fluorine, 2), new MaterialStack(Silicon, 3), new MaterialStack(Oxygen, 10))); + public static Materials GraniteBlack = new Materials( 849, TextureSet.SET_ROUGH , 4.0F, 64, 3, 1 |64|128 , 10, 10, 10, 0, "GraniteBlack" , "Black Granite" , 0, 0, -1, 0, false, false, 0, 1, 1, Dyes.dyeBlack , 2, Arrays.asList(new MaterialStack(SiliconDioxide, 4), new MaterialStack(Biotite, 1)), Collections.singletonList(new TC_AspectStack(TC_Aspects.TUTAMEN, 1))); + public static Materials GraniteRed = new Materials( 850, TextureSet.SET_ROUGH , 4.0F, 64, 3, 1 |64|128 , 255, 0, 128, 0, "GraniteRed" , "Red Granite" , 0, 0, -1, 0, false, false, 0, 1, 1, Dyes.dyeMagenta , 0, Arrays.asList(new MaterialStack(Aluminium, 2), new MaterialStack(PotassiumFeldspar, 1), new MaterialStack(Oxygen, 3)), Collections.singletonList(new TC_AspectStack(TC_Aspects.TUTAMEN, 1))); + public static Materials Chrysotile = new Materials( 912, TextureSet.SET_DULL , 32.0F, 10240, 3, 1|2 |8 |64|128 , 110, 140, 110, 0, "Chrysotile" , "Chrysotile" , 0, 0, 9400, 9400, true, false, 1, 1, 1, Dyes.dyeWhite , 2, Collections.singletonList(new MaterialStack(Asbestos, 1))).disableAutoGeneratedBlastFurnaceRecipes().setTurbineMultipliers(280, 280, 1); + public static Materials Realgar = new Materials( 913, TextureSet.SET_DULL , 1.0F, 32, 1, 1|2 |8 |64|128 , 140, 100, 100, 0, "Realgar" , "Realgar" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeWhite , 2, Arrays.asList(new MaterialStack(Arsenic, 4), new MaterialStack(Sulfur,4))); + public static Materials VanadiumMagnetite = new Materials( 923, TextureSet.SET_METALLIC , 1.0F, 0, 2, 1 |8 , 35, 35, 60, 0, "VanadiumMagnetite" , "Vanadium Magnetite" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeBlack , 2, Arrays.asList(new MaterialStack(Magnetite, 1), new MaterialStack(Vanadium, 1)), Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.MAGNETO, 1))); // Mixture of Fe3O4 and V2O5 + public static Materials BasalticMineralSand = new Materials( 935, TextureSet.SET_SAND , 1.0F, 0, 1, 1 |8 , 40, 50, 40, 0, "BasalticMineralSand" , "Basaltic Mineral Sand" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeBlack , 2, Arrays.asList(new MaterialStack(Magnetite, 1), new MaterialStack(Basalt, 1)), Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.MAGNETO, 1))); + public static Materials GraniticMineralSand = new Materials( 936, TextureSet.SET_SAND , 1.0F, 0, 1, 1 |8 , 40, 60, 60, 0, "GraniticMineralSand" , "Granitic Mineral Sand" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeBlack , 2, Arrays.asList(new MaterialStack(Magnetite, 1), new MaterialStack(GraniteBlack, 1)), Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.MAGNETO, 1))); + public static Materials GarnetSand = new Materials( 938, TextureSet.SET_SAND , 1.0F, 0, 1, 1 |8 , 200, 100, 0, 0, "GarnetSand" , "Garnet Sand" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeOrange , 2, Arrays.asList(new MaterialStack(GarnetRed, 1), new MaterialStack(GarnetYellow, 1))); + public static Materials QuartzSand = new Materials( 939, TextureSet.SET_SAND , 1.0F, 0, 1, 1 |8 , 194, 178, 128, 0, "QuartzSand" , "Quartz Sand" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeWhite , 2, Arrays.asList(new MaterialStack(CertusQuartz, 1), new MaterialStack(Quartzite, 1))); + public static Materials Bastnasite = new Materials( 905, TextureSet.SET_FINE , 1.0F, 0, 2, 1 |8 , 200, 110, 45, 0, "Bastnasite" , "Bastnasite" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes._NULL , 1, Arrays.asList(new MaterialStack(Cerium, 1), new MaterialStack(Carbon, 1), new MaterialStack(Fluorine, 1), new MaterialStack(Oxygen, 3))); // (Ce, La, Y)CO3F + public static Materials Pentlandite = new Materials( 909, TextureSet.SET_DULL , 1.0F, 0, 2, 1 |8 , 165, 150, 5, 0, "Pentlandite" , "Pentlandite" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes._NULL , 1, Arrays.asList(new MaterialStack(Nickel, 9), new MaterialStack(Sulfur, 8))); // (Fe,Ni)9S8 + public static Materials Spodumene = new Materials( 920, TextureSet.SET_DULL , 1.0F, 0, 2, 1 |8 , 190, 170, 170, 0, "Spodumene" , "Spodumene" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes._NULL , 0, Arrays.asList(new MaterialStack(Lithium, 1), new MaterialStack(Aluminium, 1), new MaterialStack(Silicon, 2), new MaterialStack(Oxygen, 6))); // LiAl(SiO3)2 + public static Materials Pollucite = new Materials( 919, TextureSet.SET_DULL , 1.0F, 0, 2, 1 |8 , 240, 210, 210, 0, "Pollucite" , "Pollucite" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes._NULL , 0, Arrays.asList(new MaterialStack(Caesium, 2), new MaterialStack(Aluminium, 2), new MaterialStack(Silicon, 4), new MaterialStack(Water, 2), new MaterialStack(Oxygen, 12))); // (Cs,Na)2Al2Si4O12 2H2O (also a source of Rb) + public static Materials Tantalite = new Materials( 921, TextureSet.SET_METALLIC , 1.0F, 0, 3, 1 |8 , 145, 80, 40, 0, "Tantalite" , "Tantalite" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes._NULL , 1, Arrays.asList(new MaterialStack(Manganese, 1), new MaterialStack(Tantalum, 2), new MaterialStack(Oxygen, 6))); // (Fe, Mn)Ta2O6 (also source of Nb) + public static Materials Lepidolite = new Materials( 907, TextureSet.SET_FINE , 1.0F, 0, 2, 1 |8 , 240, 50, 140, 0, "Lepidolite" , "Lepidolite" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes._NULL , 0, Arrays.asList(new MaterialStack(Potassium, 1), new MaterialStack(Lithium, 3), new MaterialStack(Aluminium, 4), new MaterialStack(Fluorine, 2), new MaterialStack(Oxygen, 10))); // K(Li,Al,Rb)3(Al,Si)4O10(F,OH)2 + public static Materials Glauconite = new Materials( 933, TextureSet.SET_DULL , 1.0F, 0, 2, 1 |8 , 130, 180, 60, 0, "Glauconite" , "Glauconite" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes._NULL , 0, Arrays.asList(new MaterialStack(Potassium, 1), new MaterialStack(Magnesium, 2), new MaterialStack(Aluminium, 4), new MaterialStack(Hydrogen, 2), new MaterialStack(Oxygen, 12))); // (K,Na)(Fe3+,Al,Mg)2(Si,Al)4O10(OH)2 + public static Materials GlauconiteSand = new Materials( 949, TextureSet.SET_DULL , 1.0F, 0, 2, 1 |8 , 130, 180, 60, 0, "GlauconiteSand" , "Glauconite Sand" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes._NULL , 0, Arrays.asList(new MaterialStack(Potassium, 1), new MaterialStack(Magnesium, 2), new MaterialStack(Aluminium, 4), new MaterialStack(Hydrogen, 2), new MaterialStack(Oxygen, 12))); // (K,Na)(Fe3+,Al,Mg)2(Si,Al)4O10(OH)2 + public static Materials Vermiculite = new Materials( 932, TextureSet.SET_METALLIC , 1.0F, 0, 2, 1 |8 , 200, 180, 15, 0, "Vermiculite" , "Vermiculite" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes._NULL , 0, Arrays.asList(new MaterialStack(Iron, 3), new MaterialStack(Aluminium, 4), new MaterialStack(Silicon, 4), new MaterialStack(Hydrogen, 2), new MaterialStack(Water, 4), new MaterialStack(Oxygen, 12))); // (Mg+2, Fe+2, Fe+3)3 [(AlSi)4O10] (OH)2 4H2O) + public static Materials Bentonite = new Materials( 927, TextureSet.SET_ROUGH , 1.0F, 0, 2, 1 |8 , 245, 215, 210, 0, "Bentonite" , "Bentonite" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes._NULL , 1, Arrays.asList(new MaterialStack(Sodium, 1), new MaterialStack(Magnesium, 6), new MaterialStack(Silicon, 12), new MaterialStack(Hydrogen, 6), new MaterialStack(Water, 5), new MaterialStack(Oxygen, 36))); // (Na,Ca)0.33(Al,Mg)2(Si4O10)(OH)2 nH2O + public static Materials FullersEarth = new Materials( 928, TextureSet.SET_FINE , 1.0F, 0, 2, 1 |8 , 160, 160, 120, 0, "FullersEarth" , "Fullers Earth" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes._NULL , 1, Arrays.asList(new MaterialStack(Magnesium, 1), new MaterialStack(Silicon, 4), new MaterialStack(Hydrogen, 1), new MaterialStack(Water, 4), new MaterialStack(Oxygen, 11))); // (Mg,Al)2Si4O10(OH) 4(H2O) + public static Materials Pitchblende = new Materials( 873, TextureSet.SET_DULL , 1.0F, 0, 3, 1 |8 , 200, 210, 0, 0, "Pitchblende" , "Pitchblende" , 0, 0, -1, 0, false, false, 5, 1, 1, Dyes.dyeYellow , 2, Arrays.asList(new MaterialStack(Uraninite, 3), new MaterialStack(Thorium, 1), new MaterialStack(Lead, 1))); + public static Materials Monazite = new Materials( 520, TextureSet.SET_DIAMOND , 1.0F, 0, 1, 1 |4|8 , 50, 70, 50, 0, "Monazite" , "Monazite" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes.dyeGreen , 1, Arrays.asList(new MaterialStack(RareEarth, 1), new MaterialStack(Phosphate, 1))); // Wikipedia: (Ce, La, Nd, Th, Sm, Gd)PO4 Monazite also smelt-extract to Helium, it is brown like the rare earth Item Monazite sand deposits are inevitably of the monazite-(Ce) composition. Typically, the lanthanides in such monazites contain about 45.8% cerium, about 24% lanthanum, about 17% neodymium, about 5% praseodymium, and minor quantities of samarium, gadolinium, and yttrium. Europium concentrations tend to be low, about 0.05% Thorium content of monazite is variable. + public static Materials Malachite = new Materials( 871, TextureSet.SET_DULL , 1.0F, 0, 1, 1 |8 , 5, 95, 5, 0, "Malachite" , "Malachite" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes.dyeGreen , 1, Arrays.asList(new MaterialStack(Copper, 2), new MaterialStack(Carbon, 1), new MaterialStack(Hydrogen, 2), new MaterialStack(Oxygen, 5))); // Cu2CO3(OH)2 + public static Materials Mirabilite = new Materials( 900, TextureSet.SET_DULL , 1.0F, 0, 2, 1 |8 , 240, 250, 210, 0, "Mirabilite" , "Mirabilite" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes._NULL , 1, Arrays.asList(new MaterialStack(Sodium, 2), new MaterialStack(Sulfur, 1), new MaterialStack(Water, 10), new MaterialStack(Oxygen, 4))); // Na2SO4 10H2O + public static Materials Mica = new Materials( 901, TextureSet.SET_FINE , 1.0F, 0, 1, 1 |8 , 195, 195, 205, 0, "Mica" , "Mica" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes._NULL , 0, Arrays.asList(new MaterialStack(Potassium, 1), new MaterialStack(Aluminium, 3), new MaterialStack(Silicon, 3), new MaterialStack(Fluorine, 2), new MaterialStack(Oxygen, 10))); // KAl2(AlSi3O10)(F,OH)2 + public static Materials Trona = new Materials( 903, TextureSet.SET_METALLIC , 1.0F, 0, 1, 1 |8 , 135, 135, 95, 0, "Trona" , "Trona" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes._NULL , 1, Arrays.asList(new MaterialStack(Sodium, 3), new MaterialStack(Carbon, 2), new MaterialStack(Hydrogen, 1), new MaterialStack(Water, 2), new MaterialStack(Oxygen, 6))); // Na3(CO3)(HCO3) 2H2O + public static Materials Barite = new Materials( 904, TextureSet.SET_DULL , 1.0F, 0, 2, 1 |8 , 230, 235, 255, 0, "Barite" , "Barite" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes._NULL , 1, Arrays.asList(new MaterialStack(Barium, 1), new MaterialStack(Sulfur, 1), new MaterialStack(Oxygen, 4))); + public static Materials Gypsum = new Materials( 934, TextureSet.SET_DULL , 1.0F, 0, 1, 1 |8 , 230, 230, 250, 0, "Gypsum" , "Gypsum" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes._NULL , 1, Arrays.asList(new MaterialStack(Calcium, 1), new MaterialStack(Sulfur, 1), new MaterialStack(Oxygen, 4), new MaterialStack(Water, 2))); // CaSO4 2H2O + public static Materials Alunite = new Materials( 911, TextureSet.SET_METALLIC , 1.0F, 0, 2, 1 |8 , 225, 180, 65, 0, "Alunite" , "Alunite" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes._NULL , 0, Arrays.asList(new MaterialStack(Potassium, 1), new MaterialStack(Aluminium, 3), new MaterialStack(Silicon, 2), new MaterialStack(Hydrogen, 6), new MaterialStack(Oxygen, 14))); // KAl3(SO4)2(OH)6 + public static Materials Dolomite = new Materials( 914, TextureSet.SET_FLINT , 1.0F, 0, 1, 1 |8 , 225, 205, 205, 0, "Dolomite" , "Dolomite" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes._NULL , 1, Arrays.asList(new MaterialStack(Calcium, 1), new MaterialStack(Magnesium, 1), new MaterialStack(Carbon, 2), new MaterialStack(Oxygen, 6))); // CaMg(CO3)2 + public static Materials Wollastonite = new Materials( 915, TextureSet.SET_DULL , 1.0F, 0, 2, 1 |8 , 240, 240, 240, 0, "Wollastonite" , "Wollastonite" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes._NULL , 1, Arrays.asList(new MaterialStack(Calcium, 1), new MaterialStack(Silicon, 1), new MaterialStack(Oxygen, 3))); // CaSiO3 + public static Materials Zeolite = new Materials( 916, TextureSet.SET_DULL , 1.0F, 0, 2, 1 |8 , 240, 230, 230, 0, "Zeolite" , "Zeolite" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes._NULL , 0, Arrays.asList(new MaterialStack(Sodium, 1), new MaterialStack(Calcium, 4), new MaterialStack(Silicon, 27), new MaterialStack(Aluminium, 9), new MaterialStack(Oxygen, 72))); // NaCa4(Si27Al9)O72 + public static Materials Kyanite = new Materials( 924, TextureSet.SET_FLINT , 1.0F, 0, 2, 1 |8 , 110, 110, 250, 0, "Kyanite" , "Kyanite" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes._NULL , 0, Arrays.asList(new MaterialStack(Aluminium, 2), new MaterialStack(Silicon, 1), new MaterialStack(Oxygen, 5))); // Al2SiO5 + public static Materials Kaolinite = new Materials( 929, TextureSet.SET_DULL , 1.0F, 0, 2, 1 |8 , 245, 235, 235, 0, "Kaolinite" , "Kaolinite" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes._NULL , 0, Arrays.asList(new MaterialStack(Aluminium, 2), new MaterialStack(Silicon, 2), new MaterialStack(Hydrogen, 4), new MaterialStack(Oxygen, 9))); // Al2Si2O5(OH)4 + public static Materials Talc = new Materials( 902, TextureSet.SET_DULL , 1.0F, 0, 2, 1 |8 , 90, 180, 90, 0, "Talc" , "Talc" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes._NULL , 1, Arrays.asList(new MaterialStack(Magnesium, 3), new MaterialStack(Silicon, 4), new MaterialStack(Hydrogen, 2), new MaterialStack(Oxygen, 12))); // H2Mg3(SiO3)4 + public static Materials Soapstone = new Materials( 877, TextureSet.SET_DULL , 1.0F, 0, 1, 1 |8 , 95, 145, 95, 0, "Soapstone" , "Soapstone" , 0, 0, -1, 0, false, false, 1, 1, 1, Dyes._NULL , 1, Arrays.asList(new MaterialStack(Magnesium, 3), new MaterialStack(Silicon, 4), new MaterialStack(Hydrogen, 2), new MaterialStack(Oxygen, 12))); // H2Mg3(SiO3)4 + public static Materials Concrete = new Materials( 947, TextureSet.SET_ROUGH , 1.0F, 0, 1, 1 , 100, 100, 100, 0, "Concrete" , "Concrete" , 0, 0, 300, 0, false, false, 0, 1, 1, Dyes.dyeGray , 0, Collections.singletonList(new MaterialStack(Stone, 1)), Collections.singletonList(new TC_AspectStack(TC_Aspects.TERRA, 1))); + public static Materials IronMagnetic = new Materials( 354, TextureSet.SET_MAGNETIC , 6.0F, 256, 2, 1|2 |64|128 , 200, 200, 200, 0, "IronMagnetic" , "Magnetic Iron" , 0, 0, -1, 0, false, false, 4, 51, 50, Dyes.dyeGray , 1, Collections.singletonList(new MaterialStack(Iron, 1)), Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.MAGNETO, 1))); + public static Materials SteelMagnetic = new Materials( 355, TextureSet.SET_MAGNETIC , 6.0F, 512, 3, 1|2 |64|128 , 128, 128, 128, 0, "SteelMagnetic" , "Magnetic Steel" , 0, 0, 1000, 1000, true, false, 4, 51, 50, Dyes.dyeGray , 1, Collections.singletonList(new MaterialStack(Steel, 1)), Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 1), new TC_AspectStack(TC_Aspects.ORDO, 1), new TC_AspectStack(TC_Aspects.MAGNETO, 1))); + public static Materials NeodymiumMagnetic = new Materials( 356, TextureSet.SET_MAGNETIC , 7.0F, 512, 2, 1|2 |64|128 , 100, 100, 100, 0, "NeodymiumMagnetic" , "Magnetic Neodymium" , 0, 0, 1297, 1297, true, false, 4, 51, 50, Dyes.dyeGray , 1, Collections.singletonList(new MaterialStack(Neodymium, 1)), Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 1), new TC_AspectStack(TC_Aspects.MAGNETO, 3))); + public static Materials SamariumMagnetic = new Materials( 399, TextureSet.SET_MAGNETIC , 1.0F, 0, 2, 1|2 |64|128 , 255, 255, 204, 0, "SamariumMagnetic" , "Magnetic Samarium" , 0, 0, 1345, 1345, true, false, 4, 1, 1, Dyes.dyeWhite , 1, Collections.singletonList(new MaterialStack(Samarium, 1)),Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.RADIO, 1), new TC_AspectStack(TC_Aspects.MAGNETO,10))); + public static Materials TungstenCarbide = new Materials( 370, TextureSet.SET_METALLIC , 14.0F, 1280, 4, 1|2 |64|128 , 51, 0, 102, 0, "TungstenCarbide" , "Tungstencarbide" , 0, 0, 2460, 2460, true, false, 4, 1, 1, Dyes.dyeBlack , 2, Arrays.asList(new MaterialStack(Tungsten, 1), new MaterialStack(Carbon, 1))).disableAutoGeneratedVacuumFreezerRecipe(); + public static Materials VanadiumSteel = new Materials( 371, TextureSet.SET_METALLIC , 3.0F, 1920, 3, 1|2 |64|128 , 192, 192, 192, 0, "VanadiumSteel" , "Vanadiumsteel" , 0, 0, 1453, 1453, true, false, 4, 1, 1, Dyes.dyeWhite , 2, Arrays.asList(new MaterialStack(Vanadium, 1), new MaterialStack(Chrome, 1), new MaterialStack(Steel, 7))).disableAutoGeneratedBlastFurnaceRecipes(); + public static Materials HSSG = new Materials( 372, TextureSet.SET_METALLIC , 10.0F, 4000, 3, 1|2 |64|128 , 153, 153, 0, 0, "HSSG" , "HSS-G" , 0, 0, 4500, 4500, true, false, 4, 1, 1, Dyes.dyeYellow , 2, Arrays.asList(new MaterialStack(TungstenSteel, 5), new MaterialStack(Chrome, 1), new MaterialStack(Molybdenum, 2), new MaterialStack(Vanadium, 1))).disableAutoGeneratedBlastFurnaceRecipes().disableAutoGeneratedVacuumFreezerRecipe(); + public static Materials HSSE = new Materials( 373, TextureSet.SET_METALLIC , 32.0F, 10240, 7, 1|2 |64|128 , 51, 102, 0, 0, "HSSE" , "HSS-E" , 0, 0, 5400, 5400, true, false, 4, 1, 1, Dyes.dyeGreen , 2, Arrays.asList(new MaterialStack(HSSG, 6), new MaterialStack(Cobalt, 1),new MaterialStack(Manganese, 1), new MaterialStack(Silicon, 1))).disableAutoGeneratedBlastFurnaceRecipes().disableAutoGeneratedVacuumFreezerRecipe(); + public static Materials HSSS = new Materials( 374, TextureSet.SET_METALLIC , 32.0F, 10240, 8, 1|2 |64|128 , 102, 0, 51, 0, "HSSS" , "HSS-S" , 0, 0, 5400, 5400, true, false, 4, 1, 1, Dyes.dyeRed , 2, Arrays.asList(new MaterialStack(HSSG, 6), new MaterialStack(Iridium, 2), new MaterialStack(Osmium, 1))).disableAutoGeneratedBlastFurnaceRecipes().disableAutoGeneratedVacuumFreezerRecipe(); + public static Materials TPV = new Materials( 576, TextureSet.SET_METALLIC , 16.0F, 4000, 5, 1|2 |64|128 , 250, 170,250, 0, "TPVAlloy" , "TPV-Alloy" , 0, 0, 3000, 3000, true, false, 4, 1, 1, Dyes.dyeRed , 2, Arrays.asList(new MaterialStack(Titanium, 3), new MaterialStack(Platinum, 3), new MaterialStack(Vanadium, 1))).disableAutoGeneratedBlastFurnaceRecipes().disableAutoGeneratedVacuumFreezerRecipe(); + public static Materials DilutedSulfuricAcid = new MaterialBuilder(640, TextureSet.SET_FLUID , "Diluted Sulfuric Acid").addCell().addFluid().setRGB(192, 120, 32).setColor(Dyes.dyeOrange).setMaterialList(new MaterialStack(SulfuricAcid, 1)).constructMaterial(); + public static Materials EpoxidFiberReinforced = new Materials( 610, TextureSet.SET_DULL ,3.0F, 64, 1, 1|2 |64|128 , 160, 112, 16, 0, "EpoxidFiberReinforced" , "Fiber-Reinforced Epoxy Resin" , 0, 0, 400, 0, false, false, 1, 1, 1, Dyes.dyeBrown , 2, Collections.singletonList(new MaterialStack(Epoxid, 1)), Collections.singletonList(new TC_AspectStack(TC_Aspects.MOTUS, 2))); + public static Materials SodiumCarbonate = new MaterialBuilder(695, TextureSet.SET_QUARTZ, "Sodium Carbonate").setToolSpeed(1.0F).setDurability(64).setToolQuality(1).addDustItems().setRGB(255, 255, 235).setColor(Dyes.dyeWhite).setBlastFurnaceTemp(851 ).setMeltingPoint(851 ).setBlastFurnaceRequired(false).setOreValue(1).setExtraData(1).setMaterialList(new MaterialStack(Sodium, 2), new MaterialStack(Carbon, 1), new MaterialStack(Oxygen, 3)).constructMaterial().disableAutoGeneratedBlastFurnaceRecipes(); + public static Materials SodiumAluminate = new MaterialBuilder(696, TextureSet.SET_QUARTZ, "Sodium Aluminate").setToolSpeed(1.0F).setDurability(64).setToolQuality(1).addDustItems().setRGB(255, 235, 255).setColor(Dyes.dyeWhite).setBlastFurnaceTemp(1800).setMeltingPoint(1800).setBlastFurnaceRequired(false).setOreValue(1).setExtraData(0).setMaterialList(new MaterialStack(Sodium, 1), new MaterialStack(Aluminium, 1), new MaterialStack(Oxygen, 2)).constructMaterial().disableAutoGeneratedBlastFurnaceRecipes(); + public static Materials Aluminiumoxide = new MaterialBuilder(697, TextureSet.SET_QUARTZ, "Alumina").setToolSpeed(1.0F).setDurability(64).setToolQuality(1).addDustItems().setRGB(235, 255, 255).setColor(Dyes.dyeWhite).setBlastFurnaceTemp(2054).setMeltingPoint(2054).setBlastFurnaceRequired(true).setOreValue(1).setExtraData(0).setMaterialList(new MaterialStack(Aluminium, 2), new MaterialStack(Oxygen, 3)).setAspects(Collections.singletonList(new TC_AspectStack(TC_Aspects.GELUM, 2))).constructMaterial().disableAutoGeneratedBlastFurnaceRecipes(); + public static Materials Aluminiumhydroxide = new MaterialBuilder(698, TextureSet.SET_QUARTZ, "Aluminium Hydroxide").setToolSpeed(1.0F).setDurability(64).setToolQuality(1).addDustItems().setRGB(235, 235, 255).setColor(Dyes.dyeWhite).setBlastFurnaceTemp(1200).setMeltingPoint(1200).setBlastFurnaceRequired(true).setOreValue(1).setExtraData(0).setMaterialList(new MaterialStack(Aluminium, 1), new MaterialStack(Oxygen, 3), new MaterialStack(Hydrogen, 3)).setAspects(Collections.singletonList(new TC_AspectStack(TC_Aspects.GELUM, 2))).constructMaterial().disableAutoGeneratedBlastFurnaceRecipes(); + public static Materials Cryolite = new MaterialBuilder(699, TextureSet.SET_QUARTZ, "Cryolite").setToolSpeed(1.0F).setDurability(64).setToolQuality(1).addOreItems().setRGB(191, 239, 255).setColor(Dyes.dyeLightBlue).setMeltingPoint(1012).setBlastFurnaceTemp(1012).setExtraData(0).setMaterialList(new MaterialStack(Sodium, 3), new MaterialStack(Aluminium, 1), new MaterialStack(Fluorine, 6)).constructMaterial().disableAutoGeneratedBlastFurnaceRecipes(); + public static Materials RedMud = new MaterialBuilder(743, TextureSet.SET_FLUID, "Red Mud").addCell().addFluid().setRGB(140, 22, 22).setColor(Dyes.dyeRed).constructMaterial(); + + public static Materials Brick = new MaterialBuilder(625, TextureSet.SET_ROUGH , "Brick").addDustItems().setRGB(155, 86, 67).setColor(Dyes.dyeBrown).setExtraData(0).setMaterialList(new MaterialStack(Aluminium, 2), new MaterialStack(Silicon, 4), new MaterialStack(Oxygen, 11)).constructMaterial(); + public static Materials Fireclay = new MaterialBuilder(626, TextureSet.SET_ROUGH , "Fireclay").addDustItems().setRGB(173, 160, 155).setExtraData(2).setColor(Dyes.dyeBrown).setMaterialList(new MaterialStack(Brick, 1)).constructMaterial(); + + // Polybenzimidazole stuff + public static Materials PotassiumNitrade = new MaterialBuilder(590, TextureSet.SET_DULL , "Potassium Nitrate").setName("PotassiumNitrate").addDustItems().setRGB(129, 34, 141).setColor(Dyes.dyePurple).setMaterialList(new MaterialStack(Potassium, 1), new MaterialStack(Nitrogen, 1), new MaterialStack(Oxygen, 3)).addElectrolyzerRecipe().constructMaterial(); + public static Materials ChromiumTrioxide = new MaterialBuilder(591, TextureSet.SET_DULL , "Chromium Trioxide").setName("Chromiumtrioxide").addDustItems().setRGB(255, 228, 225).setColor(Dyes.dyePink).setMaterialList(new MaterialStack(Chrome, 1), new MaterialStack(Oxygen, 3)).addElectrolyzerRecipe().constructMaterial(); + public static Materials Nitrochlorobenzene = new MaterialBuilder(592, TextureSet.SET_FLUID , "2-Nitrochlorobenzene").addCell().addFluid().setRGB(143, 181, 26).setColor(Dyes.dyeLime).setMaterialList(new MaterialStack(Carbon, 6), new MaterialStack(Hydrogen, 4), new MaterialStack(Chlorine, 1), new MaterialStack(Nitrogen, 1), new MaterialStack(Oxygen, 2)).constructMaterial(); + public static Materials Dimethylbenzene = new MaterialBuilder(593, TextureSet.SET_FLUID , "1,2-Dimethylbenzene").setName("Dimethylbenzene").addCell().addFluid().setRGB(102, 156, 64).setColor(Dyes.dyeLime).setMeltingPoint(248).setMaterialList(new MaterialStack(Carbon, 8), new MaterialStack(Hydrogen, 10)).addElectrolyzerRecipe().constructMaterial(); + public static Materials Potassiumdichromate = new MaterialBuilder(594, TextureSet.SET_DULL , "Potassium Dichromate").setName("PotassiumDichromate").addDustItems().setRGB(255, 8, 127).setColor(Dyes.dyePink).setMaterialList(new MaterialStack(Potassium, 2), new MaterialStack(Chrome, 2), new MaterialStack(Oxygen, 7)).addElectrolyzerRecipe().constructMaterial(); + public static Materials PhthalicAcid = new MaterialBuilder(595, TextureSet.SET_FLUID , "Phthalic Acid").setName("phtalicacid").addCell().addFluid().setRGB(54, 133, 71).setColor(Dyes.dyeOrange).setMaterialList(new MaterialStack(Carbon, 8), new MaterialStack(Hydrogen, 6), new MaterialStack(Oxygen, 4)).constructMaterial(); + public static Materials Dichlorobenzidine = new MaterialBuilder(596, TextureSet.SET_FLUID , "3,3-Dichlorobenzidine").addCell().addFluid().setRGB(161, 222, 166).setColor(Dyes.dyeOrange).setMaterialList(new MaterialStack(Carbon, 12),new MaterialStack(Hydrogen, 10), new MaterialStack(Nitrogen, 2), new MaterialStack(Chlorine, 2)).constructMaterial(); + public static Materials Diaminobenzidin = new MaterialBuilder(597, TextureSet.SET_FLUID , "3,3-Diaminobenzidine").addCell().addFluid().setRGB(51, 125, 89).setColor(Dyes.dyeOrange).setMaterialList(new MaterialStack(Carbon, 12),new MaterialStack(Hydrogen, 14),new MaterialStack(Nitrogen, 4)).constructMaterial(); + public static Materials Diphenylisophthalate = new MaterialBuilder(598, TextureSet.SET_FLUID , "Diphenyl Isophtalate").addCell().addFluid().setRGB(36, 110, 87).setColor(Dyes.dyeOrange).setMaterialList(new MaterialStack(Carbon, 20),new MaterialStack(Hydrogen, 14),new MaterialStack(Oxygen, 4)).constructMaterial(); + public static Materials Polybenzimidazole = new Materials(599, TextureSet.SET_DULL ,3.0F, 64, 1, 1|2 |64|128 , 45, 45, 45, 0, "Polybenzimidazole" , "Polybenzimidazole" , 0, 0, 1450, 0, false, false, 1, 1, 1, Dyes.dyeBlack , 0, Arrays.asList(new MaterialStack(Carbon, 20), new MaterialStack(Nitrogen, 4), new MaterialStack(Hydrogen, 12)), Arrays.asList(new TC_AspectStack(TC_Aspects.ORDO, 2),new TC_AspectStack(TC_Aspects.VOLATUS, 1))); + + //Gasoline + public static Materials MTBEMixture = new MaterialBuilder(983, TextureSet.SET_FLUID , "MTBE Reaction Mixture").addCell().addGas().setRGB(255, 255, 255).setColor(Dyes.dyeWhite).setMaterialList(new MaterialStack(Carbon, 5), new MaterialStack(Hydrogen, 12), new MaterialStack(Oxygen, 1)).constructMaterial(); + public static Materials NitrousOxide = new MaterialBuilder(993, TextureSet.SET_FLUID , "Nitrous Oxide").addCell().addGas().setRGB(125, 200, 255).setColor(Dyes.dyeBlue).setMaterialList(new MaterialStack(Nitrogen, 2), new MaterialStack(Oxygen, 1)).addElectrolyzerRecipe().constructMaterial(); + public static Materials AntiKnock = new MaterialBuilder(994, TextureSet.SET_FLUID , "Anti-Knock Agent").setName("EthylTertButylEther").addCell().addFluid().setRGB(255, 255, 255).setColor(Dyes.dyeWhite).setMaterialList(new MaterialStack(Carbon, 6), new MaterialStack(Hydrogen, 14), new MaterialStack(Oxygen, 1)).constructMaterial(); + public static Materials Octane = new MaterialBuilder(995, TextureSet.SET_FLUID , "Octane").addCell().addFluid().setRGB(255, 255, 255).setColor(Dyes.dyeWhite).setFuelType(MaterialBuilder.DIESEL).setFuelPower(80).setMaterialList(new MaterialStack(Carbon, 8), new MaterialStack(Hydrogen, 18)).constructMaterial(); + public static Materials GasolineRaw = new MaterialBuilder(996, TextureSet.SET_FLUID , "Raw Gasoline").addCell().addFluid().setRGB(255,100,0).setColor(Dyes.dyeOrange).constructMaterial(); + public static Materials GasolineRegular = new MaterialBuilder(997, TextureSet.SET_FLUID , "Gasoline").addCell().addFluid().setRGB(255,165,0).setColor(Dyes.dyeOrange).setFuelType(MaterialBuilder.DIESEL).setFuelPower(576).constructMaterial(); + public static Materials GasolinePremium = new MaterialBuilder(998, TextureSet.SET_FLUID , "High Octane Gasoline").addCell().addFluid().setRGB(255,165,0).setColor(Dyes.dyeOrange).setFuelType(MaterialBuilder.DIESEL).setFuelPower(2500).constructMaterial(); + + //ADDED + public static Materials Electrotine = new Materials( 812, TextureSet.SET_SHINY , 1.0F, 0, 1, 1 |8 , 60, 180, 200, 0, "Electrotine" , "Electrotine" , 0, 0, -1, 0, false, false, 3, 1, 1, Dyes.dyeCyan , 0, Arrays.asList(new MaterialStack(Redstone, 1), new MaterialStack(Electrum, 1)), Collections.singletonList(new TC_AspectStack(TC_Aspects.ELECTRUM, 2))); + public static Materials Galgadorian = new Materials( 384, TextureSet.SET_METALLIC , 16.0F, 3600, 3, 1|2 |64|128 , 154, 105, 119, 0, "Galgadorian" , "Galgadorian" , 0, 0, 3000, 3000, true, false, 1, 1, 1, Dyes.dyePink ).disableAutoGeneratedBlastFurnaceRecipes(); + public static Materials EnhancedGalgadorian = new Materials( 385, TextureSet.SET_METALLIC , 32.0F, 7200, 5, 1|2| 64|128 , 152, 93, 133, 0, "EnhancedGalgadorian" , "Enhanced Galgadorian" , 0, 0, 4500, 4500, true, false, 1, 1, 1, Dyes.dyePink ).disableAutoGeneratedBlastFurnaceRecipes(); + public static Materials BloodInfusedIron = new Materials( 977, TextureSet.SET_METALLIC , 10.0F, 384, 2, 1|2 |64|128 , 69, 9, 10, 0, "BloodInfusedIron" , "Blood Infused Iron" , 0, 0, 2400, 0, false, false, 3, 1, 1, Dyes.dyeRed , Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 3), new TC_AspectStack(TC_Aspects.PRAECANTATIO, 1))); + public static Materials Shadow = new Materials( 368, TextureSet.SET_METALLIC , 32.0F, 8192, 4, 1|2 |8 |64|128 , 16, 3, 66, 0, "Shadow" , "Shadow Metal" , 0, 0, 1800, 1800, true, false, 3, 4, 3, Dyes.dyeBlue ); + + /** + * Galaxy Space 1.10 compat from Version 2.6 + */ + public static Materials Ledox = new Materials( 390, TextureSet.SET_SHINY , 15.0F, 1024, 4, 1|2 |8 |64|128 , 0, 116, 255, 0, "Ledox" , "Ledox" , 0, 0, -1, 0, false, false, 4, 1, 1, Dyes.dyeBlue ); + public static Materials Quantium = new Materials( 391, TextureSet.SET_SHINY , 18.0F, 2048, 4, 1|2 |8 |64|128 , 0, 209, 11, 0, "Quantium" , "Quantium" , 0, 0, 9900, 9900, true, false, 4, 1, 1, Dyes.dyeLime ).disableAutoGeneratedVacuumFreezerRecipe(); + public static Materials Mytryl = new Materials( 387, TextureSet.SET_SHINY , 8.0F, 512, 4, 1|2 |8 |64|128 , 242, 100, 4, 0, "Mytryl" , "Mytryl" , 0, 0, 3600, 3600, true, false, 4, 1, 1, Dyes.dyeOrange ); + public static Materials BlackPlutonium = new Materials( 388, TextureSet.SET_DULL , 36.0F, 8192, 8, 1|2 |8 |64|128 , 50, 50, 50, 0, "BlackPlutonium" , "Black Plutonium" , 0, 0, 9000, 9000, true, false, 4, 1, 1, Dyes.dyeBlack ).disableAutoGeneratedBlastFurnaceRecipes().disableAutoGeneratedVacuumFreezerRecipe(); + public static Materials CallistoIce = new Materials( 389, TextureSet.SET_SHINY , 9.0F, 1024, 4, 1|2 |8 |64|128 , 30, 177, 255, 0, "CallistoIce" , "Callisto Ice" , 0, 0, -1, 0, false, false, 4, 1, 1, Dyes.dyeLightBlue ); + public static Materials Duralumin = new Materials( 392, TextureSet.SET_SHINY , 16.0F, 512, 3, 1|2 |8 |64|128 , 235, 209, 160, 0, "Duralumin" , "Duralumin" , 0, 0, 1600, 1600, true, false, 4, 1, 1, Dyes.dyeOrange , 2, Arrays.asList(new MaterialStack(Aluminium, 6), new MaterialStack(Copper, 1), new MaterialStack(Manganese, 1), new MaterialStack(Magnesium, 1))); + public static Materials Oriharukon = new Materials( 393, TextureSet.SET_SHINY , 32.0F, 10240, 5, 1|2 |8 |32|64|128 , 103, 125, 104, 0, "Oriharukon" , "Oriharukon" , 0, 0, 5400, 5400, true, false, 4, 1, 1, Dyes.dyeLime , Element.Oh, Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2),new TC_AspectStack(TC_Aspects.LUCRUM, 2), new TC_AspectStack(TC_Aspects.ALIENIS, 1))).disableAutoGeneratedBlastFurnaceRecipes().disableAutoGeneratedVacuumFreezerRecipe(); + public static Materials MysteriousCrystal = new Materials( 398, TextureSet.SET_SHINY , 8.0F, 256, 6, 1|2 |8 |64|128 , 22, 133, 108, 0, "MysteriousCrystal" , "Mysterious Crystal" , 0, 0, 7200, 7200, true, false, 4, 1, 1, Dyes.dyeCyan ).disableAutoGeneratedBlastFurnaceRecipes().disableAutoGeneratedVacuumFreezerRecipe(); + + //\/HAD TO MOVE DOWN SECTION + public static Materials RedstoneAlloy = new Materials( 381, TextureSet.SET_METALLIC , 3.0F, 128, 2, 1|2 |64|128 , 181, 51, 51, 0, "RedstoneAlloy" , "Redstone Alloy" , 0, 0, 671, 1000, true, false, 1, 1, 1, Dyes.dyeRed , 1, Arrays.asList(new MaterialStack(Redstone, 1), new MaterialStack(Silicon, 1), new MaterialStack(Coal, 1))).disableAutoGeneratedBlastFurnaceRecipes(); + public static Materials Soularium = new Materials( 379, TextureSet.SET_METALLIC , 8.0F, 256, 2, 1|2 |64|128 , 65, 46, 29, 0, "Soularium" , "Soularium" , 0, 0, 800, 1000, true, false, 3, 1, 1, Dyes.dyeBrown , 1, Arrays.asList(new MaterialStack(SoulSand, 1), new MaterialStack(Gold, 1), new MaterialStack(Ash, 1))).disableAutoGeneratedBlastFurnaceRecipes(); + public static Materials ConductiveIron = new Materials( 369, TextureSet.SET_METALLIC , 6.0F, 256, 3, 1|2 |64|128 , 217, 178, 171, 0, "ConductiveIron" , "Conductive Iron" , 0, 0, -1, 1200, true, false, 4, 1, 1, Dyes.dyeRed , 1, Arrays.asList(new MaterialStack(RedstoneAlloy, 1), new MaterialStack(Iron, 1), new MaterialStack(Silver, 1))).disableAutoGeneratedBlastFurnaceRecipes(); + public static Materials ElectricalSteel = new Materials( 365, TextureSet.SET_METALLIC , 6.0F, 512, 2, 1|2 |64|128 , 185, 185, 185, 0, "ElectricalSteel" , "Electrical Steel" , 0, 0, 1811, 1000, true, false, 4, 1, 1, Dyes.dyeGray , 1, Arrays.asList(new MaterialStack(Steel, 1), new MaterialStack(Coal, 1), new MaterialStack(Silicon, 1))).disableAutoGeneratedBlastFurnaceRecipes(); + public static Materials EnergeticAlloy = new Materials( 366, TextureSet.SET_METALLIC , 12.0F, 1024, 3, 1|2 |64|128 , 255, 170, 81, 0, "EnergeticAlloy" , "Energetic Alloy" , 0, 0, -1, 2200, true, false, 3, 1, 1, Dyes.dyeOrange , 1, Arrays.asList(new MaterialStack(ConductiveIron, 1), new MaterialStack(Gold, 1), new MaterialStack(BlackSteel, 1))).disableAutoGeneratedBlastFurnaceRecipes(); + public static Materials VibrantAlloy = new Materials( 367, TextureSet.SET_METALLIC , 18.0F, 4048, 4, 1|2 |64|128 , 157, 188, 53, 0, "VibrantAlloy" , "Vibrant Alloy" , 0, 0, 3300, 3300, true, false, 4, 1, 1, Dyes.dyeLime , 1, Arrays.asList(new MaterialStack(EnergeticAlloy, 1), new MaterialStack(EnderEye, 1), new MaterialStack(Chrome, 1))).disableAutoGeneratedBlastFurnaceRecipes().disableAutoGeneratedVacuumFreezerRecipe(); + public static Materials PulsatingIron = new Materials( 378, TextureSet.SET_METALLIC , 6.0F, 256, 3, 1|2 |64|128 , 128, 246, 155, 0, "PulsatingIron" , "Pulsating Iron" , 0, 0, -1, 1800, true, false, 4, 1, 1, Dyes.dyeLime , 1, Arrays.asList(new MaterialStack(Iron, 1), new MaterialStack(EnderPearl, 1), new MaterialStack(RedstoneAlloy, 1))).disableAutoGeneratedBlastFurnaceRecipes(); + public static Materials DarkSteel = new Materials( 364, TextureSet.SET_METALLIC , 8.0F, 512, 3, 1|2 |64|128 , 80, 70, 80, 0, "DarkSteel" , "Dark Steel" , 0, 0, -1, 1800, true, false, 3, 1, 1, Dyes.dyePurple , 1, Arrays.asList(new MaterialStack(ElectricalSteel, 1), new MaterialStack(Coal, 1), new MaterialStack(Obsidian, 1))).disableAutoGeneratedBlastFurnaceRecipes(); + public static Materials EndSteel = new Materials( 401, TextureSet.SET_METALLIC , 12.0F, 2000, 4, 1|2 |64|128 , 223, 217, 165, 0, "EndSteel" , "End Steel" , 0, 0, 940, 3600, true, false, 3, 1, 1, Dyes.dyeYellow , 1, Arrays.asList(new MaterialStack(DarkSteel, 1), new MaterialStack(Tungsten, 1), new MaterialStack(Endstone, 1))).disableAutoGeneratedBlastFurnaceRecipes(); + public static Materials CrudeSteel = new Materials( 402, TextureSet.SET_METALLIC , 2.0F, 64, 2, 1|2 |64|128 , 163, 158, 154, 0, "CrudeSteel" , "Clay Compound" , 0, 0, -1, 1000, false, false, 4, 1, 1, Dyes.dyeGray , 1, Arrays.asList(new MaterialStack(Stone, 1), new MaterialStack(Clay, 1), new MaterialStack(Flint, 1))).disableAutoGeneratedBlastFurnaceRecipes(); + public static Materials CrystallineAlloy = new Materials( 403, TextureSet.SET_METALLIC , 18.0F, 768, 4, 1|2 |64|128 , 114, 197, 197, 0, "CrystallineAlloy" , "Crystalline Alloy" , 0, 0, 4500, 4500, true, false, 4, 1, 1, Dyes.dyeCyan , 1, Arrays.asList(new MaterialStack(Gold, 1), new MaterialStack(Diamond, 1), new MaterialStack(PulsatingIron, 1))).disableAutoGeneratedBlastFurnaceRecipes().disableAutoGeneratedVacuumFreezerRecipe(); + public static Materials MelodicAlloy = new Materials( 404, TextureSet.SET_METALLIC , 24.0F, 1024, 5, 1|2 |64|128 , 136, 98, 136, 0, "MelodicAlloy" , "Melodic Alloy" , 0, 0, 5400, 5400, true, false, 4, 1, 1, Dyes.dyeMagenta , 1, Arrays.asList(new MaterialStack(EndSteel, 1), new MaterialStack(EnderEye, 1), new MaterialStack(Oriharukon, 1))).disableAutoGeneratedBlastFurnaceRecipes().disableAutoGeneratedVacuumFreezerRecipe(); + public static Materials StellarAlloy = new Materials( 405, TextureSet.SET_METALLIC , 96.0F, 10240, 7, 1|2 |64|128 , 217, 220, 203, 0, "StellarAlloy" , "Stellar Alloy" , 0, 0, 7200, 7200, true, false, 4, 1, 1, Dyes.dyeWhite , 1, Arrays.asList(new MaterialStack(NetherStar, 1), new MaterialStack(MelodicAlloy, 1), new MaterialStack(Naquadah, 1))).disableAutoGeneratedBlastFurnaceRecipes().disableAutoGeneratedVacuumFreezerRecipe(); + public static Materials CrystallinePinkSlime = new Materials( 406, TextureSet.SET_METALLIC , 6.0F, 128, 3, 1|2 |64|128 , 231, 158, 219, 0, "CrystallinePinkSlime" , "Crystalline Pink Slime" , 0, 0, 5000, 5000, true, false, 4, 1, 1, Dyes.dyePink , 1, Arrays.asList(new MaterialStack(CrystallineAlloy, 1), new MaterialStack(Diamond, 1))).disableAutoGeneratedBlastFurnaceRecipes().disableAutoGeneratedVacuumFreezerRecipe(); + public static Materials EnergeticSilver = new Materials( 407, TextureSet.SET_METALLIC , 8.0F, 512, 3, 1|2 |64|128 , 149, 183, 205, 0, "EnergeticSilver" , "Energetic Silver" , 0, 0, -1, 2200, true, false, 4, 1, 1, Dyes.dyeLightBlue , 1, Arrays.asList(new MaterialStack(Silver, 1), new MaterialStack(ConductiveIron, 1), new MaterialStack(BlackSteel, 1))).disableAutoGeneratedBlastFurnaceRecipes(); + public static Materials VividAlloy = new Materials( 408, TextureSet.SET_METALLIC , 12.0F, 768, 4, 1|2 |64|128 , 70, 188, 219, 0, "VividAlloy" , "Vivid Alloy" , 0, 0, 3300, 3300, true, false, 4, 1, 1, Dyes.dyeBlue , 1, Arrays.asList(new MaterialStack(EnergeticSilver, 1), new MaterialStack(EnderEye, 1), new MaterialStack(Chrome, 1))).disableAutoGeneratedBlastFurnaceRecipes().disableAutoGeneratedVacuumFreezerRecipe(); + public static Materials Enderium = new Materials( 321, TextureSet.SET_DULL , 8.0F, 1500, 3, 1|2 |64|128 , 89, 145, 135, 0, "Enderium" , "Enderium" , 0, 0, 4500, 4500, true, false, 1, 1, 1, Dyes.dyeGreen , 1, Arrays.asList(new MaterialStack(EnderiumBase, 2), new MaterialStack(Thaumium, 1), new MaterialStack(EnderPearl, 1)), Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 2), new TC_AspectStack(TC_Aspects.ALIENIS, 1))).disableAutoGeneratedBlastFurnaceRecipes().disableAutoGeneratedVacuumFreezerRecipe(); + public static Materials Mithril = new Materials( 331, TextureSet.SET_SHINY , 32.0F, 64, 2, 1|2 |8 |64 , 255, 255, 210, 0, "Mithril" , "Mithril" , 0, 0, 6600, 6600, true, false, 4, 3, 2, Dyes.dyeLightBlue , 2, Arrays.asList(new MaterialStack(Platinum, 2), new MaterialStack(Thaumium, 1))).disableAutoGeneratedBlastFurnaceRecipes().setTurbineMultipliers(22, 1, 1); + public static Materials BlueAlloy = new Materials( 309, TextureSet.SET_DULL , 1.0F, 0, 0, 1|2 , 100, 180, 255, 0, "BlueAlloy" , "Blue Alloy" , 0, 0, -1, 0, false, false, 3, 5, 1, Dyes.dyeLightBlue , 2, Arrays.asList(new MaterialStack(Silver, 1), new MaterialStack(Electrotine, 4)), Collections.singletonList(new TC_AspectStack(TC_Aspects.ELECTRUM, 3))); + public static Materials ShadowIron = new Materials( 336, TextureSet.SET_METALLIC , 32.0F, 10240, 2, 1|2 |8 |64 , 120, 120, 120, 0, "ShadowIron" , "Shadow Iron" , 0, 0, 8400, 8400, true, false, 3, 4, 3, Dyes.dyeBlack , 2, Arrays.asList(new MaterialStack(Iron, 1), new MaterialStack(Thaumium, 3))).disableAutoGeneratedBlastFurnaceRecipes().setTurbineMultipliers(1, 76, 1); + public static Materials ShadowSteel = new Materials( 337, TextureSet.SET_METALLIC , 6.0F, 768, 4, 1|2 |64 , 90, 90, 90, 0, "ShadowSteel" , "Shadow Steel" , 0, 0, -1, 1700, true, false, 4, 4, 3, Dyes.dyeBlack , 2, Arrays.asList(new MaterialStack(Steel, 1), new MaterialStack(Thaumium, 3))); + public static Materials AstralSilver = new Materials( 333, TextureSet.SET_SHINY , 10.0F, 64, 2, 1|2 |64 , 230, 230, 255, 0, "AstralSilver" , "Astral Silver" , 0, 0, -1, 0, false, false, 4, 3, 2, Dyes.dyeWhite , 2, Arrays.asList(new MaterialStack(Silver, 2), new MaterialStack(Thaumium, 1))); + + /** + * Op materials (draconic evolution above) + */ + public static Materials InfinityCatalyst = new Materials( 394, TextureSet.SET_SHINY , 64.0F,1310720, 10, 1|2 |8 |64|128 , 255, 255, 255, 0, "InfinityCatalyst" , "Infinity Catalyst" , 5, 500000, 10800, 10800, true, false, 20, 1, 1, Dyes.dyeLightGray ).setProcessingMaterialTierEU(TierEU.RECIPE_UV).disableAutoGeneratedVacuumFreezerRecipe(); + public static Materials Infinity = new Materials( 397, new TextureSet("infinity", true), 256.0F,2621440, 17, 1|2 |64|128 , 255, 255, 255, 0, "Infinity" , "Infinity" , 5, 5000000, 10800, 10800, true, false, 40, 1, 1, Dyes.dyeLightGray ).setProcessingMaterialTierEU(TierEU.RECIPE_UV).disableAutoGeneratedVacuumFreezerRecipe(); + public static Materials Bedrockium = new MaterialBuilder(395,TextureSet.SET_DULL, "Bedrockium").addOreItems().addDustItems().addMetalItems().setDurability(327680).setToolSpeed(8f).setToolQuality(9).setRGB(50,50,50).setName("Bedrockium").setBlastFurnaceRequired(true).setBlastFurnaceTemp(9900).setMeltingPoint(9900).setColor(Dyes.dyeBlack).setOreValue(4).setDensityDivider(1).setDensityMultiplier(1).constructMaterial().setProcessingMaterialTierEU(TierEU.RECIPE_EV).disableAutoGeneratedVacuumFreezerRecipe(); + public static Materials Trinium = new Materials( 868, TextureSet.SET_SHINY , 128.0F, 51200, 8, 1|2 |8 |64|128 , 200, 200, 210, 0, "Trinium" , "Trinium" , 0, 0, 7200, 7200, true, false, 4, 1, 1, Dyes.dyeLightGray ).disableAutoGeneratedBlastFurnaceRecipes().disableAutoGeneratedVacuumFreezerRecipe(); + public static Materials Ichorium = new Materials( 978, TextureSet.SET_SHINY , 32.0F, 850000, 12, 1|2 |8 |32|64|128 , 211, 120, 6, 0, "Ichorium" , "Ichorium" , 5, 250000, 9000, 9000, true, false, 4, 1, 1, Dyes.dyeOrange ).setTurbineMultipliers(30, 30, 3).setHasCorrespondingPlasma(true); + public static Materials CosmicNeutronium = new Materials( 982, TextureSet.SET_SHINY , 96.0F, 163840, 12, 1|2 |8 |32|64|128 , 50, 50, 50, 0, "CosmicNeutronium" , "Cosmic Neutronium" , 0, 0, 9900, 9900, true, false, 4, 1, 1, Dyes.dyeBlack ).setProcessingMaterialTierEU(TierEU.RECIPE_ZPM).disableAutoGeneratedVacuumFreezerRecipe().setHasCorrespondingPlasma(true); + + // Superconductor base. + public static Materials Pentacadmiummagnesiumhexaoxid = new Materials( 987, TextureSet.SET_SHINY , 1.0F, 0, 3, 1|2 , 85, 85, 85, 0, "Pentacadmiummagnesiumhexaoxid" , "Superconductor Base MV" , 0, 0, 2500, 2500, true, false, 1, 1, 1, Dyes.dyeGray , 1, Arrays.asList(new MaterialStack(Cadmium, 5), new MaterialStack(Magnesium, 1), new MaterialStack(Oxygen, 6)), Collections.singletonList(new TC_AspectStack(TC_Aspects.ELECTRUM, 3))).disableAutoGeneratedVacuumFreezerRecipe(); + public static Materials Titaniumonabariumdecacoppereikosaoxid = new Materials( 988, TextureSet.SET_METALLIC , 1.0F, 0, 3, 1|2 , 51, 25, 0, 0, "Titaniumonabariumdecacoppereikosaoxid" , "Superconductor Base HV" , 0, 0, 3300, 3300, true, false, 1, 1, 1, Dyes.dyeBrown , 1, Arrays.asList(new MaterialStack(Titanium, 1), new MaterialStack(Barium, 9), new MaterialStack(Copper, 10), new MaterialStack(Oxygen, 20)), Collections.singletonList(new TC_AspectStack(TC_Aspects.ELECTRUM, 6))).disableAutoGeneratedVacuumFreezerRecipe(); + public static Materials Uraniumtriplatinid = new Materials( 989, TextureSet.SET_SHINY , 1.0F, 0, 3, 1|2 , 0,135, 0, 0, "Uraniumtriplatinid" , "Superconductor Base EV" , 0, 0, 4400, 4400, true, false, 1, 1, 1, Dyes.dyeLime , 1, Arrays.asList(new MaterialStack(Uranium, 1), new MaterialStack(Platinum, 3)), Collections.singletonList(new TC_AspectStack(TC_Aspects.ELECTRUM, 9))).disableAutoGeneratedVacuumFreezerRecipe(); + public static Materials Vanadiumtriindinid = new Materials( 990, TextureSet.SET_SHINY , 1.0F, 0, 3, 1|2 , 51, 0, 51, 0, "Vanadiumtriindinid" , "Superconductor Base IV" , 0, 0, 5200, 5200, true, false, 1, 1, 1, Dyes.dyeMagenta , 1, Arrays.asList(new MaterialStack(Vanadium , 1), new MaterialStack(Indium, 3)), Collections.singletonList(new TC_AspectStack(TC_Aspects.ELECTRUM, 12))).disableAutoGeneratedVacuumFreezerRecipe(); + public static Materials Tetraindiumditindibariumtitaniumheptacoppertetrakaidekaoxid = new Materials( 991, TextureSet.SET_METALLIC , 1.0F, 0, 3, 1|2 , 153, 76, 0, 0, "Tetraindiumditindibariumtitaniumheptacoppertetrakaidekaoxid" , "Superconductor Base LuV" , 0, 0, 6000, 6000, true, false, 1, 1, 1, Dyes.dyeBrown , 1, Arrays.asList(new MaterialStack(Indium, 4), new MaterialStack(Tin, 2), new MaterialStack(Barium, 2), new MaterialStack(Titanium, 1), new MaterialStack(Copper, 7), new MaterialStack(Oxygen, 14)), Collections.singletonList(new TC_AspectStack(TC_Aspects.ELECTRUM, 15))).disableAutoGeneratedVacuumFreezerRecipe(); + public static Materials Tetranaquadahdiindiumhexaplatiumosminid = new Materials( 992, TextureSet.SET_METALLIC , 1.0F, 0, 3, 1|2 , 10, 10, 10, 0, "Tetranaquadahdiindiumhexaplatiumosminid" , "Superconductor Base ZPM" , 0, 0, 9000, 9000, true, false, 1, 1, 1, Dyes.dyeBlack , 1, Arrays.asList(new MaterialStack(Naquadah, 4), new MaterialStack(Indium, 2), new MaterialStack(Palladium, 6), new MaterialStack(Osmium, 1)), Collections.singletonList(new TC_AspectStack(TC_Aspects.ELECTRUM, 18))).disableAutoGeneratedVacuumFreezerRecipe(); + public static Materials Longasssuperconductornameforuvwire = new Materials( 986, TextureSet.SET_METALLIC , 1.0F, 0, 3, 1|2 , 224,210, 7, 0, "Longasssuperconductornameforuvwire" , "Superconductor Base UV" , 0, 0, 9900, 9900, true, false, 1, 1, 1, Dyes.dyeYellow , 1, Arrays.asList(new MaterialStack(Naquadria, 4), new MaterialStack(Osmiridium, 3), new MaterialStack(Europium, 1), new MaterialStack(Samarium, 1)), Collections.singletonList(new TC_AspectStack(TC_Aspects.ELECTRUM, 21))).setProcessingMaterialTierEU(TierEU.RECIPE_LuV).disableAutoGeneratedVacuumFreezerRecipe(); + public static Materials Longasssuperconductornameforuhvwire = new Materials( 985, TextureSet.SET_SHINY , 1.0F, 0, 3, 1|2 , 38,129, 189, 0, "Longasssuperconductornameforuhvwire" , "Superconductor Base UHV" , 0, 0, 10800, 10800, true, false, 1, 1, 1, Dyes.dyeWhite , 1, Arrays.asList(new MaterialStack(Draconium, 6), new MaterialStack(CosmicNeutronium, 7), new MaterialStack(Tritanium, 5), new MaterialStack(Americium, 6)), Collections.singletonList(new TC_AspectStack(TC_Aspects.ELECTRUM, 24))).setProcessingMaterialTierEU(TierEU.RECIPE_ZPM).disableAutoGeneratedVacuumFreezerRecipe(); + public static Materials SuperconductorUEVBase = new Materials( 974, TextureSet.SET_SHINY , 1.0F, 0, 3, 1|2 , 174, 8, 8, 0, "SuperconductorUEVBase" , "Superconductor Base UEV" , 0, 0, 11700, 11800, true, false, 1, 1, 1, Dyes.dyeWhite, Collections.singletonList(new TC_AspectStack(TC_Aspects.ELECTRUM, 27))).setProcessingMaterialTierEU(TierEU.RECIPE_UV).disableAutoGeneratedVacuumFreezerRecipe(); + public static Materials SuperconductorUIVBase = new Materials( 131, TextureSet.SET_SHINY , 1.0F, 0, 3, 1|2 , 229, 88, 177, 0, "SuperconductorUIVBase" , "Superconductor Base UIV" , 0, 0, 12700, 12700, true, false, 1, 1, 1, Dyes.dyeWhite, Collections.singletonList(new TC_AspectStack(TC_Aspects.ELECTRUM, 34))).setProcessingMaterialTierEU(TierEU.RECIPE_UHV).disableAutoGeneratedVacuumFreezerRecipe(); + public static Materials SuperconductorUMVBase = new Materials( 134, TextureSet.SET_SHINY , 1.0F, 0, 3, 1|2 , 181, 38, 205, 0, "SuperconductorUMVBase" , "Superconductor Base UMV" , 0, 0, 13600, 13600, true, false, 1, 1, 1, Dyes.dyeWhite, Collections.singletonList(new TC_AspectStack(TC_Aspects.ELECTRUM, 40))).setProcessingMaterialTierEU(TierEU.RECIPE_UEV).disableAutoGeneratedVacuumFreezerRecipe(); + + // Superconductors. + public static Materials SuperconductorMV = new Materials( -1, TextureSet.SET_SHINY , 1.0F, 0, 0, 0 , 85, 85, 85, 0, "SuperconductorMV" , "Superconductor MV" , 0, 0, -1, -1, false, false, 1, 1, 1, Dyes.dyeGray , Collections.singletonList(new TC_AspectStack(TC_Aspects.ELECTRUM, 6))); + public static Materials SuperconductorHV = new Materials( -1, TextureSet.SET_SHINY , 1.0F, 0, 0, 0 , 51, 25, 0, 0, "SuperconductorHV" , "Superconductor HV" , 0, 0, -1, -1, false, false, 1, 1, 1, Dyes.dyeBrown , Collections.singletonList(new TC_AspectStack(TC_Aspects.ELECTRUM, 12))); + public static Materials SuperconductorEV = new Materials( -1, TextureSet.SET_SHINY , 1.0F, 0, 0, 0 , 0,135, 0, 0, "SuperconductorEV" , "Superconductor EV" , 0, 0, -1, -1, false, false, 1, 1, 1, Dyes.dyeLime , Collections.singletonList(new TC_AspectStack(TC_Aspects.ELECTRUM, 18))); + public static Materials SuperconductorIV = new Materials( -1, TextureSet.SET_SHINY , 1.0F, 0, 0, 0 , 51, 0, 51, 0, "SuperconductorIV" , "Superconductor IV" , 0, 0, -1, -1, false, false, 1, 1, 1, Dyes.dyeMagenta , Collections.singletonList(new TC_AspectStack(TC_Aspects.ELECTRUM, 24))); + public static Materials SuperconductorLuV = new Materials( -1, TextureSet.SET_SHINY , 1.0F, 0, 0, 0 , 153, 76, 0, 0, "SuperconductorLuV" , "Superconductor LuV" , 0, 0, -1, -1, false, false, 1, 1, 1, Dyes.dyeBrown , Collections.singletonList(new TC_AspectStack(TC_Aspects.ELECTRUM, 30))); + public static Materials SuperconductorZPM = new Materials( -1, TextureSet.SET_SHINY , 1.0F, 0, 0, 0 , 10, 10, 10, 0, "SuperconductorZPM" , "Superconductor ZPM" , 0, 0, -1, -1, false, false, 1, 1, 1, Dyes.dyeBlack , Collections.singletonList(new TC_AspectStack(TC_Aspects.ELECTRUM, 36))); + public static Materials SuperconductorUV = new Materials( -1, TextureSet.SET_SHINY , 1.0F, 0, 0, 0 , 224,210, 7, 0, "SuperconductorUV" , "Superconductor UV" , 0, 0, -1, -1, false, false, 1, 1, 1, Dyes.dyeYellow , Collections.singletonList(new TC_AspectStack(TC_Aspects.ELECTRUM, 42))); + public static Materials SuperconductorUHV = new Materials( -1, TextureSet.SET_SHINY , 1.0F, 0, 0, 0 , 38,129, 189, 0, "Superconductor" , "Superconductor UHV" , 0, 0, -1, -1, false, false, 1, 1, 1, Dyes.dyeWhite , Collections.singletonList(new TC_AspectStack(TC_Aspects.ELECTRUM, 48))); + public static Materials SuperconductorUEV = new Materials( -1, TextureSet.SET_SHINY , 1.0F, 0, 0, 0 , 174, 8, 8, 0, "SuperconductorUEV" , "Superconductor UEV" , 0, 0, -1, -1, false, false, 1, 1, 1, Dyes.dyeWhite , Collections.singletonList(new TC_AspectStack(TC_Aspects.ELECTRUM, 54))); + public static Materials SuperconductorUIV = new Materials( -1, TextureSet.SET_SHINY , 1.0F, 0, 0, 0 , 229, 88, 177, 0, "SuperconductorUIV" , "Superconductor UIV" , 0, 0, -1, -1, false, false, 1, 1, 1, Dyes.dyeWhite , Collections.singletonList(new TC_AspectStack(TC_Aspects.ELECTRUM, 60))); + public static Materials SuperconductorUMV = new Materials( -1, TextureSet.SET_SHINY , 1.0F, 0, 0, 0 , 181, 38, 205, 0, "SuperconductorUMV" , "Superconductor UMV" , 0, 0, -1, -1, false, false, 1, 1, 1, Dyes.dyeWhite , Collections.singletonList(new TC_AspectStack(TC_Aspects.ELECTRUM, 66))); + + public static Materials SuperCoolant = new MaterialBuilder( 140, TextureSet.SET_DULL,"Super Coolant").setRGB(2, 91, 111).addCell().addFluid().constructMaterial().setLiquidTemperature(1); + + public static Materials EnrichedHolmium = new Materials(582, TextureSet.SET_METALLIC , 1.0F, 0, 2, 2 , 18, 100, 255, 0, "EnrichedHolmium" , "Enriched Holmium" , -1, -1, 0, 3000, true, false,200, 1, 1, Dyes.dyePurple); + + public static Materials TengamPurified = new MaterialBuilder(111, TextureSet.SET_METALLIC, "Purified Tengam").addDustItems().addGearItems().addMetalItems().addToolHeadItems().setAspects(Arrays.asList(new TC_AspectStack(TC_Aspects.MAGNETO, 2), new TC_AspectStack(TC_Aspects.ELECTRUM, 2))).setColor(Dyes.dyeLime).setName("TengamPurified").setRGB(186, 223, 112).constructMaterial().setProcessingMaterialTierEU(TierEU.RECIPE_UV); + public static Materials TengamAttuned = new MaterialBuilder(112, TextureSet.SET_MAGNETIC, "Attuned Tengam") .addDustItems().addGearItems().addMetalItems().addToolHeadItems().setAspects(Arrays.asList(new TC_AspectStack(TC_Aspects.MAGNETO, 4), new TC_AspectStack(TC_Aspects.ELECTRUM, 1))).setColor(Dyes.dyeLime).setName("TengamAttuned") .setRGB(213, 255, 128).constructMaterial().setProcessingMaterialTierEU(TierEU.RECIPE_UV); + public static Materials TengamRaw = new MaterialBuilder(110, TextureSet.SET_ROUGH, "Raw Tengam") .addOreItems() .setAspects(Arrays.asList(new TC_AspectStack(TC_Aspects.MAGNETO, 1), new TC_AspectStack(TC_Aspects.ELECTRUM, 4))).setColor(Dyes.dyeLime).setName("TengamRaw") .setRGB(160, 191, 96).constructMaterial().setProcessingMaterialTierEU(TierEU.RECIPE_UV); + + // spotless:on + + static { + MaterialsBotania.init(); + } + + static { + MaterialsKevlar.init(); + } + + static { + MaterialsOreAlum.init(); + } + + static { + MaterialsUEVplus.init(); + } + + /** + * Superconductor re-routed for mod compatibility. Circuits are re-routed into SuperconductorUHV as well. + * <p> + * Internal name is now Superconductor while translated name is SuperconductorUHV. + * </p> + * + * @deprecated Use {@link #SuperconductorUHV} instead + */ + @Deprecated + public static Materials Superconductor = new Materials(SuperconductorUHV, true); // new Materials( -1, + // TextureSet.SET_NONE , 1.0F, 0, + // 0, 0 + // , 255, 255, 255, 0, "Superconductor" , "Superconductor" , 0, + // 0, -1, 0, false, false, 1, 1, 1, Dyes.dyeLightGray , Arrays.asList(new + // TC_AspectStack(TC_Aspects.ELECTRUM, 9))); + + @Deprecated + public static Materials Nikolite = new Materials(Electrotine, false); + + @Deprecated + public static Materials Phosphor = new Materials(Phosphorus, false); + + private static Materials[] MATERIALS_ARRAY = new Materials[] {}; + + static { + initSubTags(); + + setReRegistration(); + setMaceratingInto(); + setSmeltingInto(); + setDirectSmelting(); + setOthers(); + setMultipliers(); + setEnchantments(); + setHeatDamage(); + setByProducts(); + setColors(); + + overrideChemicalFormulars(); + } + + public final short[] mRGBa = new short[] { 255, 255, 255, 0 }, mMoltenRGBa = new short[] { 255, 255, 255, 0 }; + public TextureSet mIconSet; + public GT_GeneratedMaterial_Renderer renderer; + public List<MaterialStack> mMaterialList = new ArrayList<>(); + public List<Materials> mOreByProducts = new ArrayList<>(), mOreReRegistrations = new ArrayList<>(); + public List<TC_Aspects.TC_AspectStack> mAspects = new ArrayList<>(); + public ArrayList<ItemStack> mMaterialItems = new ArrayList<>(); + public Collection<SubTag> mSubTags = new LinkedHashSet<>(); + public Enchantment mEnchantmentTools = null, mEnchantmentArmors = null; + public boolean mUnificatable, mBlastFurnaceRequired = false, mAutoGenerateBlastFurnaceRecipes = true, + mAutoGenerateVacuumFreezerRecipes = true, mAutoGenerateRecycleRecipes = true, mTransparent = false, + mHasParentMod = true, mHasPlasma = false, mHasGas = false, mCustomOre = false; + public byte mEnchantmentToolsLevel = 0, mEnchantmentArmorsLevel = 0, mToolQuality = 0; + public short mBlastFurnaceTemp = 0; + public int mMeltingPoint = 0; + public int mGasTemp = 0; + public int mMetaItemSubID; + public int mTypes = 0; + public int mDurability = 16; + public int mFuelPower = 0; + public int mFuelType = 0; + public int mExtraData = 0; + public int mOreValue = 0; + public int mOreMultiplier = 1; + public int mByProductMultiplier = 1; + public int mSmeltingMultiplier = 1; + public int mDensityMultiplier = 1; + public int mDensityDivider = 1; + + public int getProcessingMaterialTierEU() { + return processingMaterialTierEU; + } + + public Materials setProcessingMaterialTierEU(final long processingMaterialTierEU) { + this.processingMaterialTierEU = (int) processingMaterialTierEU; + return this; + } + + public int processingMaterialTierEU = 0; + public long mDensity = M; + public float mToolSpeed = 1.0F, mHeatDamage = 0.0F, mSteamMultiplier = 1.0F, mGasMultiplier = 1.0F, + mPlasmaMultiplier = 1.0F; + public String mChemicalFormula = "?", mName, mDefaultLocalName, mCustomID = "null", mConfigSection = "null", + mLocalizedName = "null"; + public Dyes mColor = Dyes._NULL; + public Element mElement = null; + public Materials mDirectSmelting = this, mOreReplacement = this, mMacerateInto = this, mSmeltInto = this, + mArcSmeltInto = this, mHandleMaterial = this, mMaterialInto; + public Fluid mSolid = null, mFluid = null, mGas = null, mPlasma = null; + /** + * This Fluid is used as standard Unit for Molten Materials. 1296 is a Molten Block, that means 144 is one Material + * Unit worth of fluid. + */ + public Fluid mStandardMoltenFluid = null; + + private boolean hasCorrespondingFluid = false, hasCorrespondingGas = false, canBeCracked = false; + private Fluid[] hydroCrackedFluids = new Fluid[3], steamCrackedFluids = new Fluid[3]; + + public Materials(int aMetaItemSubID, TextureSet aIconSet, float aToolSpeed, int aDurability, int aToolQuality, + boolean aUnificatable, String aName, String aDefaultLocalName) { + this( + aMetaItemSubID, + aIconSet, + aToolSpeed, + aDurability, + aToolQuality, + aUnificatable, + aName, + aDefaultLocalName, + "ore", + false, + "null"); + } + + public Materials(int aMetaItemSubID, TextureSet aIconSet, float aToolSpeed, int aDurability, int aToolQuality, + boolean aUnificatable, String aName, String aDefaultLocalName, String aConfigSection, boolean aCustomOre, + String aCustomID) { + mMetaItemSubID = aMetaItemSubID; + mDefaultLocalName = aDefaultLocalName; + mName = aName; + MATERIALS_MAP.put(mName, this); + mCustomOre = aCustomOre; + mCustomID = aCustomID; + mConfigSection = aConfigSection; + mUnificatable = aUnificatable; + mDurability = aDurability; + mToolSpeed = aToolSpeed; + mToolQuality = (byte) aToolQuality; + mMaterialInto = this; + mIconSet = aIconSet; + } + + public Materials(Materials aMaterialInto, boolean aReRegisterIntoThis) { + mUnificatable = false; + mDefaultLocalName = aMaterialInto.mDefaultLocalName; + mName = aMaterialInto.mName; + mMaterialInto = aMaterialInto.mMaterialInto; + if (aReRegisterIntoThis) mMaterialInto.mOreReRegistrations.add(this); + mChemicalFormula = aMaterialInto.mChemicalFormula; + mMetaItemSubID = -1; + mIconSet = TextureSet.SET_NONE; + } + + public Materials(int aMetaItemSubID, TextureSet aIconSet, float aToolSpeed, int aDurability, int aToolQuality, + int aTypes, int aR, int aG, int aB, int aA, String aName, String aDefaultLocalName, int aFuelType, + int aFuelPower, int aMeltingPoint, int aBlastFurnaceTemp, boolean aBlastFurnaceRequired, boolean aTransparent, + int aOreValue, int aDensityMultiplier, int aDensityDivider, Dyes aColor) { + this( + aMetaItemSubID, + aIconSet, + aToolSpeed, + aDurability, + aToolQuality, + aTypes, + aR, + aG, + aB, + aA, + aName, + aDefaultLocalName, + aFuelType, + aFuelPower, + aMeltingPoint, + aBlastFurnaceTemp, + aBlastFurnaceRequired, + aTransparent, + aOreValue, + aDensityMultiplier, + aDensityDivider, + aColor, + "ore", + false, + "null"); + } + + public Materials(int aMetaItemSubID, TextureSet aIconSet, float aToolSpeed, int aDurability, int aToolQuality, + int aTypes, int aR, int aG, int aB, int aA, String aName, String aDefaultLocalName, int aFuelType, + int aFuelPower, int aMeltingPoint, int aBlastFurnaceTemp, boolean aBlastFurnaceRequired, boolean aTransparent, + int aOreValue, int aDensityMultiplier, int aDensityDivider, Dyes aColor, String aConfigSection) { + this( + aMetaItemSubID, + aIconSet, + aToolSpeed, + aDurability, + aToolQuality, + aTypes, + aR, + aG, + aB, + aA, + aName, + aDefaultLocalName, + aFuelType, + aFuelPower, + aMeltingPoint, + aBlastFurnaceTemp, + aBlastFurnaceRequired, + aTransparent, + aOreValue, + aDensityMultiplier, + aDensityDivider, + aColor, + aConfigSection, + false, + "null"); + } + + /** + * @param aMetaItemSubID the Sub-ID used in my own MetaItems. Range 0-1000. -1 for no Material + * @param aTypes which kind of Items should be generated. Bitmask as follows: 1 = Dusts of all kinds. + * 2 = Dusts, Ingots, Plates, Rods/Sticks, Machine Components and other Metal specific + * things. 4 = Dusts, Gems, Plates, Lenses (if transparent). 8 = Dusts, Impure Dusts, + * crushed Ores, purified Ores, centrifuged Ores etc. 16 = Cells 32 = Plasma Cells 64 = + * Tool Heads 128 = Gears 256 = Designates something as empty (only used for the Empty + * material) + * @param aR, aG, aB Color of the Material 0-255 each. + * @param aA transparency of the Material Texture. 0 = fully visible, 255 = Invisible. + * @param aName The Name used as Default for localization. + * @param aFuelType Type of Generator to get Energy from this Material. + * @param aFuelPower EU generated. Will be multiplied by 1000, also additionally multiplied by 2 for + * Gems. + * @param aMeltingPoint Used to determine the smelting Costs in furnace. >>>>**ADD 20000 to remove EBF + * recipes to add them MANUALLY ! :D**<<<< + * @param aBlastFurnaceTemp Used to determine the needed Heat capacity Costs in Blast Furnace. + * @param aBlastFurnaceRequired If this requires a Blast Furnace. + * @param aColor Vanilla MC Wool Color which comes the closest to this. + */ + public Materials(int aMetaItemSubID, TextureSet aIconSet, float aToolSpeed, int aDurability, int aToolQuality, + int aTypes, int aR, int aG, int aB, int aA, String aName, String aDefaultLocalName, int aFuelType, + int aFuelPower, int aMeltingPoint, int aBlastFurnaceTemp, boolean aBlastFurnaceRequired, boolean aTransparent, + int aOreValue, int aDensityMultiplier, int aDensityDivider, Dyes aColor, String aConfigSection, + boolean aCustomOre, String aCustomID) { + this( + aMetaItemSubID, + aIconSet, + aToolSpeed, + aDurability, + aToolQuality, + true, + aName, + aDefaultLocalName, + aConfigSection, + aCustomOre, + aCustomID); + mMeltingPoint = aMeltingPoint; + mBlastFurnaceRequired = aBlastFurnaceRequired; + mBlastFurnaceTemp = (short) aBlastFurnaceTemp; + mTransparent = aTransparent; + mFuelPower = aFuelPower; + mFuelType = aFuelType; + mOreValue = aOreValue; + mDensityMultiplier = aDensityMultiplier; + mDensityDivider = aDensityDivider; + mDensity = (M * aDensityMultiplier) / aDensityDivider; + mColor = aColor; + mRGBa[0] = mMoltenRGBa[0] = (short) aR; + mRGBa[1] = mMoltenRGBa[1] = (short) aG; + mRGBa[2] = mMoltenRGBa[2] = (short) aB; + mRGBa[3] = mMoltenRGBa[3] = (short) aA; + mTypes = aTypes; + if (mColor != null) add(SubTag.HAS_COLOR); + if (mTransparent) add(SubTag.TRANSPARENT); + if ((mTypes & 2) != 0) add(SubTag.SMELTING_TO_FLUID); + } + + public Materials(int aMetaItemSubID, TextureSet aIconSet, float aToolSpeed, int aDurability, int aToolQuality, + int aTypes, int aR, int aG, int aB, int aA, String aName, String aDefaultLocalName, int aFuelType, + int aFuelPower, int aMeltingPoint, int aBlastFurnaceTemp, boolean aBlastFurnaceRequired, boolean aTransparent, + int aOreValue, int aDensityMultiplier, int aDensityDivider, Dyes aColor, + List<TC_Aspects.TC_AspectStack> aAspects) { + this( + aMetaItemSubID, + aIconSet, + aToolSpeed, + aDurability, + aToolQuality, + aTypes, + aR, + aG, + aB, + aA, + aName, + aDefaultLocalName, + aFuelType, + aFuelPower, + aMeltingPoint, + aBlastFurnaceTemp, + aBlastFurnaceRequired, + aTransparent, + aOreValue, + aDensityMultiplier, + aDensityDivider, + aColor); + mAspects.addAll(aAspects); + } + + public Materials(int aMetaItemSubID, TextureSet aIconSet, float aToolSpeed, int aDurability, int aToolQuality, + int aTypes, int aR, int aG, int aB, int aA, String aName, String aDefaultLocalName, int aFuelType, + int aFuelPower, int aMeltingPoint, int aBlastFurnaceTemp, boolean aBlastFurnaceRequired, boolean aTransparent, + int aOreValue, int aDensityMultiplier, int aDensityDivider, Dyes aColor, Element aElement, + List<TC_Aspects.TC_AspectStack> aAspects) { + this( + aMetaItemSubID, + aIconSet, + aToolSpeed, + aDurability, + aToolQuality, + aTypes, + aR, + aG, + aB, + aA, + aName, + aDefaultLocalName, + aFuelType, + aFuelPower, + aMeltingPoint, + aBlastFurnaceTemp, + aBlastFurnaceRequired, + aTransparent, + aOreValue, + aDensityMultiplier, + aDensityDivider, + aColor); + mElement = aElement; + mElement.mLinkedMaterials.add(this); + if (aElement == Element._NULL) { + mChemicalFormula = "Empty"; + } else { + mChemicalFormula = aElement.toString(); + mChemicalFormula = mChemicalFormula.replaceAll("_", "-"); + } + mAspects.addAll(aAspects); + } + + public Materials(int aMetaItemSubID, TextureSet aIconSet, float aToolSpeed, int aDurability, int aToolQuality, + int aTypes, int aR, int aG, int aB, int aA, String aName, String aDefaultLocalName, int aFuelType, + int aFuelPower, int aMeltingPoint, int aBlastFurnaceTemp, boolean aBlastFurnaceRequired, boolean aTransparent, + int aOreValue, int aDensityMultiplier, int aDensityDivider, Dyes aColor, int aExtraData, + List<MaterialStack> aMaterialList) { + this( + aMetaItemSubID, + aIconSet, + aToolSpeed, + aDurability, + aToolQuality, + aTypes, + aR, + aG, + aB, + aA, + aName, + aDefaultLocalName, + aFuelType, + aFuelPower, + aMeltingPoint, + aBlastFurnaceTemp, + aBlastFurnaceRequired, + aTransparent, + aOreValue, + aDensityMultiplier, + aDensityDivider, + aColor, + aExtraData, + aMaterialList, + null); + } + + public Materials(int aMetaItemSubID, TextureSet aIconSet, float aToolSpeed, int aDurability, int aToolQuality, + int aTypes, int aR, int aG, int aB, int aA, String aName, String aDefaultLocalName, int aFuelType, + int aFuelPower, int aMeltingPoint, int aBlastFurnaceTemp, boolean aBlastFurnaceRequired, boolean aTransparent, + int aOreValue, int aDensityMultiplier, int aDensityDivider, Dyes aColor, int aExtraData, + List<MaterialStack> aMaterialList, List<TC_Aspects.TC_AspectStack> aAspects) { + this( + aMetaItemSubID, + aIconSet, + aToolSpeed, + aDurability, + aToolQuality, + aTypes, + aR, + aG, + aB, + aA, + aName, + aDefaultLocalName, + aFuelType, + aFuelPower, + aMeltingPoint, + aBlastFurnaceTemp, + aBlastFurnaceRequired, + aTransparent, + aOreValue, + aDensityMultiplier, + aDensityDivider, + aColor); + mExtraData = aExtraData; + mMaterialList.addAll(aMaterialList); + if (mMaterialList.size() == 1) mChemicalFormula = mMaterialList.get(0) + .toString(true); + else mChemicalFormula = mMaterialList.stream() + .map(MaterialStack::toString) + .collect(Collectors.joining()) + .replaceAll("_", "-"); + + int tAmountOfComponents = 0, tMeltingPoint = 0; + for (MaterialStack tMaterial : mMaterialList) { + tAmountOfComponents += tMaterial.mAmount; + if (tMaterial.mMaterial.mMeltingPoint > 0) + tMeltingPoint += tMaterial.mMaterial.mMeltingPoint * tMaterial.mAmount; + if (aAspects == null) for (TC_Aspects.TC_AspectStack tAspect : tMaterial.mMaterial.mAspects) + tAspect.addToAspectList(mAspects); + } + + if (mMeltingPoint < 0) mMeltingPoint = (short) (tMeltingPoint / tAmountOfComponents); + + tAmountOfComponents *= aDensityMultiplier; + tAmountOfComponents /= aDensityDivider; + if (aAspects == null) for (TC_Aspects.TC_AspectStack tAspect : mAspects) + tAspect.mAmount = Math.max(1, tAspect.mAmount / Math.max(1, tAmountOfComponents)); + else mAspects.addAll(aAspects); + } + + private static void setSmeltingInto() { + SamariumMagnetic.setSmeltingInto(Samarium) + .setMaceratingInto(Samarium) + .setArcSmeltingInto(Samarium); + NeodymiumMagnetic.setSmeltingInto(Neodymium) + .setMaceratingInto(Neodymium) + .setArcSmeltingInto(Neodymium); + SteelMagnetic.setSmeltingInto(Steel) + .setMaceratingInto(Steel) + .setArcSmeltingInto(Steel); + Iron.setSmeltingInto(Iron) + .setMaceratingInto(Iron) + .setArcSmeltingInto(WroughtIron); + AnyIron.setSmeltingInto(Iron) + .setMaceratingInto(Iron) + .setArcSmeltingInto(WroughtIron); + PigIron.setSmeltingInto(Iron) + .setMaceratingInto(Iron) + .setArcSmeltingInto(WroughtIron); + WroughtIron.setSmeltingInto(WroughtIron) + .setMaceratingInto(WroughtIron) + .setArcSmeltingInto(WroughtIron); + IronMagnetic.setSmeltingInto(Iron) + .setMaceratingInto(Iron) + .setArcSmeltingInto(WroughtIron); + Copper.setSmeltingInto(Copper) + .setMaceratingInto(Copper) + .setArcSmeltingInto(AnnealedCopper); + AnyCopper.setSmeltingInto(Copper) + .setMaceratingInto(Copper) + .setArcSmeltingInto(AnnealedCopper); + AnnealedCopper.setSmeltingInto(AnnealedCopper) + .setMaceratingInto(AnnealedCopper) + .setArcSmeltingInto(AnnealedCopper); + Netherrack.setSmeltingInto(NetherBrick); + MeatRaw.setSmeltingInto(MeatCooked); + Sand.setSmeltingInto(Glass); + Ice.setSmeltingInto(Water); + Snow.setSmeltingInto(Water); + TengamAttuned.setSmeltingInto(TengamPurified) + .setMaceratingInto(TengamPurified) + .setArcSmeltingInto(TengamPurified); + } + + private static void setOthers() { + Mercury.add(SubTag.SMELTING_TO_GEM); + BandedIron.setOreReplacement(RoastedIron); + Garnierite.setOreReplacement(RoastedNickel); + } + + private static void setDirectSmelting() { + Cinnabar.setDirectSmelting(Mercury) + .add(SubTag.INDUCTIONSMELTING_LOW_OUTPUT) + .add(SubTag.SMELTING_TO_GEM); + Tetrahedrite.setDirectSmelting(Copper) + .add(SubTag.INDUCTIONSMELTING_LOW_OUTPUT) + .add(SubTag.DONT_ADD_DEFAULT_BBF_RECIPE); + Chalcopyrite.setDirectSmelting(Copper) + .add(SubTag.INDUCTIONSMELTING_LOW_OUTPUT) + .add(SubTag.DONT_ADD_DEFAULT_BBF_RECIPE); + Malachite.setDirectSmelting(Copper) + .add(SubTag.INDUCTIONSMELTING_LOW_OUTPUT); + Pentlandite.setDirectSmelting(Nickel) + .add(SubTag.INDUCTIONSMELTING_LOW_OUTPUT); + Sphalerite.setDirectSmelting(Zinc) + .add(SubTag.INDUCTIONSMELTING_LOW_OUTPUT); + Pyrite.setDirectSmelting(Iron) + .add(SubTag.INDUCTIONSMELTING_LOW_OUTPUT); + BasalticMineralSand.setDirectSmelting(Iron) + .add(SubTag.INDUCTIONSMELTING_LOW_OUTPUT); + GraniticMineralSand.setDirectSmelting(Iron) + .add(SubTag.INDUCTIONSMELTING_LOW_OUTPUT); + YellowLimonite.setDirectSmelting(Iron) + .add(SubTag.INDUCTIONSMELTING_LOW_OUTPUT); + BrownLimonite.setDirectSmelting(Iron); + BandedIron.setDirectSmelting(Iron); + Magnetite.setDirectSmelting(Iron); + Cassiterite.setDirectSmelting(Tin); + CassiteriteSand.setDirectSmelting(Tin); + Chromite.setDirectSmelting(Chrome); + Garnierite.setDirectSmelting(Nickel); + Cobaltite.setDirectSmelting(Cobalt); + Stibnite.setDirectSmelting(Antimony); + Cooperite.setDirectSmelting(Platinum) + .add(SubTag.DONT_ADD_DEFAULT_BBF_RECIPE); + Molybdenite.setDirectSmelting(Molybdenum) + .add(SubTag.DONT_ADD_DEFAULT_BBF_RECIPE); + Galena.setDirectSmelting(Lead); + RoastedIron.setDirectSmelting(Iron); + RoastedAntimony.setDirectSmelting(Antimony); + RoastedLead.setDirectSmelting(Lead); + RoastedArsenic.setDirectSmelting(Arsenic); + RoastedCobalt.setDirectSmelting(Cobalt); + RoastedZinc.setDirectSmelting(Zinc); + RoastedNickel.setDirectSmelting(Nickel); + RoastedCopper.setDirectSmelting(Copper); + } + + private static void setMultipliers() { + Amber.setOreMultiplier(2) + .setSmeltingMultiplier(2); + InfusedAir.setOreMultiplier(2) + .setSmeltingMultiplier(2); + InfusedFire.setOreMultiplier(2) + .setSmeltingMultiplier(2); + InfusedEarth.setOreMultiplier(2) + .setSmeltingMultiplier(2); + InfusedWater.setOreMultiplier(2) + .setSmeltingMultiplier(2); + InfusedEntropy.setOreMultiplier(2) + .setSmeltingMultiplier(2); + InfusedOrder.setOreMultiplier(2) + .setSmeltingMultiplier(2); + InfusedVis.setOreMultiplier(2) + .setSmeltingMultiplier(2); + InfusedDull.setOreMultiplier(2) + .setSmeltingMultiplier(2); + Salt.setOreMultiplier(2) + .setSmeltingMultiplier(2); + RockSalt.setOreMultiplier(2) + .setSmeltingMultiplier(2); + Scheelite.setOreMultiplier(2) + .setSmeltingMultiplier(2); + Tungstate.setOreMultiplier(2) + .setSmeltingMultiplier(2); + Cassiterite.setOreMultiplier(2) + .setSmeltingMultiplier(2); + CassiteriteSand.setOreMultiplier(2) + .setSmeltingMultiplier(2); + NetherQuartz.setOreMultiplier(2) + .setSmeltingMultiplier(2); + CertusQuartz.setOreMultiplier(2) + .setSmeltingMultiplier(2); + CertusQuartzCharged.setOreMultiplier(2) + .setSmeltingMultiplier(2); + TricalciumPhosphate.setOreMultiplier(3) + .setSmeltingMultiplier(3); + Saltpeter.setOreMultiplier(4) + .setSmeltingMultiplier(4); + Apatite.setOreMultiplier(4) + .setSmeltingMultiplier(4) + .setByProductMultiplier(2); + Electrotine.setOreMultiplier(5) + .setSmeltingMultiplier(5); + Teslatite.setOreMultiplier(5) + .setSmeltingMultiplier(5); + Redstone.setOreMultiplier(5) + .setSmeltingMultiplier(5); + Glowstone.setOreMultiplier(5) + .setSmeltingMultiplier(5); + Lapis.setOreMultiplier(6) + .setSmeltingMultiplier(6) + .setByProductMultiplier(4); + Sodalite.setOreMultiplier(6) + .setSmeltingMultiplier(6) + .setByProductMultiplier(4); + Lazurite.setOreMultiplier(6) + .setSmeltingMultiplier(6) + .setByProductMultiplier(4); + Monazite.setOreMultiplier(8) + .setSmeltingMultiplier(8) + .setByProductMultiplier(2); + Cryolite.setOreMultiplier(4) + .setByProductMultiplier(4); + } + + private static void setEnchantmentKnockbackTools() { + Plastic.setEnchantmentForTools(Enchantment.knockback, 1); + PolyvinylChloride.setEnchantmentForTools(Enchantment.knockback, 1); + Polystyrene.setEnchantmentForTools(Enchantment.knockback, 1); + Rubber.setEnchantmentForTools(Enchantment.knockback, 2); + StyreneButadieneRubber.setEnchantmentForTools(Enchantment.knockback, 2); + InfusedAir.setEnchantmentForTools(Enchantment.knockback, 2); + } + + private static void setEnchantmentFortuneTools() { + IronWood.setEnchantmentForTools(Enchantment.fortune, 1); + Steeleaf.setEnchantmentForTools(Enchantment.fortune, 2); + Mithril.setEnchantmentForTools(Enchantment.fortune, 3); + Vinteum.setEnchantmentForTools(Enchantment.fortune, 1); + Thaumium.setEnchantmentForTools(Enchantment.fortune, 2); + InfusedWater.setEnchantmentForTools(Enchantment.fortune, 3); + } + + private static void setEnchantmentFireAspectTools() { + Flint.setEnchantmentForTools(Enchantment.fireAspect, 1); + DarkIron.setEnchantmentForTools(Enchantment.fireAspect, 2); + Firestone.setEnchantmentForTools(Enchantment.fireAspect, 3); + FierySteel.setEnchantmentForTools(Enchantment.fireAspect, 3); + Pyrotheum.setEnchantmentForTools(Enchantment.fireAspect, 3); + Blaze.setEnchantmentForTools(Enchantment.fireAspect, 3); + InfusedFire.setEnchantmentForTools(Enchantment.fireAspect, 3); + } + + private static void setEnchantmentSilkTouchTools() { + Force.setEnchantmentForTools(Enchantment.silkTouch, 1); + Amber.setEnchantmentForTools(Enchantment.silkTouch, 1); + EnderPearl.setEnchantmentForTools(Enchantment.silkTouch, 1); + Enderium.setEnchantmentForTools(Enchantment.silkTouch, 1); + NetherStar.setEnchantmentForTools(Enchantment.silkTouch, 1); + InfusedOrder.setEnchantmentForTools(Enchantment.silkTouch, 1); + } + + private static void setEnchantmentSmiteTools() { + BlackBronze.setEnchantmentForTools(Enchantment.smite, 2); + Gold.setEnchantmentForTools(Enchantment.smite, 3); + RoseGold.setEnchantmentForTools(Enchantment.smite, 4); + Platinum.setEnchantmentForTools(Enchantment.smite, 5); + InfusedVis.setEnchantmentForTools(Enchantment.smite, 5); + Ichorium.setEnchantmentForTools(Enchantment.smite, 8); + } + + private static void setEnchantmentBaneOfArthropodsTools() { + Lead.setEnchantmentForTools(Enchantment.baneOfArthropods, 2); + Nickel.setEnchantmentForTools(Enchantment.baneOfArthropods, 2); + Invar.setEnchantmentForTools(Enchantment.baneOfArthropods, 3); + Antimony.setEnchantmentForTools(Enchantment.baneOfArthropods, 3); + BatteryAlloy.setEnchantmentForTools(Enchantment.baneOfArthropods, 4); + Bismuth.setEnchantmentForTools(Enchantment.baneOfArthropods, 4); + BismuthBronze.setEnchantmentForTools(Enchantment.baneOfArthropods, 5); + InfusedEarth.setEnchantmentForTools(Enchantment.baneOfArthropods, 5); + } + + private static void setEnchantmentSharpnessTools() { + Iron.setEnchantmentForTools(Enchantment.sharpness, 1); + Bronze.setEnchantmentForTools(Enchantment.sharpness, 1); + Brass.setEnchantmentForTools(Enchantment.sharpness, 2); + HSLA.setEnchantmentForTools(Enchantment.sharpness, 2); + Steel.setEnchantmentForTools(Enchantment.sharpness, 2); + WroughtIron.setEnchantmentForTools(Enchantment.sharpness, 2); + StainlessSteel.setEnchantmentForTools(Enchantment.sharpness, 3); + Knightmetal.setEnchantmentForTools(Enchantment.sharpness, 3); + ShadowIron.setEnchantmentForTools(Enchantment.sharpness, 3); + ShadowSteel.setEnchantmentForTools(Enchantment.sharpness, 4); + BlackSteel.setEnchantmentForTools(Enchantment.sharpness, 4); + RedSteel.setEnchantmentForTools(Enchantment.sharpness, 4); + BlueSteel.setEnchantmentForTools(Enchantment.sharpness, 5); + DamascusSteel.setEnchantmentForTools(Enchantment.sharpness, 5); + InfusedEntropy.setEnchantmentForTools(Enchantment.sharpness, 5); + TungstenCarbide.setEnchantmentForTools(Enchantment.sharpness, 5); + HSSE.setEnchantmentForTools(Enchantment.sharpness, 5); + HSSG.setEnchantmentForTools(Enchantment.sharpness, 4); + HSSS.setEnchantmentForTools(Enchantment.sharpness, 5); + } + + /** + * DO NOT ADD MORE THAN 1 TOOL AND ARMOR ENCHANTMENT PER MATERIAL! It will get overwritten! + */ + private static void setEnchantments() { + setToolEnchantments(); + setArmorEnchantments(); + } + + private static void setToolEnchantments() { + setEnchantmentKnockbackTools(); + setEnchantmentFortuneTools(); + setEnchantmentFireAspectTools(); + setEnchantmentSilkTouchTools(); + setEnchantmentSmiteTools(); + setEnchantmentBaneOfArthropodsTools(); + setEnchantmentSharpnessTools(); + } + + private static void setArmorEnchantments() { + InfusedAir.setEnchantmentForArmors(Enchantment.respiration, 3); + + InfusedFire.setEnchantmentForArmors(Enchantment.featherFalling, 4); + + Steeleaf.setEnchantmentForArmors(Enchantment.protection, 2); + Knightmetal.setEnchantmentForArmors(Enchantment.protection, 1); + InfusedEarth.setEnchantmentForArmors(Enchantment.protection, 4); + + InfusedEntropy.setEnchantmentForArmors(Enchantment.thorns, 3); + + InfusedWater.setEnchantmentForArmors(Enchantment.aquaAffinity, 1); + IronWood.setEnchantmentForArmors(Enchantment.aquaAffinity, 1); + + InfusedOrder.setEnchantmentForArmors(Enchantment.projectileProtection, 4); + + InfusedDull.setEnchantmentForArmors(Enchantment.blastProtection, 4); + + InfusedVis.setEnchantmentForArmors(Enchantment.protection, 4); + } + + private static void setMaceratingInto() { + Peanutwood.setMaceratingInto(Wood); + WoodSealed.setMaceratingInto(Wood); + NetherBrick.setMaceratingInto(Netherrack); + AnyRubber.setMaceratingInto(Rubber); + } + + private static void setReRegistration() { + Iron.mOreReRegistrations.add(AnyIron); + PigIron.mOreReRegistrations.add(AnyIron); + WroughtIron.mOreReRegistrations.add(AnyIron); + Copper.mOreReRegistrations.add(AnyCopper); + AnnealedCopper.mOreReRegistrations.add(AnyCopper); + Bronze.mOreReRegistrations.add(AnyBronze); + Rubber.mOreReRegistrations.add(AnyRubber); + StyreneButadieneRubber.mOreReRegistrations.add(AnyRubber); + Silicone.mOreReRegistrations.add(AnyRubber); + StyreneButadieneRubber.mOreReRegistrations.add(AnySyntheticRubber); + Silicone.mOreReRegistrations.add(AnySyntheticRubber); + } + + private static void setHeatDamage() { + FryingOilHot.setHeatDamage(1.0F); + Lava.setHeatDamage(3.0F); + Firestone.setHeatDamage(5.0F); + Pyrotheum.setHeatDamage(5.0F); + } + + private static void setByProducts() { + Mytryl.addOreByProducts(Samarium, Samarium, Zinc, Zinc); + Rubracium.addOreByProducts(Samarium, Samarium, Samarium, Samarium); + Chalcopyrite.addOreByProducts(Pyrite, Cobalt, Cadmium, Gold); + Sphalerite.addOreByProducts(GarnetYellow, Cadmium, Gallium, Zinc); + MeteoricIron.addOreByProducts(Iron, Nickel, Iridium, Platinum); + GlauconiteSand.addOreByProducts(Sodium, Aluminiumoxide, Iron); + Glauconite.addOreByProducts(Sodium, Aluminiumoxide, Iron); + Vermiculite.addOreByProducts(Iron, Aluminiumoxide, Magnesium); + FullersEarth.addOreByProducts(Aluminiumoxide, SiliconDioxide, Magnesium); + Bentonite.addOreByProducts(Aluminiumoxide, Calcium, Magnesium); + Uraninite.addOreByProducts(Uranium, Thorium, Uranium235); + Pitchblende.addOreByProducts(Thorium, Uranium, Lead); + Galena.addOreByProducts(Sulfur, Silver, Lead); + Lapis.addOreByProducts(Lazurite, Sodalite, Pyrite); + Pyrite.addOreByProducts(Sulfur, TricalciumPhosphate, Iron); + Copper.addOreByProducts(Cobalt, Gold, Nickel); + Nickel.addOreByProducts(Cobalt, Platinum, Iron); + GarnetRed.addOreByProducts(Spessartine, Pyrope, Almandine); + GarnetYellow.addOreByProducts(Andradite, Grossular, Uvarovite); + Cooperite.addOreByProducts(Palladium, Nickel, Iridium); + Cinnabar.addOreByProducts(Redstone, Sulfur, Glowstone); + Tantalite.addOreByProducts(Manganese, Niobium, Tantalum); + Pollucite.addOreByProducts(Caesium, Aluminiumoxide, Rubidium); + Chrysotile.addOreByProducts(Asbestos, SiliconDioxide, Magnesium); + Asbestos.addOreByProducts(Asbestos, SiliconDioxide, Magnesium); + Pentlandite.addOreByProducts(Iron, Sulfur, Cobalt); + Uranium.addOreByProducts(Lead, Uranium235, Thorium); + Scheelite.addOreByProducts(Manganese, Molybdenum, Calcium); + Tungstate.addOreByProducts(Manganese, Silver, Lithium); + Bauxite.addOreByProducts(Grossular, Rutile, Gallium); + QuartzSand.addOreByProducts(CertusQuartz, Quartzite, Barite); + Redstone.addOreByProducts(Cinnabar, RareEarth, Glowstone); + Monazite.addOreByProducts(Thorium, Neodymium, RareEarth); + Forcicium.addOreByProducts(Thorium, Neodymium, RareEarth); + Forcillium.addOreByProducts(Thorium, Neodymium, RareEarth); + Malachite.addOreByProducts(Copper, BrownLimonite, Calcite); + YellowLimonite.addOreByProducts(Nickel, BrownLimonite, Cobalt); + Lepidolite.addOreByProducts(Lithium, Caesium); + Andradite.addOreByProducts(GarnetYellow, Iron); + Pyrolusite.addOreByProducts(Manganese, Tantalite, Niobium) + .add(SubTag.DONT_ADD_DEFAULT_BBF_RECIPE); + TricalciumPhosphate.addOreByProducts(Apatite, Phosphate, Pyrochlore); + Apatite.addOreByProducts(TricalciumPhosphate, Phosphate, Pyrochlore); + Pyrochlore.addOreByProducts(Apatite, Calcite, Niobium); + Quartzite.addOreByProducts(CertusQuartz, Barite); + CertusQuartz.addOreByProducts(Quartzite, Barite); + CertusQuartzCharged.addOreByProducts(CertusQuartz, Quartzite, Barite); + BrownLimonite.addOreByProducts(Malachite, YellowLimonite); + Neodymium.addOreByProducts(Monazite, RareEarth); + Bastnasite.addOreByProducts(Neodymium, RareEarth); + Glowstone.addOreByProducts(Redstone, Gold); + Zinc.addOreByProducts(Tin, Gallium); + Tungsten.addOreByProducts(Manganese, Molybdenum); + Diatomite.addOreByProducts(BandedIron, Sapphire); + Iron.addOreByProducts(Nickel, Tin); + Gold.addOreByProducts(Copper, Nickel); + Tin.addOreByProducts(Iron, Zinc); + Antimony.addOreByProducts(Zinc, Iron); + Silver.addOreByProducts(Lead, Sulfur); + Lead.addOreByProducts(Silver, Sulfur); + Thorium.addOreByProducts(Uranium, Lead); + Plutonium.addOreByProducts(Uranium, Lead); + Electrum.addOreByProducts(Gold, Silver); + Electrotine.addOreByProducts(Redstone, Electrum); + Bronze.addOreByProducts(Copper, Tin); + Brass.addOreByProducts(Copper, Zinc); + Coal.addOreByProducts(Lignite, Thorium); + Ilmenite.addOreByProducts(Iron, Rutile); + Manganese.addOreByProducts(Chrome, Iron); + Sapphire.addOreByProducts(Aluminiumoxide, GreenSapphire); + GreenSapphire.addOreByProducts(Aluminiumoxide, Sapphire); + Platinum.addOreByProducts(Nickel, Iridium); + Emerald.addOreByProducts(Beryllium, Aluminiumoxide); + Olivine.addOreByProducts(Pyrope, Magnesium); + Chrome.addOreByProducts(Iron, Magnesium); + Chromite.addOreByProducts(Iron, Magnesium); + Tetrahedrite.addOreByProducts(Antimony, Zinc); + GarnetSand.addOreByProducts(GarnetRed, GarnetYellow); + Magnetite.addOreByProducts(Iron, Gold); + GraniticMineralSand.addOreByProducts(GraniteBlack, Magnetite); + BasalticMineralSand.addOreByProducts(Basalt, Magnetite); + Basalt.addOreByProducts(Olivine, DarkAsh); + VanadiumMagnetite.addOreByProducts(Magnetite, Vanadium); + Lazurite.addOreByProducts(Sodalite, Lapis); + Sodalite.addOreByProducts(Lazurite, Lapis); + Spodumene.addOreByProducts(Aluminiumoxide, Lithium); + Ruby.addOreByProducts(Chrome, GarnetRed); + Iridium.addOreByProducts(Platinum, Osmium); + Pyrope.addOreByProducts(GarnetRed, Magnesium); + Almandine.addOreByProducts(GarnetRed, Aluminiumoxide); + Spessartine.addOreByProducts(GarnetRed, Manganese); + Grossular.addOreByProducts(GarnetYellow, Calcium); + Uvarovite.addOreByProducts(GarnetYellow, Chrome); + Calcite.addOreByProducts(Andradite, Malachite); + NaquadahEnriched.addOreByProducts(Naquadah, Naquadria); + Salt.addOreByProducts(RockSalt, Borax); + RockSalt.addOreByProducts(Salt, Borax); + Naquadah.addOreByProducts(NaquadahEnriched); + Molybdenite.addOreByProducts(Molybdenum); + Stibnite.addOreByProducts(Antimony); + Garnierite.addOreByProducts(Nickel); + Lignite.addOreByProducts(Coal); + Diamond.addOreByProducts(Graphite); + Beryllium.addOreByProducts(Emerald); + Electrotine.addOreByProducts(Diamond); + Teslatite.addOreByProducts(Diamond); + Magnesite.addOreByProducts(Magnesium); + NetherQuartz.addOreByProducts(Netherrack); + PigIron.addOreByProducts(Iron); + DeepIron.addOreByProducts(Trinium, Iron, Trinium); + ShadowIron.addOreByProducts(Iron); + DarkIron.addOreByProducts(Iron); + MeteoricIron.addOreByProducts(Iron); + Steel.addOreByProducts(Iron); + HSLA.addOreByProducts(Iron); + Mithril.addOreByProducts(Platinum); + AstralSilver.addOreByProducts(Silver); + Graphite.addOreByProducts(Carbon); + Netherrack.addOreByProducts(Sulfur); + Flint.addOreByProducts(Obsidian); + Cobaltite.addOreByProducts(Cobalt); + Cobalt.addOreByProducts(Cobaltite); + Sulfur.addOreByProducts(Sulfur); + Saltpeter.addOreByProducts(Saltpeter); + Endstone.addOreByProducts(Helium_3); + Osmium.addOreByProducts(Iridium); + Magnesium.addOreByProducts(Olivine); + Aluminium.addOreByProducts(Bauxite); + Titanium.addOreByProducts(Almandine); + Obsidian.addOreByProducts(Olivine); + Ash.addOreByProducts(Carbon); + DarkAsh.addOreByProducts(Carbon); + Redrock.addOreByProducts(Clay); + Marble.addOreByProducts(Calcite); + Clay.addOreByProducts(Clay); + Cassiterite.addOreByProducts(Tin); + CassiteriteSand.addOreByProducts(Tin); + GraniteBlack.addOreByProducts(Biotite); + GraniteRed.addOreByProducts(PotassiumFeldspar); + Phosphate.addOreByProducts(Phosphorus); + Phosphorus.addOreByProducts(Phosphate); + Tanzanite.addOreByProducts(Opal); + Opal.addOreByProducts(Tanzanite); + Amethyst.addOreByProducts(Amethyst); + FoolsRuby.addOreByProducts(Jasper); + Amber.addOreByProducts(Amber); + Topaz.addOreByProducts(BlueTopaz); + BlueTopaz.addOreByProducts(Topaz); + Niter.addOreByProducts(Saltpeter); + Vinteum.addOreByProducts(Vinteum); + Force.addOreByProducts(Force); + Dilithium.addOreByProducts(Dilithium); + Neutronium.addOreByProducts(Neutronium); + Lithium.addOreByProducts(Lithium); + Silicon.addOreByProducts(SiliconDioxide); + InfusedGold.addOreByProduct(Gold); + Cryolite.addOreByProducts(Aluminiumoxide, Sodium); + Naquadria.addOreByProduct(Naquadria); + RoastedNickel.addOreByProduct(Nickel); + TengamRaw.addOreByProducts(NeodymiumMagnetic, SamariumMagnetic); + } + + private static void setColors() { + Naquadah.mMoltenRGBa[0] = 0; + Naquadah.mMoltenRGBa[1] = 255; + Naquadah.mMoltenRGBa[2] = 0; + Naquadah.mMoltenRGBa[3] = 0; + NaquadahEnriched.mMoltenRGBa[0] = 64; + NaquadahEnriched.mMoltenRGBa[1] = 255; + NaquadahEnriched.mMoltenRGBa[2] = 64; + NaquadahEnriched.mMoltenRGBa[3] = 0; + Naquadria.mMoltenRGBa[0] = 128; + Naquadria.mMoltenRGBa[1] = 255; + Naquadria.mMoltenRGBa[2] = 128; + Naquadria.mMoltenRGBa[3] = 0; + } + + private static void overrideChemicalFormulars() { + Glue.mChemicalFormula = "No Horses were harmed for the Production"; + AdvancedGlue.mChemicalFormula = "A chemically approved glue!"; + UUAmplifier.mChemicalFormula = "Accelerates the Mass Fabricator"; + LiveRoot.mChemicalFormula = ""; + WoodSealed.mChemicalFormula = ""; + Wood.mChemicalFormula = ""; + Electrotine.mChemicalFormula = "Rp"; + Trinium.mChemicalFormula = "Ke"; + Naquadah.mChemicalFormula = "Nq"; + NaquadahEnriched.mChemicalFormula = "Nq+"; + Naquadria.mChemicalFormula = "Nq*"; + NaquadahAlloy.mChemicalFormula = "Nq\u2082KeC"; + Sunnarium.mChemicalFormula = "Su"; + Adamantium.mChemicalFormula = "Ad"; + InfusedGold.mChemicalFormula = "AuMa*"; + MeteoricIron.mChemicalFormula = "SpFe"; + MeteoricSteel.mChemicalFormula = "SpFe\u2085\u2080C"; + Duranium.mChemicalFormula = "Du"; + Tritanium.mChemicalFormula = "Tn"; + Ardite.mChemicalFormula = "Ai"; + Manyullyn.mChemicalFormula = "AiCo"; + Mytryl.mChemicalFormula = "SpPt\u2082FeMa"; + BlackPlutonium.mChemicalFormula = "SpPu"; + Ledox.mChemicalFormula = "SpPb"; + CallistoIce.mChemicalFormula = "SpH\u2082O"; + Quantium.mChemicalFormula = "Qt"; + Desh.mChemicalFormula = "De"; + Oriharukon.mChemicalFormula = "Oh"; + Draconium.mChemicalFormula = "D"; + DraconiumAwakened.mChemicalFormula = "D*"; + BlueAlloy.mChemicalFormula = "AgRp\u2084"; + RedAlloy.mChemicalFormula = "Cu(" + Redstone.mChemicalFormula + ")\u2084"; + AnyIron.mChemicalFormula = "Fe"; + AnyCopper.mChemicalFormula = "Cu"; + ElectrumFlux.mChemicalFormula = "The formula is too long..."; + DeepIron.mChemicalFormula = "Sp\u2082Fe"; + Ichorium.mChemicalFormula = "IcMa"; + Infinity.mChemicalFormula = "If*"; + InfinityCatalyst.mChemicalFormula = "If"; + CosmicNeutronium.mChemicalFormula = "SpNt"; + Aluminiumhydroxide.mChemicalFormula = "Al\u0028OH\u0029\u2083"; + MaterialsKevlar.LiquidCrystalKevlar.mChemicalFormula = "[-CO-C\u2086H\u2084-CO-NH-C\u2086H\u2084-NH-]n"; + MaterialsKevlar.RhodiumChloride.mChemicalFormula = "RhCl\u2083"; + MaterialsKevlar.OrganorhodiumCatalyst.mChemicalFormula = "RhHCO(P(C\u2086H\u2085)\u2083)\u2083"; + MaterialsKevlar.CobaltIINitrate.mChemicalFormula = "Co(NO\u2083)\u2082"; + MaterialsKevlar.CobaltIIHydroxide.mChemicalFormula = "Co(OH)\u2082"; + SiliconSG.mChemicalFormula = "Si*"; + NetherQuartz.mChemicalFormula = "SiO\u2082"; + Quartzite.mChemicalFormula = "SiO\u2082"; + CertusQuartz.mChemicalFormula = "SiO\u2082"; + CertusQuartzCharged.mChemicalFormula = "SiO\u2082"; + MaterialsUEVplus.SpaceTime.mChemicalFormula = "Reality itself distilled into physical form"; + MaterialsUEVplus.Universium.mChemicalFormula = "A tear into the space beyond space"; + MaterialsUEVplus.Eternity.mChemicalFormula = "En\u29BC"; + MaterialsUEVplus.MagMatter.mChemicalFormula = "M\u238B"; + Longasssuperconductornameforuvwire.mChemicalFormula = "Nq*\u2084(Ir\u2083Os)\u2083EuSm"; + Longasssuperconductornameforuhvwire.mChemicalFormula = "D\u2086(SpNt)\u2087Tn\u2085Am\u2086"; + SuperconductorUEVBase.mChemicalFormula = "D*\u2085If*\u2085(\u2726\u25C6\u2726)(\u26B7\u2699\u26B7 Ni4Ti6)"; + SuperconductorUIVBase.mChemicalFormula = "(C\u2081\u2084Os\u2081\u2081O\u2087Ag\u2083SpH\u2082O)\u2084?\u2081\u2080(Fs\u26B6)\u2086(\u2318\u262F\u262F\u2318)\u2085"; + SuperconductorUMVBase.mChemicalFormula = "?\u2086Or\u2083(Hy\u26B6)\u2081\u2081(((CW)\u2087Ti\u2083)\u2083???)\u2085\u06DE\u2082"; + Diatomite.mChemicalFormula = "(SiO\u2082)\u2088Fe\u2082O\u2083(Al\u2082O\u2083)"; + EnrichedHolmium.mChemicalFormula = "Nq+\u2088Ho\u2082"; + Grade1PurifiedWater.mChemicalFormula = "H\u2082O"; + Grade2PurifiedWater.mChemicalFormula = "H\u2082O"; + Grade3PurifiedWater.mChemicalFormula = "H\u2082O"; + Grade4PurifiedWater.mChemicalFormula = "H\u2082O"; + Grade5PurifiedWater.mChemicalFormula = "H\u2082O"; + Grade6PurifiedWater.mChemicalFormula = "H\u2082O"; + Grade7PurifiedWater.mChemicalFormula = "H\u2082O"; + Grade8PurifiedWater.mChemicalFormula = "H\u2082O"; + TengamRaw.mChemicalFormula = ""; + TengamPurified.mChemicalFormula = "M"; + TengamAttuned.mChemicalFormula = "M"; + MaterialsUEVplus.ExcitedDTSC.mChemicalFormula = "[-Stellar-Stellar-]"; + MaterialsUEVplus.DimensionallyTranscendentStellarCatalyst.mChemicalFormula = "Stellar"; + } + + private static void initSubTags() { + SubTag.ELECTROMAGNETIC_SEPERATION_NEODYMIUM.addTo(Bastnasite, Monazite, Forcicium, Forcillium); + + SubTag.ELECTROMAGNETIC_SEPERATION_GOLD + .addTo(Magnetite, VanadiumMagnetite, BasalticMineralSand, GraniticMineralSand); + + SubTag.NO_RECIPES.addTo(MaterialsUEVplus.MagnetohydrodynamicallyConstrainedStarMatter); + + SubTag.ELECTROMAGNETIC_SEPERATION_IRON.addTo( + YellowLimonite, + BrownLimonite, + Pyrite, + BandedIron, + Nickel, + Vermiculite, + Glauconite, + GlauconiteSand, + Pentlandite, + Tin, + Antimony, + Ilmenite, + Manganese, + Chrome, + Chromite, + Andradite); + + SubTag.BLASTFURNACE_CALCITE_DOUBLE + .addTo(Pyrite, BrownLimonite, YellowLimonite, BasalticMineralSand, GraniticMineralSand, Magnetite); + + SubTag.BLASTFURNACE_CALCITE_TRIPLE.addTo(Iron, PigIron, DeepIron, ShadowIron, WroughtIron, MeteoricIron); + + SubTag.WASHING_MERCURY.addTo(Gold, Osmium, Mithril, Platinum, Cooperite, AstralSilver); + + SubTag.WASHING_MERCURY_99_PERCENT.addTo(Silver); + + SubTag.WASHING_SODIUMPERSULFATE.addTo(Zinc, Nickel, Copper, Cobalt, Cobaltite, Tetrahedrite); + SubTag.METAL.addTo( + AnyIron, + AnyCopper, + AnyBronze, + Metal, + Aluminium, + Americium, + Antimony, + Beryllium, + Bismuth, + Caesium, + Cerium, + Chrome, + Cobalt, + Copper, + Dysprosium, + Erbium, + Europium, + Gadolinium, + Gallium, + Gold, + Holmium, + Indium, + Iridium, + Iron, + Lanthanum, + Lead, + Lutetium, + Magnesium, + Manganese, + Mercury, + Niobium, + Molybdenum, + Neodymium, + Neutronium, + Nickel, + Osmium, + Palladium, + Platinum, + Plutonium, + Plutonium241, + Praseodymium, + Promethium, + Rubidium, + Samarium, + Scandium, + Silicon, + Silver, + Tantalum, + Tellurium, + Terbium, + Thorium, + Thulium, + Tin, + Titanium, + Tungsten, + Uranium, + Uranium235, + Vanadium, + Ytterbium, + Yttrium, + Zinc, + Flerovium, + PhasedIron, + PhasedGold, + DarkSteel, + TinAlloy, + ConductiveIron, + ElectricalSteel, + EnergeticAlloy, + VibrantAlloy, + MelodicAlloy, + StellarAlloy, + VividAlloy, + EnergeticSilver, + CrystallinePinkSlime, + CrystallineAlloy, + CrudeSteel, + EndSteel, + PulsatingIron, + DarkThaumium, + Adamantium, + Amordrine, + Angmallen, + Ardite, + Aredrite, + Atlarus, + Carmot, + Celenegil, + Ceruclase, + DarkIron, + Desh, + Desichalkos, + Duranium, + ElectrumFlux, + Enderium, + EnderiumBase, + Eximite, + FierySteel, + Force, + Haderoth, + Hematite, + Hepatizon, + HSLA, + Infuscolium, + InfusedGold, + Inolashite, + Mercassium, + MeteoricIron, + BloodInfusedIron, + MaterialsUEVplus.Universium, + MaterialsUEVplus.MagnetohydrodynamicallyConstrainedStarMatter, + MeteoricSteel, + Naquadah, + NaquadahAlloy, + NaquadahEnriched, + Naquadria, + ObsidianFlux, + Orichalcum, + Osmonium, + Oureclase, + Phoenixite, + Prometheum, + Sanguinite, + CosmicNeutronium, + Tartarite, + Ichorium, + Tritanium, + Vulcanite, + Vyroxeres, + Yellorium, + Zectium, + AluminiumBrass, + Osmiridium, + Sunnarium, + AnnealedCopper, + BatteryAlloy, + Brass, + Bronze, + ChromiumDioxide, + Cupronickel, + DeepIron, + Electrum, + Invar, + Kanthal, + Magnalium, + Nichrome, + NiobiumNitride, + NiobiumTitanium, + PigIron, + SolderingAlloy, + StainlessSteel, + Steel, + Ultimet, + VanadiumGallium, + WroughtIron, + YttriumBariumCuprate, + IronWood, + Alumite, + Manyullyn, + ShadowIron, + Shadow, + ShadowSteel, + Steeleaf, + SterlingSilver, + RoseGold, + BlackBronze, + BismuthBronze, + BlackSteel, + RedSteel, + BlueSteel, + DamascusSteel, + TungstenSteel, + TPV, + AstralSilver, + Mithril, + BlueAlloy, + RedAlloy, + CobaltBrass, + Thaumium, + Void, + IronMagnetic, + SteelMagnetic, + NeodymiumMagnetic, + SamariumMagnetic, + Knightmetal, + HSSG, + HSSE, + HSSS, + TungstenCarbide, + HeeEndium, + VanadiumSteel, + Kalendrite, + Ignatius, + Trinium, + Infinity, + InfinityCatalyst, + Realgar, + Chrysotile, + BlackPlutonium, + Alduorite, + Adluorite, + Vinteum, + Rubracium, + Draconium, + DraconiumAwakened, + Pentacadmiummagnesiumhexaoxid, + Titaniumonabariumdecacoppereikosaoxid, + Uraniumtriplatinid, + Vanadiumtriindinid, + Tetraindiumditindibariumtitaniumheptacoppertetrakaidekaoxid, + Tetranaquadahdiindiumhexaplatiumosminid, + Longasssuperconductornameforuvwire, + Longasssuperconductornameforuhvwire, + SuperconductorUEVBase, + SuperconductorUIVBase, + SuperconductorUMVBase, + Quantium, + RedstoneAlloy, + Bedrockium, + EnrichedHolmium, + TengamPurified, + TengamAttuned, + MaterialsUEVplus.Eternity, + MaterialsUEVplus.MagMatter); + + SubTag.FOOD.addTo( + MeatRaw, + MeatCooked, + Ice, + Water, + Salt, + Chili, + Cocoa, + Cheese, + Coffee, + Chocolate, + Milk, + Honey, + FryingOilHot, + FishOil, + SeedOil, + SeedOilLin, + SeedOilHemp, + Wheat, + Sugar, + FreshWater); + + Wood.add(SubTag.WOOD, SubTag.FLAMMABLE, SubTag.NO_SMELTING, SubTag.NO_SMASHING); + WoodSealed.add(SubTag.WOOD, SubTag.FLAMMABLE, SubTag.NO_SMELTING, SubTag.NO_SMASHING, SubTag.NO_WORKING); + Peanutwood.add(SubTag.WOOD, SubTag.FLAMMABLE, SubTag.NO_SMELTING, SubTag.NO_SMASHING); + LiveRoot.add( + SubTag.WOOD, + SubTag.FLAMMABLE, + SubTag.NO_SMELTING, + SubTag.NO_SMASHING, + SubTag.MAGICAL, + SubTag.MORTAR_GRINDABLE); + IronWood.add(SubTag.WOOD, SubTag.FLAMMABLE, SubTag.MAGICAL, SubTag.MORTAR_GRINDABLE); + Steeleaf.add(SubTag.WOOD, SubTag.FLAMMABLE, SubTag.MAGICAL, SubTag.MORTAR_GRINDABLE, SubTag.NO_SMELTING); + + MeatRaw.add(SubTag.NO_SMASHING); + MeatCooked.add(SubTag.NO_SMASHING, SubTag.NO_SMELTING); + Snow.add(SubTag.NO_SMASHING, SubTag.NO_SMELTING, SubTag.NO_RECYCLING); + Ice.add(SubTag.NO_SMASHING, SubTag.NO_SMELTING, SubTag.NO_RECYCLING); + Water.add(SubTag.NO_SMASHING, SubTag.NO_SMELTING, SubTag.NO_RECYCLING); + Sulfur.add(SubTag.NO_SMASHING, SubTag.NO_SMELTING, SubTag.FLAMMABLE); + Saltpeter.add(SubTag.NO_SMASHING, SubTag.NO_SMELTING, SubTag.FLAMMABLE); + Graphite.add(SubTag.NO_SMASHING, SubTag.NO_SMELTING, SubTag.FLAMMABLE, SubTag.NO_SMELTING); + + Wheat.add(SubTag.FLAMMABLE, SubTag.MORTAR_GRINDABLE); + Paper.add(SubTag.FLAMMABLE, SubTag.NO_SMELTING, SubTag.NO_SMASHING, SubTag.MORTAR_GRINDABLE, SubTag.PAPER); + Coal.add(SubTag.FLAMMABLE, SubTag.NO_SMELTING, SubTag.NO_SMASHING, SubTag.MORTAR_GRINDABLE); + Charcoal.add(SubTag.FLAMMABLE, SubTag.NO_SMELTING, SubTag.NO_SMASHING, SubTag.MORTAR_GRINDABLE); + Lignite.add(SubTag.FLAMMABLE, SubTag.NO_SMELTING, SubTag.NO_SMASHING, SubTag.MORTAR_GRINDABLE); + + Rubber.add(SubTag.FLAMMABLE, SubTag.NO_SMASHING, SubTag.BOUNCY, SubTag.STRETCHY); + StyreneButadieneRubber.add(SubTag.FLAMMABLE, SubTag.NO_SMASHING, SubTag.BOUNCY, SubTag.STRETCHY); + Plastic.add(SubTag.FLAMMABLE, SubTag.NO_SMASHING, SubTag.BOUNCY, SubTag.STRETCHY); + PolyvinylChloride.add(SubTag.FLAMMABLE, SubTag.NO_SMASHING, SubTag.BOUNCY, SubTag.STRETCHY); + Polystyrene.add(SubTag.FLAMMABLE, SubTag.NO_SMASHING, SubTag.BOUNCY, SubTag.STRETCHY); + Silicone.add(SubTag.FLAMMABLE, SubTag.NO_SMASHING, SubTag.BOUNCY, SubTag.STRETCHY); + Polytetrafluoroethylene.add(SubTag.FLAMMABLE, SubTag.NO_SMASHING, SubTag.STRETCHY); + Polybenzimidazole.add(SubTag.FLAMMABLE, SubTag.NO_SMASHING, SubTag.STRETCHY); + PolyphenyleneSulfide.add(SubTag.FLAMMABLE, SubTag.NO_SMASHING, SubTag.STRETCHY); + MaterialsKevlar.Kevlar.add(SubTag.FLAMMABLE, SubTag.NO_SMASHING, SubTag.STRETCHY); + + TNT.add(SubTag.FLAMMABLE, SubTag.EXPLOSIVE, SubTag.NO_SMELTING, SubTag.NO_SMASHING); + Gunpowder.add(SubTag.FLAMMABLE, SubTag.EXPLOSIVE, SubTag.NO_SMELTING, SubTag.NO_SMASHING); + Glyceryl.add(SubTag.FLAMMABLE, SubTag.EXPLOSIVE, SubTag.NO_SMELTING, SubTag.NO_SMASHING); + NitroCoalFuel.add(SubTag.FLAMMABLE, SubTag.EXPLOSIVE, SubTag.NO_SMELTING, SubTag.NO_SMASHING); + NitroFuel.add(SubTag.FLAMMABLE, SubTag.EXPLOSIVE, SubTag.NO_SMELTING, SubTag.NO_SMASHING); + NitroCarbon.add(SubTag.FLAMMABLE, SubTag.EXPLOSIVE, SubTag.NO_SMELTING, SubTag.NO_SMASHING); + + Lead.add(SubTag.MORTAR_GRINDABLE, SubTag.SOLDERING_MATERIAL, SubTag.SOLDERING_MATERIAL_BAD); + Tin.add(SubTag.MORTAR_GRINDABLE, SubTag.SOLDERING_MATERIAL); + SolderingAlloy.add(SubTag.MORTAR_GRINDABLE, SubTag.SOLDERING_MATERIAL, SubTag.SOLDERING_MATERIAL_GOOD); + + Cheese.add(SubTag.SMELTING_TO_FLUID); + Sugar.add(SubTag.SMELTING_TO_FLUID); + + Concrete.add(SubTag.STONE, SubTag.NO_SMASHING, SubTag.SMELTING_TO_FLUID); + ConstructionFoam.add(SubTag.STONE, SubTag.NO_SMASHING, SubTag.EXPLOSIVE, SubTag.NO_SMELTING); + ReinforceGlass.add(SubTag.CRYSTAL, SubTag.NO_SMASHING, SubTag.SMELTING_TO_FLUID); + Redstone.add( + SubTag.STONE, + SubTag.NO_SMASHING, + SubTag.UNBURNABLE, + SubTag.SMELTING_TO_FLUID, + SubTag.PULVERIZING_CINNABAR); + Glowstone.add(SubTag.STONE, SubTag.NO_SMASHING, SubTag.UNBURNABLE, SubTag.SMELTING_TO_FLUID); + Electrotine.add(SubTag.STONE, SubTag.NO_SMASHING, SubTag.UNBURNABLE, SubTag.SMELTING_TO_FLUID); + Teslatite.add(SubTag.STONE, SubTag.NO_SMASHING, SubTag.UNBURNABLE, SubTag.SMELTING_TO_FLUID); + Netherrack.add(SubTag.STONE, SubTag.NO_SMASHING, SubTag.UNBURNABLE, SubTag.FLAMMABLE); + Stone.add(SubTag.STONE, SubTag.NO_SMASHING, SubTag.NO_RECYCLING); + Brick.add(SubTag.STONE, SubTag.NO_SMASHING, SubTag.NO_SMELTING); + NetherBrick.add(SubTag.STONE, SubTag.NO_SMASHING); + Endstone.add(SubTag.STONE, SubTag.NO_SMASHING); + Marble.add(SubTag.STONE, SubTag.NO_SMASHING); + Basalt.add(SubTag.STONE, SubTag.NO_SMASHING); + Redrock.add(SubTag.STONE, SubTag.NO_SMASHING); + Obsidian.add(SubTag.STONE, SubTag.NO_SMASHING); + Flint.add(SubTag.STONE, SubTag.NO_SMASHING, SubTag.MORTAR_GRINDABLE); + GraniteRed.add(SubTag.STONE, SubTag.NO_SMASHING); + GraniteBlack.add(SubTag.STONE, SubTag.NO_SMASHING); + Salt.add(SubTag.STONE, SubTag.NO_SMASHING); + RockSalt.add(SubTag.STONE, SubTag.NO_SMASHING); + + Sand.add(SubTag.NO_RECYCLING); + + Gold.add(SubTag.MORTAR_GRINDABLE); + Silver.add(SubTag.MORTAR_GRINDABLE); + Iron.add(SubTag.MORTAR_GRINDABLE); + IronMagnetic.add(SubTag.MORTAR_GRINDABLE); + HSLA.add(SubTag.MORTAR_GRINDABLE); + Steel.add(SubTag.MORTAR_GRINDABLE); + SteelMagnetic.add(SubTag.MORTAR_GRINDABLE); + Zinc.add(SubTag.MORTAR_GRINDABLE); + Antimony.add(SubTag.MORTAR_GRINDABLE); + Copper.add(SubTag.MORTAR_GRINDABLE); + AnnealedCopper.add(SubTag.MORTAR_GRINDABLE); + Bronze.add(SubTag.MORTAR_GRINDABLE); + Nickel.add(SubTag.MORTAR_GRINDABLE); + Invar.add(SubTag.MORTAR_GRINDABLE); + Brass.add(SubTag.MORTAR_GRINDABLE); + WroughtIron.add(SubTag.MORTAR_GRINDABLE); + Electrum.add(SubTag.MORTAR_GRINDABLE); + Clay.add(SubTag.MORTAR_GRINDABLE); + + Glass.add(SubTag.CRYSTAL, SubTag.NO_SMASHING, SubTag.NO_RECYCLING, SubTag.SMELTING_TO_FLUID); + Diamond.add(SubTag.CRYSTAL, SubTag.NO_SMASHING, SubTag.NO_SMELTING, SubTag.FLAMMABLE); + Emerald.add(SubTag.CRYSTAL, SubTag.NO_SMASHING, SubTag.NO_SMELTING); + Amethyst.add(SubTag.CRYSTAL, SubTag.NO_SMASHING, SubTag.NO_SMELTING); + Tanzanite.add(SubTag.CRYSTAL, SubTag.NO_SMASHING, SubTag.NO_SMELTING); + Topaz.add(SubTag.CRYSTAL, SubTag.NO_SMASHING, SubTag.NO_SMELTING); + BlueTopaz.add(SubTag.CRYSTAL, SubTag.NO_SMASHING, SubTag.NO_SMELTING); + Amber.add(SubTag.CRYSTAL, SubTag.NO_SMASHING, SubTag.NO_SMELTING); + GreenSapphire.add(SubTag.CRYSTAL, SubTag.NO_SMASHING, SubTag.NO_SMELTING); + Sapphire.add(SubTag.CRYSTAL, SubTag.NO_SMASHING, SubTag.NO_SMELTING); + Ruby.add(SubTag.CRYSTAL, SubTag.NO_SMASHING, SubTag.NO_SMELTING); + FoolsRuby.add(SubTag.CRYSTAL, SubTag.NO_SMASHING, SubTag.NO_SMELTING); + Opal.add(SubTag.CRYSTAL, SubTag.NO_SMASHING, SubTag.NO_SMELTING); + Olivine.add(SubTag.CRYSTAL, SubTag.NO_SMASHING, SubTag.NO_SMELTING); + Jasper.add(SubTag.CRYSTAL, SubTag.NO_SMASHING, SubTag.NO_SMELTING); + GarnetRed.add(SubTag.CRYSTAL, SubTag.NO_SMASHING, SubTag.NO_SMELTING); + GarnetYellow.add(SubTag.CRYSTAL, SubTag.NO_SMASHING, SubTag.NO_SMELTING); + Mimichite.add(SubTag.CRYSTAL, SubTag.NO_SMASHING, SubTag.NO_SMELTING); + CrystalFlux.add(SubTag.CRYSTAL, SubTag.NO_SMASHING, SubTag.NO_SMELTING); + Crystal.add(SubTag.CRYSTAL, SubTag.NO_SMASHING, SubTag.NO_SMELTING); + Niter.add(SubTag.CRYSTAL, SubTag.NO_SMASHING, SubTag.NO_SMELTING); + Apatite.add(SubTag.CRYSTAL, SubTag.NO_SMASHING, SubTag.NO_SMELTING, SubTag.CRYSTALLISABLE); + Lapis.add(SubTag.CRYSTAL, SubTag.NO_SMASHING, SubTag.NO_SMELTING, SubTag.CRYSTALLISABLE); + Sodalite.add(SubTag.CRYSTAL, SubTag.NO_SMASHING, SubTag.NO_SMELTING, SubTag.CRYSTALLISABLE); + Lazurite.add(SubTag.CRYSTAL, SubTag.NO_SMASHING, SubTag.NO_SMELTING, SubTag.CRYSTALLISABLE); + Monazite.add(SubTag.CRYSTAL, SubTag.NO_SMASHING, SubTag.NO_SMELTING, SubTag.CRYSTALLISABLE); + Quartzite.add(SubTag.CRYSTAL, SubTag.NO_SMASHING, SubTag.NO_SMELTING, SubTag.CRYSTALLISABLE, SubTag.QUARTZ); + Quartz.add(SubTag.CRYSTAL, SubTag.NO_SMASHING, SubTag.NO_SMELTING, SubTag.CRYSTALLISABLE, SubTag.QUARTZ); + SiliconDioxide + .add(SubTag.CRYSTAL, SubTag.NO_SMASHING, SubTag.NO_SMELTING, SubTag.CRYSTALLISABLE, SubTag.QUARTZ); + Dilithium.add(SubTag.CRYSTAL, SubTag.NO_SMASHING, SubTag.NO_SMELTING, SubTag.CRYSTALLISABLE, SubTag.QUARTZ); + NetherQuartz.add(SubTag.CRYSTAL, SubTag.NO_SMASHING, SubTag.NO_SMELTING, SubTag.CRYSTALLISABLE, SubTag.QUARTZ); + CertusQuartz.add(SubTag.CRYSTAL, SubTag.NO_SMASHING, SubTag.NO_SMELTING, SubTag.CRYSTALLISABLE, SubTag.QUARTZ); + CertusQuartzCharged + .add(SubTag.CRYSTAL, SubTag.NO_SMASHING, SubTag.NO_SMELTING, SubTag.CRYSTALLISABLE, SubTag.QUARTZ); + Fluix.add(SubTag.CRYSTAL, SubTag.NO_SMASHING, SubTag.NO_SMELTING, SubTag.CRYSTALLISABLE, SubTag.QUARTZ); + TricalciumPhosphate + .add(SubTag.CRYSTAL, SubTag.NO_SMASHING, SubTag.NO_SMELTING, SubTag.FLAMMABLE, SubTag.EXPLOSIVE); + Phosphate.add(SubTag.CRYSTAL, SubTag.NO_SMASHING, SubTag.NO_SMELTING, SubTag.FLAMMABLE, SubTag.EXPLOSIVE); + InfusedAir.add(SubTag.CRYSTAL, SubTag.NO_SMASHING, SubTag.NO_SMELTING, SubTag.MAGICAL, SubTag.UNBURNABLE); + InfusedFire.add(SubTag.CRYSTAL, SubTag.NO_SMASHING, SubTag.NO_SMELTING, SubTag.MAGICAL, SubTag.UNBURNABLE); + InfusedEarth.add(SubTag.CRYSTAL, SubTag.NO_SMASHING, SubTag.NO_SMELTING, SubTag.MAGICAL, SubTag.UNBURNABLE); + InfusedWater.add(SubTag.CRYSTAL, SubTag.NO_SMASHING, SubTag.NO_SMELTING, SubTag.MAGICAL, SubTag.UNBURNABLE); + InfusedEntropy.add(SubTag.CRYSTAL, SubTag.NO_SMASHING, SubTag.NO_SMELTING, SubTag.MAGICAL, SubTag.UNBURNABLE); + InfusedOrder.add(SubTag.CRYSTAL, SubTag.NO_SMASHING, SubTag.NO_SMELTING, SubTag.MAGICAL, SubTag.UNBURNABLE); + InfusedVis.add(SubTag.CRYSTAL, SubTag.NO_SMASHING, SubTag.NO_SMELTING, SubTag.MAGICAL, SubTag.UNBURNABLE); + InfusedDull.add(SubTag.CRYSTAL, SubTag.NO_SMASHING, SubTag.NO_SMELTING, SubTag.MAGICAL, SubTag.UNBURNABLE); + NetherStar.add(SubTag.CRYSTAL, SubTag.NO_SMASHING, SubTag.NO_SMELTING, SubTag.MAGICAL, SubTag.UNBURNABLE); + EnderPearl.add(SubTag.CRYSTAL, SubTag.NO_SMASHING, SubTag.NO_SMELTING, SubTag.MAGICAL, SubTag.PEARL); + EnderEye.add(SubTag.CRYSTAL, SubTag.NO_SMASHING, SubTag.NO_SMELTING, SubTag.MAGICAL, SubTag.PEARL); + Firestone.add( + SubTag.CRYSTAL, + SubTag.NO_SMASHING, + SubTag.NO_SMELTING, + SubTag.CRYSTALLISABLE, + SubTag.MAGICAL, + SubTag.QUARTZ, + SubTag.UNBURNABLE, + SubTag.BURNING); + Forcicium.add(SubTag.CRYSTAL, SubTag.NO_SMASHING, SubTag.NO_SMELTING, SubTag.CRYSTALLISABLE, SubTag.MAGICAL); + Forcillium.add(SubTag.CRYSTAL, SubTag.NO_SMASHING, SubTag.NO_SMELTING, SubTag.CRYSTALLISABLE, SubTag.MAGICAL); + Force.add(SubTag.CRYSTAL, SubTag.MAGICAL, SubTag.UNBURNABLE); + Magic.add(SubTag.CRYSTAL, SubTag.MAGICAL, SubTag.UNBURNABLE); + + Primitive.add(SubTag.NO_SMASHING, SubTag.NO_SMELTING); + Basic.add(SubTag.NO_SMASHING, SubTag.NO_SMELTING); + Good.add(SubTag.NO_SMASHING, SubTag.NO_SMELTING); + Advanced.add(SubTag.NO_SMASHING, SubTag.NO_SMELTING); + Data.add(SubTag.NO_SMASHING, SubTag.NO_SMELTING); + Elite.add(SubTag.NO_SMASHING, SubTag.NO_SMELTING); + Master.add(SubTag.NO_SMASHING, SubTag.NO_SMELTING); + Ultimate.add(SubTag.NO_SMASHING, SubTag.NO_SMELTING); + Superconductor.add(SubTag.NO_SMASHING, SubTag.NO_SMELTING); // Todo: remove this once it will be fully + // deprecated + Infinite.add(SubTag.NO_SMASHING, SubTag.NO_SMELTING); + Bio.add(SubTag.NO_SMASHING, SubTag.NO_SMELTING); + SuperconductorMV.add(SubTag.NO_SMASHING, SubTag.NO_SMELTING); + SuperconductorHV.add(SubTag.NO_SMASHING, SubTag.NO_SMELTING); + SuperconductorEV.add(SubTag.NO_SMASHING, SubTag.NO_SMELTING); + SuperconductorIV.add(SubTag.NO_SMASHING, SubTag.NO_SMELTING); + SuperconductorLuV.add(SubTag.NO_SMASHING, SubTag.NO_SMELTING); + SuperconductorZPM.add(SubTag.NO_SMASHING, SubTag.NO_SMELTING); + // SuperconductorUV .add(SubTag.NO_SMASHING, SubTag.NO_SMELTING); + SuperconductorUHV.add(SubTag.NO_SMASHING, SubTag.NO_SMELTING); + + Blaze.add(SubTag.MAGICAL, SubTag.SMELTING_TO_FLUID, SubTag.MORTAR_GRINDABLE, SubTag.UNBURNABLE, SubTag.BURNING); + FierySteel.add(SubTag.MAGICAL, SubTag.UNBURNABLE, SubTag.BURNING); + DarkThaumium.add(SubTag.MAGICAL); + Thaumium.add(SubTag.MAGICAL); + Void.add(SubTag.MAGICAL); + Enderium.add(SubTag.MAGICAL); + AstralSilver.add(SubTag.MAGICAL); + Mithril.add(SubTag.MAGICAL); + + Carbon.add(SubTag.NO_SMELTING); + Boron.add(SubTag.SMELTING_TO_FLUID); + } + + public static void init() { + new ProcessingConfig(); + if (!GT_Mod.gregtechproxy.mEnableAllMaterials) new ProcessingModSupport(); + mMaterialHandlers.forEach(IMaterialHandler::onMaterialsInit); // This is where addon mods can add/manipulate + // materials + initMaterialProperties(); // No more material addition or manipulation should be done past this point! + MATERIALS_ARRAY = MATERIALS_MAP.values() + .toArray(new Materials[0]); // Generate standard object array. This is a + // lot faster to loop over. + VALUES = Arrays.asList(MATERIALS_ARRAY); + + disableUnusedHotIngots(); + fillGeneratedMaterialsMap(); + } + + private static void disableUnusedHotIngots() { + OrePrefixes.ingotHot.mDisabledItems.addAll( + Arrays.stream(Materials.values()) + .parallel() + .filter(OrePrefixes.ingotHot::doGenerateItem) + .filter(m -> m.mBlastFurnaceTemp < 1750 && m.mAutoGenerateBlastFurnaceRecipes) + .collect(Collectors.toSet())); + OrePrefixes.ingotHot.disableComponent(Materials.Reinforced); + OrePrefixes.ingotHot.disableComponent(Materials.ConductiveIron); + OrePrefixes.ingotHot.disableComponent(Materials.FierySteel); + OrePrefixes.ingotHot.disableComponent(Materials.ElectricalSteel); + OrePrefixes.ingotHot.disableComponent(Materials.EndSteel); + OrePrefixes.ingotHot.disableComponent(Materials.Soularium); + OrePrefixes.ingotHot.disableComponent(Materials.EnergeticSilver); + OrePrefixes.ingotHot.disableComponent(Materials.Cheese); + OrePrefixes.ingotHot.disableComponent(Materials.Calcium); + OrePrefixes.ingotHot.disableComponent(Materials.Flerovium); + OrePrefixes.ingotHot.disableComponent(Materials.Cobalt); + OrePrefixes.ingotHot.disableComponent(Materials.RedstoneAlloy); + OrePrefixes.ingotHot.disableComponent(Materials.Ardite); + OrePrefixes.ingotHot.disableComponent(Materials.DarkSteel); + OrePrefixes.ingotHot.disableComponent(Materials.BlackSteel); + OrePrefixes.ingotHot.disableComponent(Materials.EnergeticAlloy); + OrePrefixes.ingotHot.disableComponent(Materials.PulsatingIron); + OrePrefixes.ingotHot.disableComponent(Materials.CrudeSteel); + } + + /** + * Init rendering properties. Will be called at pre init by GT client proxy. + */ + public static void initClient() { + MaterialsUEVplus.TranscendentMetal.renderer = new TranscendentMetalRenderer(); + MaterialsBotania.GaiaSpirit.renderer = new GaiaSpiritRenderer(); + Infinity.renderer = new InfinityRenderer(); + CosmicNeutronium.renderer = new CosmicNeutroniumRenderer(); + MaterialsUEVplus.Universium.renderer = new UniversiumRenderer(); + MaterialsUEVplus.Eternity.renderer = new InfinityRenderer(); + MaterialsUEVplus.MagMatter.renderer = new InfinityRenderer(); + } + + private static void fillGeneratedMaterialsMap() { + for (Materials aMaterial : MATERIALS_ARRAY) { + if (aMaterial.mMetaItemSubID >= 0) { + if (aMaterial.mMetaItemSubID < 1000) { + if (aMaterial.mHasParentMod) { + if (GregTech_API.sGeneratedMaterials[aMaterial.mMetaItemSubID] == null) { + GregTech_API.sGeneratedMaterials[aMaterial.mMetaItemSubID] = aMaterial; + } else throw new IllegalArgumentException( + "The Material Index " + aMaterial.mMetaItemSubID + + " for " + + aMaterial.mName + + " is already used!"); + } + } else throw new IllegalArgumentException( + "The Material Index " + aMaterial.mMetaItemSubID + + " for " + + aMaterial.mName + + " is/over the maximum of 1000"); + } + } + } + + private static void addFuelValues(Materials aMaterial, String aConfigPath) { + aMaterial.mFuelPower = GregTech_API.sMaterialProperties.get(aConfigPath, "FuelPower", aMaterial.mFuelPower); + aMaterial.mFuelType = GregTech_API.sMaterialProperties.get(aConfigPath, "FuelType", aMaterial.mFuelType); + } + + private static void addTemperatureValues(Materials aMaterial, String aConfigPath) { + aMaterial.mMeltingPoint = GregTech_API.sMaterialProperties + .get(aConfigPath, "MeltingPoint", aMaterial.mMeltingPoint); + aMaterial.mBlastFurnaceRequired = GregTech_API.sMaterialProperties + .get(aConfigPath, "BlastFurnaceRequired", aMaterial.mBlastFurnaceRequired); + aMaterial.mBlastFurnaceTemp = (short) GregTech_API.sMaterialProperties + .get(aConfigPath, "BlastFurnaceTemp", aMaterial.mBlastFurnaceTemp); + aMaterial.mGasTemp = GregTech_API.sMaterialProperties.get(aConfigPath, "GasTemp", aMaterial.mGasTemp); + aMaterial.setHeatDamage( + (float) GregTech_API.sMaterialProperties.get(aConfigPath, "HeatDamage", aMaterial.mHeatDamage)); + } + + private static void addDensityValues(Materials aMaterial, String aConfigPath) { + aMaterial.mDensityMultiplier = GregTech_API.sMaterialProperties + .get(aConfigPath, "DensityMultiplier", aMaterial.mDensityMultiplier); + aMaterial.mDensityDivider = GregTech_API.sMaterialProperties + .get(aConfigPath, "DensityDivider", aMaterial.mDensityDivider); + aMaterial.mDensity = (long) GregTech_API.sMaterialProperties.get( + aConfigPath, + "Density", + ((double) M * aMaterial.mDensityMultiplier) + / (aMaterial.mDensityDivider != 0 ? aMaterial.mDensityDivider : 1)); + } + + private static void addColorValues(Materials aMaterial, String aConfigPath) { + aMaterial.mTransparent = GregTech_API.sMaterialProperties + .get(aConfigPath, "Transparent", aMaterial.mTransparent); + String aColor = GregTech_API.sMaterialProperties + .get(aConfigPath, "DyeColor", aMaterial.mColor == Dyes._NULL ? "None" : aMaterial.mColor.toString()); + aMaterial.mColor = aColor.equals("None") ? Dyes._NULL : Dyes.get(aColor); + String[] aRGBA = GregTech_API.sMaterialProperties.get( + aConfigPath, + "MatRGBA", + aMaterial.mRGBa[0] + "," + aMaterial.mRGBa[1] + "," + aMaterial.mRGBa[2] + "," + aMaterial.mRGBa[3] + ",") + .split(","); + aMaterial.mRGBa[0] = Short.parseShort(aRGBA[0]); + aMaterial.mRGBa[1] = Short.parseShort(aRGBA[1]); + aMaterial.mRGBa[2] = Short.parseShort(aRGBA[2]); + aMaterial.mRGBa[3] = Short.parseShort(aRGBA[3]); + } + + private static void addToolValues(Materials aMaterial, String aConfigPath) { + aMaterial.mDurability = GregTech_API.sMaterialProperties + .get(aConfigPath, "ToolDurability", aMaterial.mDurability); + aMaterial.mToolSpeed = (float) GregTech_API.sMaterialProperties + .get(aConfigPath, "ToolSpeed", aMaterial.mToolSpeed); + aMaterial.mToolQuality = (byte) GregTech_API.sMaterialProperties + .get(aConfigPath, "ToolQuality", aMaterial.mToolQuality); + // Moved from GT_Proxy? (Not sure) + aMaterial.mHandleMaterial = (aMaterial == Desh ? aMaterial.mHandleMaterial + : aMaterial == Diamond || aMaterial == Thaumium ? Wood + : aMaterial.contains(SubTag.BURNING) ? Blaze + : aMaterial.contains(SubTag.MAGICAL) && aMaterial.contains(SubTag.CRYSTAL) + && Thaumcraft.isModLoaded() ? Thaumium + : aMaterial.getMass() > Element.Tc.getMass() * 2 ? TungstenSteel + : aMaterial.getMass() > Element.Tc.getMass() ? Steel : Wood); + + if (aMaterial == MaterialsUEVplus.SpaceTime) { + aMaterial.mHandleMaterial = Materials.Infinity; + } + + if (aMaterial == MaterialsUEVplus.TranscendentMetal) { + aMaterial.mHandleMaterial = Materials.DraconiumAwakened; + } + + if (aMaterial == MaterialsUEVplus.Eternity) { + aMaterial.mHandleMaterial = MaterialsUEVplus.SpaceTime; + } + + if (aMaterial == MaterialsUEVplus.MagMatter) { + aMaterial.mHandleMaterial = MaterialsUEVplus.MagnetohydrodynamicallyConstrainedStarMatter; + } + } + + private static void addEnchantmentValues(Materials aMaterial, String aConfigPath) { + aMaterial.mEnchantmentToolsLevel = (byte) GregTech_API.sMaterialProperties + .get(aConfigPath, "EnchantmentLevel", aMaterial.mEnchantmentToolsLevel); + String aEnchantmentName = GregTech_API.sMaterialProperties.get( + aConfigPath, + "Enchantment", + aMaterial.mEnchantmentTools != null ? aMaterial.mEnchantmentTools.getName() : ""); + if (aMaterial.mEnchantmentTools != null && !aEnchantmentName.equals(aMaterial.mEnchantmentTools.getName())) + IntStream.range(0, Enchantment.enchantmentsList.length) + .filter(i -> aEnchantmentName.equals(Enchantment.enchantmentsList[i].getName())) + .forEach(i -> aMaterial.mEnchantmentTools = Enchantment.enchantmentsList[i]); + } + + private static void addProcessingIntoValues(Materials aMaterial, String aConfigPath) { + aMaterial.mSmeltInto = MATERIALS_MAP + .get(GregTech_API.sMaterialProperties.get(aConfigPath, "MaterialSmeltInto", aMaterial.mSmeltInto.mName)); + aMaterial.mMacerateInto = MATERIALS_MAP.get( + GregTech_API.sMaterialProperties.get(aConfigPath, "MaterialMacerateInto", aMaterial.mMacerateInto.mName)); + aMaterial.mArcSmeltInto = MATERIALS_MAP.get( + GregTech_API.sMaterialProperties.get(aConfigPath, "MaterialArcSmeltInto", aMaterial.mArcSmeltInto.mName)); + aMaterial.mDirectSmelting = MATERIALS_MAP.get( + GregTech_API.sMaterialProperties + .get(aConfigPath, "MaterialDirectSmeltInto", aMaterial.mDirectSmelting.mName)); + aMaterial.mAutoGenerateBlastFurnaceRecipes = GregTech_API.sMaterialProperties + .get(aConfigPath, "AutoGenerateBlastFurnaceRecipes", aMaterial.mAutoGenerateBlastFurnaceRecipes); + } + + private static void addMultiplierValues(Materials aMaterial, String aConfigPath) { + aMaterial.mOreValue = GregTech_API.sMaterialProperties.get(aConfigPath, "OreValue", aMaterial.mOreValue); + aMaterial.setOreMultiplier( + GregTech_API.sMaterialProperties.get(aConfigPath, "OreMultiplier", aMaterial.mOreMultiplier)); + aMaterial.setSmeltingMultiplier( + GregTech_API.sMaterialProperties.get(aConfigPath, "OreSmeltingMultiplier", aMaterial.mSmeltingMultiplier)); + aMaterial.setByProductMultiplier( + GregTech_API.sMaterialProperties + .get(aConfigPath, "OreByProductMultiplier", aMaterial.mByProductMultiplier)); + } + + private static void addHasGasFluid(Materials aMaterial, String aConfigPath) { + + if (!aMaterial.mIconSet.is_custom) { + aMaterial.mHasPlasma = GregTech_API.sMaterialProperties.get(aConfigPath, "AddPlasma", aMaterial.mHasPlasma); + if (aMaterial.mHasPlasma) { + GT_Mod.gregtechproxy.addAutogeneratedPlasmaFluid(aMaterial); + } + aMaterial.mHasGas = GregTech_API.sMaterialProperties.get(aConfigPath, "AddGas", aMaterial.mHasGas); + if (aMaterial.mHasGas) { + GT_FluidFactory + .of(aMaterial.mName.toLowerCase(), aMaterial.mDefaultLocalName, aMaterial, GAS, aMaterial.mGasTemp); + } + } + } + + private static void addInternalStuff(Materials aMaterial, String aConfigPath) { + aMaterial.mMetaItemSubID = GregTech_API.sMaterialProperties + .get(aConfigPath, "MaterialID", aMaterial.mCustomOre ? -1 : aMaterial.mMetaItemSubID); + aMaterial.mTypes = GregTech_API.sMaterialProperties.get( + aConfigPath, + "MaterialTypes", + aMaterial.mCustomOre ? 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128 : aMaterial.mTypes); + aMaterial.mUnificatable = GregTech_API.sMaterialProperties + .get(aConfigPath, "Unificatable", aMaterial.mUnificatable); + aMaterial.mHasParentMod = GregTech_API.sMaterialProperties + .get(aConfigPath, "HasParentMod", aMaterial.mHasParentMod); + } + + private static void addLocalisation(Materials aMaterial, String aConfigPath) { + aMaterial.mDefaultLocalName = GregTech_API.sMaterialProperties.get( + aConfigPath, + "MaterialName", + aMaterial.mCustomOre ? "CustomOre" + aMaterial.mCustomID : aMaterial.mDefaultLocalName); + aMaterial.mChemicalFormula = GregTech_API.sMaterialProperties + .get(aConfigPath, "ChemicalFormula", aMaterial.mChemicalFormula); + } + + private static String getConfigPath(Materials aMaterial) { + String cOre = aMaterial.mCustomOre ? aMaterial.mCustomID : aMaterial.mName; + return "materials." + aMaterial.mConfigSection + "." + cOre; + } + + private static void addHarvestLevelNerfs(Materials aMaterial, String aConfigPath) { + /* Moved the harvest level changes from GT_Mod to have fewer things iterating over MATERIALS_ARRAY */ + if (GT_Mod.gregtechproxy.mChangeHarvestLevels && aMaterial.mToolQuality > 0 + && aMaterial.mMetaItemSubID < GT_Mod.gregtechproxy.mHarvestLevel.length + && aMaterial.mMetaItemSubID >= 0) { + GT_Mod.gregtechproxy.mHarvestLevel[aMaterial.mMetaItemSubID] = GregTech_API.sMaterialProperties + .get(aConfigPath, "HarvestLevel", aMaterial.mToolQuality); + } + } + + private static void addHarvestLevels() { + GT_Mod.gregtechproxy.mChangeHarvestLevels = GregTech_API.sMaterialProperties + .get("harvestlevel", "ActivateHarvestLevelChange", false); + GT_Mod.gregtechproxy.mMaxHarvestLevel = Math + .min(15, GregTech_API.sMaterialProperties.get("harvestlevel", "MaxHarvestLevel", 7)); + GT_Mod.gregtechproxy.mGraniteHavestLevel = GregTech_API.sMaterialProperties + .get("harvestlevel", "GraniteHarvestLevel", 3); + } + + public static void initMaterialProperties() { + addHarvestLevels(); + for (Materials aMaterial : MATERIALS_MAP.values()) { + if (aMaterial != null && aMaterial != Materials._NULL && aMaterial != Materials.Empty) { + + String aConfigPath = getConfigPath(aMaterial); + + addFuelValues(aMaterial, aConfigPath); + addTemperatureValues(aMaterial, aConfigPath); + addDensityValues(aMaterial, aConfigPath); + addColorValues(aMaterial, aConfigPath); + addToolValues(aMaterial, aConfigPath); + addEnchantmentValues(aMaterial, aConfigPath); + addProcessingIntoValues(aMaterial, aConfigPath); + addMultiplierValues(aMaterial, aConfigPath); + addHasGasFluid(aMaterial, aConfigPath); + addInternalStuff(aMaterial, aConfigPath); + addLocalisation(aMaterial, aConfigPath); + SubTagCalculation(aMaterial, aConfigPath); + OreByProductsCalculation(aMaterial, aConfigPath); + OreReRegistrationsCalculation(aMaterial, aConfigPath); + aspectCalculation(aMaterial, aConfigPath); + addHarvestLevelNerfs(aMaterial, aConfigPath); + } + } + } + + private static void aspectCalculation(Materials aMaterial, String aConfigPath) { + + String aDefaultAspectString = aMaterial.mAspects.stream() + .map(aAspectStack -> aAspectStack.mAspect.toString()) + .collect(Collectors.joining(",", ",", "")); + String aDefaultAspectAmountString = aMaterial.mAspects.stream() + .map(aAspectStack -> String.valueOf(aAspectStack.mAmount)) + .collect(Collectors.joining(",", ",", "")); + + String aConfigAspectString = GregTech_API.sMaterialProperties + .get(aConfigPath, "ListTCAspects", aDefaultAspectString); + String aConfigAspectAmountString = GregTech_API.sMaterialProperties + .get(aConfigPath, "ListTCAspectAmounts", aDefaultAspectAmountString); + + if (!aConfigAspectString.equals(aDefaultAspectString) + || !aConfigAspectAmountString.equals(aDefaultAspectAmountString)) { + aMaterial.mAspects.clear(); + if (aConfigAspectString.length() > 0) { + String[] aAspects = aConfigAspectString.split(","); + String[] aAspectAmounts = aConfigAspectAmountString.split(","); + for (int i = 0; i < aAspects.length; i++) { + String aAspectString = aAspects[i]; + long aAspectAmount = Long.parseLong(aAspectAmounts[i]); + TC_AspectStack aAspectStack = new TC_AspectStack(TC_Aspects.valueOf(aAspectString), aAspectAmount); + aMaterial.mAspects.add(aAspectStack); + } + } + } + } + + private static void OreReRegistrationsCalculation(Materials aMaterial, String aConfigPath) { + String aDefaultMatReRegString = aMaterial.mOreReRegistrations.stream() + .map(aTag -> aTag.mName) + .collect(Collectors.joining(",", ",", "")); + String aConfigMatMatReRegString = GregTech_API.sMaterialProperties + .get(aConfigPath, "ListMaterialReRegistrations", aDefaultMatReRegString); + if (!aConfigMatMatReRegString.equals(aDefaultMatReRegString)) { + aMaterial.mOreReRegistrations.clear(); + if (aConfigMatMatReRegString.length() > 0) { + Arrays.stream(aConfigMatMatReRegString.split(",")) + .map(MATERIALS_MAP::get) + .filter(Objects::nonNull) + .forEach(aMat -> aMaterial.mOreReRegistrations.add(aMat)); + } + } + } + + private static void OreByProductsCalculation(Materials aMaterial, String aConfigPath) { + String aDefaultMatByProString = aMaterial.mOreByProducts.stream() + .map(aTag -> aTag.mName) + .collect(Collectors.joining(",", ",", "")); + String aConfigMatByProString = GregTech_API.sMaterialProperties + .get(aConfigPath, "ListMaterialByProducts", aDefaultMatByProString); + if (!aConfigMatByProString.equals(aDefaultMatByProString)) { + aMaterial.mOreByProducts.clear(); + if (aConfigMatByProString.length() > 0) { + Arrays.stream(aConfigMatByProString.split(",")) + .map(MATERIALS_MAP::get) + .filter(Objects::nonNull) + .forEach(aMat -> aMaterial.mOreByProducts.add(aMat)); + } + } + } + + /** + * Converts the pre-defined list of SubTags from a material into a list of SubTag names for setting/getting to/from + * the config. It is then converted to a String[] and finally to a singular String for insertion into the config If + * the config string is different from the default, we then want to clear the Materials SubTags and insert new ones + * from the config string. + */ + private static void SubTagCalculation(Materials aMaterial, String aConfigPath) { + String aDefaultTagString = aMaterial.mSubTags.stream() + .map(aTag -> aTag.mName) + .collect(Collectors.joining(",", ",", "")); + String aConfigTagString = GregTech_API.sMaterialProperties.get(aConfigPath, "ListSubTags", aDefaultTagString); + if (!aConfigTagString.equals(aDefaultTagString)) { + aMaterial.mSubTags.clear(); + if (aConfigTagString.length() > 0) { + Arrays.stream(aConfigTagString.split(",")) + .map(SubTag.sSubTags::get) + .filter(Objects::nonNull) + .forEach(aTag -> aMaterial.mSubTags.add(aTag)); + } + } + } + + /** + * This is for keeping compatibility with addons mods (Such as TinkersGregworks etc.) that looped over the old + * materials enum + */ + @Deprecated + public static Materials valueOf(String aMaterialName) { + return getMaterialsMap().get(aMaterialName); + } + + /** + * This is for keeping compatibility with addons mods (Such as TinkersGregworks etc.) that looped over the old + * materials enum + */ + public static Materials[] values() { + return MATERIALS_ARRAY; + } + + /** + * This should only be used for getting a Material by its name as a String. Do not loop over this map, use values(). + */ + public static Map<String, Materials> getMaterialsMap() { + return MATERIALS_MAP; + } + + @Nonnull + public static Materials get(String aMaterialName) { + return getWithFallback(aMaterialName, Materials._NULL); + } + + @Nonnull + public static Materials getWithFallback(String name, @Nonnull Materials fallback) { + Materials material = getMaterialsMap().get(name); + if (material != null) { + return material; + } + return fallback; + } + + public static Materials getRealMaterial(String aMaterialName) { + return get(aMaterialName).mMaterialInto; + } + + /** + * Adds a Class implementing IMaterialRegistrator to the master list + */ + public static boolean add(IMaterialHandler aRegistrator) { + if (aRegistrator == null) return false; + return mMaterialHandlers.add(aRegistrator); + } + + public static String getLocalizedNameForItem(String aFormat, int aMaterialID) { + if (aMaterialID >= 0 && aMaterialID < 1000) { + Materials aMaterial = GregTech_API.sGeneratedMaterials[aMaterialID]; + if (aMaterial != null) return aMaterial.getLocalizedNameForItem(aFormat); + } + return aFormat; + } + + public static Collection<Materials> getAll() { + return MATERIALS_MAP.values(); + } + + public Materials disableAutoGeneratedBlastFurnaceRecipes() { + mAutoGenerateBlastFurnaceRecipes = false; + return this; + } + + public Materials disableAutoGeneratedVacuumFreezerRecipe() { + mAutoGenerateVacuumFreezerRecipes = false; + return this; + } + + public Materials setTurbineMultipliers(float steamMultiplier, float gasMultiplier, float plasmaMultiplier) { + mSteamMultiplier = steamMultiplier; + mGasMultiplier = gasMultiplier; + mPlasmaMultiplier = plasmaMultiplier; + return this; + } + + public Materials disableAutoGeneratedRecycleRecipes() { + mAutoGenerateRecycleRecipes = false; + return this; + } + + /** + * This is for keeping compatibility with addons mods (Such as TinkersGregworks etc.) that looped over the old + * materials enum + */ + @Deprecated + public String name() { + return mName; + } + + public boolean isRadioactive() { + if (mElement != null) return mElement.mHalfLifeSeconds >= 0; + + return mMaterialList.stream() + .map(stack -> stack.mMaterial) + .anyMatch(Materials::isRadioactive); + } + + public long getProtons() { + if (mElement != null) return mElement.getProtons(); + if (mMaterialList.size() == 0) return Element.Tc.getProtons(); + long rAmount = 0, tAmount = 0; + for (MaterialStack tMaterial : mMaterialList) { + tAmount += tMaterial.mAmount; + rAmount += tMaterial.mAmount * tMaterial.mMaterial.getProtons(); + } + return (getDensity() * rAmount) / (tAmount * M); + } + + public long getNeutrons() { + if (mElement != null) return mElement.getNeutrons(); + if (mMaterialList.size() == 0) return Element.Tc.getNeutrons(); + long rAmount = 0, tAmount = 0; + for (MaterialStack tMaterial : mMaterialList) { + tAmount += tMaterial.mAmount; + rAmount += tMaterial.mAmount * tMaterial.mMaterial.getNeutrons(); + } + return (getDensity() * rAmount) / (tAmount * M); + } + + public long getMass() { + if (mElement != null) return mElement.getMass(); + if (mMaterialList.size() == 0) return Element.Tc.getMass(); + long rAmount = 0, tAmount = 0; + for (MaterialStack tMaterial : mMaterialList) { + tAmount += tMaterial.mAmount; + rAmount += tMaterial.mAmount * tMaterial.mMaterial.getMass(); + } + return (getDensity() * rAmount) / (tAmount * M); + } + + public long getDensity() { + return mDensity; + } + + public String getToolTip() { + return getToolTip(1, false); + } + + public String getToolTip(boolean aShowQuestionMarks) { + return getToolTip(1, aShowQuestionMarks); + } + + public String getToolTip(long aMultiplier) { + return getToolTip(aMultiplier, false); + } + + public String getToolTip(long aMultiplier, boolean aShowQuestionMarks) { + if (!aShowQuestionMarks && mChemicalFormula.equals("?")) return ""; + if (aMultiplier >= M * 2 && !mMaterialList.isEmpty()) { + return ((mElement != null || (mMaterialList.size() < 2 && mMaterialList.get(0).mAmount == 1)) + ? mChemicalFormula + : "(" + mChemicalFormula + ")") + aMultiplier; + } + return mChemicalFormula; + } + + /** + * Adds an ItemStack to this Material. + */ + public Materials add(ItemStack aStack) { + if (aStack != null && !contains(aStack)) mMaterialItems.add(aStack); + return this; + } + + /** + * This is used to determine if any of the ItemStacks belongs to this Material. + */ + public boolean contains(ItemStack... aStacks) { + if (aStacks == null || aStacks.length == 0) return false; + return mMaterialItems.stream() + .anyMatch( + tStack -> Arrays.stream(aStacks) + .anyMatch(aStack -> GT_Utility.areStacksEqual(aStack, tStack, !tStack.hasTagCompound()))); + } + + /** + * This is used to determine if an ItemStack belongs to this Material. + */ + public boolean remove(ItemStack aStack) { + if (aStack == null) return false; + boolean temp = false; + int mMaterialItems_sS = mMaterialItems.size(); + for (int i = 0; i < mMaterialItems_sS; i++) if (GT_Utility.areStacksEqual(aStack, mMaterialItems.get(i))) { + mMaterialItems.remove(i--); + temp = true; + } + return temp; + } + + /** + * Adds a SubTag to this Material + */ + @Override + public ISubTagContainer add(SubTag... aTags) { + if (aTags != null) for (SubTag aTag : aTags) if (aTag != null && !contains(aTag)) { + aTag.addContainerToList(this); + mSubTags.add(aTag); + } + return this; + } + + /** + * If this Material has this exact SubTag + */ + @Override + public boolean contains(SubTag aTag) { + return mSubTags.contains(aTag); + } + + /** + * Removes a SubTag from this Material + */ + @Override + public boolean remove(SubTag aTag) { + return mSubTags.remove(aTag); + } + + /** + * Sets the Heat Damage for this Material (negative = frost) + */ + @SuppressWarnings("UnusedReturnValue") // Maintains signature + public Materials setHeatDamage(float aHeatDamage) { + mHeatDamage = aHeatDamage; + return this; + } + + /** + * Adds a Material to the List of Byproducts when grinding this Ore. Is used for more precise Ore grinding, so that + * it is possible to choose between certain kinds of Materials. + */ + @SuppressWarnings("UnusedReturnValue") // Maintains signature + public Materials addOreByProduct(Materials aMaterial) { + if (!mOreByProducts.contains(aMaterial.mMaterialInto)) mOreByProducts.add(aMaterial.mMaterialInto); + return this; + } + + /** + * Adds multiple Materials to the List of Byproducts when grinding this Ore. Is used for more precise Ore grinding, + * so that it is possible to choose between certain kinds of Materials. + */ + public Materials addOreByProducts(Materials... aMaterials) { + for (Materials tMaterial : aMaterials) if (tMaterial != null) addOreByProduct(tMaterial); + return this; + } + + /** + * If this Ore gives multiple drops of its Main Material. Lapis Ore for example gives about 6 drops. + */ + public Materials setOreMultiplier(int aOreMultiplier) { + if (aOreMultiplier > 0) mOreMultiplier = aOreMultiplier; + return this; + } + + /** + * If this Ore gives multiple drops of its Byproduct Material. + */ + @SuppressWarnings("UnusedReturnValue") // Maintains signature + public Materials setByProductMultiplier(int aByProductMultiplier) { + if (aByProductMultiplier > 0) mByProductMultiplier = aByProductMultiplier; + return this; + } + + /** + * If this Ore gives multiple drops of its Main Material. Lapis Ore for example gives about 6 drops. + */ + public Materials setSmeltingMultiplier(int aSmeltingMultiplier) { + if (aSmeltingMultiplier > 0) mSmeltingMultiplier = aSmeltingMultiplier; + return this; + } + + /** + * This Ore should be molten directly into an Ingot of this Material instead of an Ingot of itself. + */ + public Materials setDirectSmelting(Materials aMaterial) { + if (aMaterial != null) mDirectSmelting = aMaterial.mMaterialInto.mDirectSmelting; + return this; + } + + /** + * This Material should be the Main Material this Ore gets ground into. Example, Chromite giving Chrome or Tungstate + * giving Tungsten. + */ + @SuppressWarnings("UnusedReturnValue") // Maintains signature + public Materials setOreReplacement(Materials aMaterial) { + if (aMaterial != null) mOreReplacement = aMaterial.mMaterialInto.mOreReplacement; + return this; + } + + /** + * This Material smelts always into an instance of aMaterial. Used for Magnets. + */ + public Materials setSmeltingInto(Materials aMaterial) { + if (aMaterial != null) mSmeltInto = aMaterial.mMaterialInto.mSmeltInto; + return this; + } + + /** + * This Material arc smelts always into an instance of aMaterial. Used for Wrought Iron. + */ + @SuppressWarnings("UnusedReturnValue") // Maintains signature + public Materials setArcSmeltingInto(Materials aMaterial) { + if (aMaterial != null) mArcSmeltInto = aMaterial.mMaterialInto.mArcSmeltInto; + return this; + } + + /** + * This Material macerates always into an instance of aMaterial. + */ + public Materials setMaceratingInto(Materials aMaterial) { + if (aMaterial != null) mMacerateInto = aMaterial.mMaterialInto.mMacerateInto; + return this; + } + + public Materials setEnchantmentForTools(Enchantment aEnchantment, int aEnchantmentLevel) { + mEnchantmentTools = aEnchantment; + mEnchantmentToolsLevel = (byte) aEnchantmentLevel; + return this; + } + + @SuppressWarnings("UnusedReturnValue") // Maintains signature + public Materials setEnchantmentForArmors(Enchantment aEnchantment, int aEnchantmentLevel) { + mEnchantmentArmors = aEnchantment; + mEnchantmentArmorsLevel = (byte) aEnchantmentLevel; + return this; + } + + public FluidStack getSolid(long aAmount) { + if (mSolid == null) return null; + return new FluidStack(mSolid, (int) aAmount); + } + + public FluidStack getFluid(long aAmount) { + if (mFluid == null) return null; + return new FluidStack(mFluid, (int) aAmount); + } + + public FluidStack getGas(long aAmount) { + if (mGas == null) return null; + return new FluidStack(mGas, (int) aAmount); + } + + public FluidStack getPlasma(long aAmount) { + if (mPlasma == null) return null; + return new FluidStack(mPlasma, (int) aAmount); + } + + public FluidStack getMolten(long aAmount) { + if (mStandardMoltenFluid == null) return null; + return new FluidStack(mStandardMoltenFluid, (int) aAmount); + } + + @Override + public short[] getRGBA() { + return mRGBa; + } + + @Override + public String toString() { + return this.mName; + } + + public String getDefaultLocalizedNameForItem(String aFormat) { + return String.format( + aFormat.replace("%s", "%temp") + .replace("%material", "%s"), + this.mDefaultLocalName) + .replace("%temp", "%s"); + } + + public String getLocalizedNameForItem(String aFormat) { + return String.format( + aFormat.replace("%s", "%temp") + .replace("%material", "%s"), + this.mLocalizedName) + .replace("%temp", "%s"); + } + + public boolean hasCorrespondingFluid() { + return hasCorrespondingFluid; + } + + public Materials setHasCorrespondingFluid(boolean hasCorrespondingFluid) { + this.hasCorrespondingFluid = hasCorrespondingFluid; + return this; + } + + public boolean hasCorrespondingGas() { + return hasCorrespondingGas; + } + + public Materials setHasCorrespondingGas(boolean hasCorrespondingGas) { + this.hasCorrespondingGas = hasCorrespondingGas; + return this; + } + + public Materials setHasCorrespondingPlasma(boolean hasCorrespondingPlasma) { + this.mHasPlasma = hasCorrespondingPlasma; + return this; + } + + public boolean canBeCracked() { + return canBeCracked; + } + + public Materials setCanBeCracked(boolean canBeCracked) { + this.canBeCracked = canBeCracked; + return this; + } + + public int getLiquidTemperature() { + return mMeltingPoint == 0 ? 295 : mMeltingPoint; + } + + public Materials setLiquidTemperature(int liquidTemperature) { + this.mMeltingPoint = liquidTemperature; + return this; + } + + public int getGasTemperature() { + return mGasTemp == 0 ? 295 : mMeltingPoint; + } + + public Materials setGasTemperature(int gasTemperature) { + this.mGasTemp = gasTemperature; + return this; + } + + public Materials setHydroCrackedFluids(Fluid[] hydroCrackedFluids) { + this.hydroCrackedFluids = hydroCrackedFluids; + return this; + } + + public FluidStack getLightlyHydroCracked(int amount) { + if (hydroCrackedFluids[0] == null) { + return null; + } + return new FluidStack(hydroCrackedFluids[0], amount); + } + + public FluidStack getModeratelyHydroCracked(int amount) { + if (hydroCrackedFluids[0] == null) { + return null; + } + return new FluidStack(hydroCrackedFluids[1], amount); + } + + public FluidStack getSeverelyHydroCracked(int amount) { + if (hydroCrackedFluids[0] == null) { + return null; + } + return new FluidStack(hydroCrackedFluids[2], amount); + } + + public Materials setSteamCrackedFluids(Fluid[] steamCrackedFluids) { + this.steamCrackedFluids = steamCrackedFluids; + return this; + } + + public FluidStack getLightlySteamCracked(int amount) { + if (hydroCrackedFluids[0] == null) { + return null; + } + return new FluidStack(steamCrackedFluids[0], amount); + } + + public FluidStack getModeratelySteamCracked(int amount) { + if (hydroCrackedFluids[0] == null) { + return null; + } + return new FluidStack(steamCrackedFluids[1], amount); + } + + public FluidStack getSeverelySteamCracked(int amount) { + if (hydroCrackedFluids[0] == null) { + return null; + } + return new FluidStack(steamCrackedFluids[2], amount); + } + + /** + * Check that the material is a proper soldering fluid + ** + * @return true if Materials is a proper soldering fluid + */ + public boolean isProperSolderingFluid() { + return mStandardMoltenFluid != null && contains(SubTag.SOLDERING_MATERIAL) + && !(GregTech_API.mUseOnlyGoodSolderingMaterials && !contains(SubTag.SOLDERING_MATERIAL_GOOD)); + } + + public ItemStack getCells(int amount) { + return GT_OreDictUnificator.get(OrePrefixes.cell, this, amount); + } + + public ItemStack getDust(int amount) { + return GT_OreDictUnificator.get(OrePrefixes.dust, this, amount); + } + + public ItemStack getDustSmall(int amount) { + return GT_OreDictUnificator.get(OrePrefixes.dustSmall, this, amount); + } + + public ItemStack getDustTiny(int amount) { + return GT_OreDictUnificator.get(OrePrefixes.dustTiny, this, amount); + } + + public ItemStack getGems(int amount) { + return GT_OreDictUnificator.get(OrePrefixes.gem, this, amount); + } + + public ItemStack getIngots(int amount) { + return GT_OreDictUnificator.get(OrePrefixes.ingot, this, amount); + } + + public ItemStack getNuggets(int amount) { + return GT_OreDictUnificator.get(OrePrefixes.nugget, this, amount); + } + + public ItemStack getBlocks(int amount) { + return GT_OreDictUnificator.get(OrePrefixes.block, this, amount); + } + + public ItemStack getPlates(int amount) { + return GT_OreDictUnificator.get(OrePrefixes.plate, this, amount); + } + + public static Materials getGtMaterialFromFluid(Fluid fluid) { + return FLUID_MAP.get(fluid); + } + + public ItemStack getNanite(int amount) { + return GT_OreDictUnificator.get(OrePrefixes.nanite, this, amount); + } +} diff --git a/src/main/java/gregtech/api/enums/MaterialsBotania.java b/src/main/java/gregtech/api/enums/MaterialsBotania.java new file mode 100644 index 0000000000..6fe538a0bc --- /dev/null +++ b/src/main/java/gregtech/api/enums/MaterialsBotania.java @@ -0,0 +1,238 @@ +package gregtech.api.enums; + +import static gregtech.api.enums.OrePrefixes.gem; +import static gregtech.api.enums.OrePrefixes.ingot; +import static gregtech.api.enums.OrePrefixes.nugget; +import static gregtech.api.enums.OrePrefixes.plate; +import static gregtech.api.enums.OrePrefixes.rod; +import static gregtech.api.enums.OrePrefixes.rotor; + +import java.util.Arrays; + +import gregtech.api.enums.TC_Aspects.TC_AspectStack; + +public class MaterialsBotania { + + // Botania materials. + public static Materials Manasteel = new MaterialBuilder(201, TextureSet.SET_METALLIC, "Manasteel") + .setName("Manasteel") + .setRGBA(150, 219, 252, 255) + .addDustItems() + .addMetalItems() + .addToolHeadItems() + .addGearItems() + .setToolSpeed(8.0F) + .setDurability(5120) + .setToolQuality(4) + .setMeltingPoint(1500) + .setBlastFurnaceTemp(1500) + .setBlastFurnaceRequired(true) + .setAspects( + Arrays.asList(new TC_AspectStack(TC_Aspects.METALLUM, 3), new TC_AspectStack(TC_Aspects.PRAECANTATIO, 1))) + .constructMaterial(); + public static Materials Terrasteel = new MaterialBuilder(202, TextureSet.SET_METALLIC, "Terrasteel") + .setName("Terrasteel") + .setRGBA(76, 191, 38, 255) + .addDustItems() + .addMetalItems() + .addToolHeadItems() + .addGearItems() + .setToolSpeed(32.0F) + .setDurability(10240) + .setToolQuality(5) + .setMeltingPoint(5400) + .setBlastFurnaceTemp(5400) + .setBlastFurnaceRequired(true) + .setAspects( + Arrays.asList( + new TC_AspectStack(TC_Aspects.METALLUM, 2), + new TC_AspectStack(TC_Aspects.TERRA, 1), + new TC_AspectStack(TC_Aspects.PRAECANTATIO, 1))) + .constructMaterial(); + public static Materials ElvenElementium = new MaterialBuilder(203, TextureSet.SET_METALLIC, "Elven Elementium") + .setName("ElvenElementium") + .setRGBA(219, 37, 205, 255) + .addDustItems() + .addMetalItems() + .addToolHeadItems() + .addGearItems() + .setToolSpeed(20.0F) + .setDurability(32768) + .setToolQuality(7) + .setMeltingPoint(7200) + .setBlastFurnaceTemp(7200) + .setBlastFurnaceRequired(true) + .setAspects( + Arrays.asList( + new TC_AspectStack(TC_Aspects.METALLUM, 3), + new TC_AspectStack(TC_Aspects.PRAECANTATIO, 2), + new TC_AspectStack(TC_Aspects.AURAM, 1))) + .constructMaterial(); + public static Materials Livingrock = new MaterialBuilder(204, new TextureSet("Livingrock", true), "Livingrock") + .setName("Livingrock") + .addDustItems() + .addToolHeadItems() + .addGearItems() + .setToolSpeed(1.0F) + .setDurability(0) + .setToolQuality(3) + .setOreValue(3) + .setDensityMultiplier(1) + .setDensityDivider(1) + .setAspects(Arrays.asList(new TC_AspectStack(TC_Aspects.TERRA, 2), new TC_AspectStack(TC_Aspects.VICTUS, 2))) + .constructMaterial(); + public static Materials GaiaSpirit = new Materials( + 205, + TextureSet.SET_METALLIC.withBlockTextures("GaiaSpirit"), + 32.0F, + 850000, + 12, + 1 | 2 | 64 | 128, + 255, + 255, + 255, + 0, + "GaiaSpirit", + "Gaia Spirit", + -1, + -1, + 0, + 0, + false, + true, + 2, + 1, + 1, + Dyes._NULL, + Arrays.asList( + new TC_AspectStack(TC_Aspects.PRAECANTATIO, 27), + new TC_AspectStack(TC_Aspects.AURAM, 24), + new TC_AspectStack(TC_Aspects.VICTUS, 24), + new TC_AspectStack(TC_Aspects.METALLUM, 1))); + public static Materials Livingwood = new MaterialBuilder(206, new TextureSet("Livingwood", true), "Livingwood") + .setName("Livingwood") + .addDustItems() + .addMetalItems() + .addToolHeadItems() + .addGearItems() + .setToolSpeed(1.0F) + .setDurability(0) + .setToolQuality(3) + .setOreValue(3) + .setDensityMultiplier(1) + .setDensityDivider(1) + .setAspects(Arrays.asList(new TC_AspectStack(TC_Aspects.ARBOR, 4), new TC_AspectStack(TC_Aspects.VICTUS, 2))) + .constructMaterial(); + public static Materials Dreamwood = new MaterialBuilder(207, new TextureSet("Dreamwood", true), "Dreamwood") + .setName("Dreamwood") + .addDustItems() + .addMetalItems() + .addToolHeadItems() + .addGearItems() + .setToolSpeed(1.0F) + .setDurability(0) + .setToolQuality(3) + .setOreValue(3) + .setDensityMultiplier(1) + .setDensityDivider(1) + .setAspects( + Arrays.asList( + new TC_AspectStack(TC_Aspects.ARBOR, 4), + new TC_AspectStack(TC_Aspects.AURAM, 2), + new TC_AspectStack(TC_Aspects.PRAECANTATIO, 1))) + .constructMaterial(); + public static Materials ManaDiamond = new Materials( + 208, + TextureSet.SET_DIAMOND, + 16.0F, + 2560, + 8, + 1 | 4, + 38, + 237, + 224, + 255, + "ManaDiamond", + "Mana Diamond", + -1, + -1, + 0, + 0, + false, + true, + 2, + 1, + 1, + Dyes._NULL, + Arrays.asList( + new TC_Aspects.TC_AspectStack(TC_Aspects.PRAECANTATIO, 4), + new TC_Aspects.TC_AspectStack(TC_Aspects.VITREUS, 4), + new TC_Aspects.TC_AspectStack(TC_Aspects.LUCRUM, 4))); + public static Materials BotaniaDragonstone = new Materials( + 209, + TextureSet.SET_DIAMOND, + 24.0F, + 3840, + 12, + 1 | 4, + 242, + 44, + 239, + 255, + "BotaniaDragonstone", + "Dragonstone", + -1, + -1, + 0, + 0, + false, + true, + 2, + 1, + 1, + Dyes._NULL, + Arrays.asList( + new TC_Aspects.TC_AspectStack(TC_Aspects.PRAECANTATIO, 6), + new TC_Aspects.TC_AspectStack(TC_Aspects.VITREUS, 6), + new TC_Aspects.TC_AspectStack(TC_Aspects.AURAM, 4))); + + public static void init() { + GaiaSpirit.mChemicalFormula = "Gs"; + Manasteel.mChemicalFormula = "Ms"; + Livingwood.mChemicalFormula = "Lw"; + Dreamwood.mChemicalFormula = "Dw"; + BotaniaDragonstone.mChemicalFormula = "Dg"; + Livingrock.mChemicalFormula = "Lv"; + Terrasteel.mChemicalFormula = "Tr"; + ElvenElementium.mChemicalFormula = "Ef"; + + Livingrock.add(SubTag.NO_SMASHING, SubTag.NO_SMELTING); + Livingwood.add(SubTag.WOOD, SubTag.FLAMMABLE, SubTag.NO_SMELTING, SubTag.NO_SMASHING); + Dreamwood.add(SubTag.WOOD, SubTag.FLAMMABLE, SubTag.NO_SMELTING, SubTag.NO_SMASHING); + ManaDiamond.add(SubTag.CRYSTAL, SubTag.NO_SMASHING, SubTag.NO_SMELTING); + BotaniaDragonstone.add(SubTag.CRYSTAL, SubTag.NO_SMASHING, SubTag.NO_SMELTING); + GaiaSpirit.add(SubTag.SOFT); + + // Botania native items + ingot.mNotGeneratedItems.add(Manasteel); + ingot.mNotGeneratedItems.add(Terrasteel); + ingot.mNotGeneratedItems.add(ElvenElementium); + ingot.mNotGeneratedItems.add(GaiaSpirit); + nugget.mNotGeneratedItems.add(Manasteel); + nugget.mNotGeneratedItems.add(Terrasteel); + nugget.mNotGeneratedItems.add(ElvenElementium); + gem.mNotGeneratedItems.add(ManaDiamond); + gem.mNotGeneratedItems.add(BotaniaDragonstone); + + // other stuff we don't want + ingot.mNotGeneratedItems.add(Livingwood); + ingot.mNotGeneratedItems.add(Dreamwood); + nugget.mNotGeneratedItems.add(Livingwood); + nugget.mNotGeneratedItems.add(Dreamwood); + rotor.mNotGeneratedItems.add(Livingrock); + + // stuff we want + plate.mGeneratedItems.add(Livingrock); + rod.mGeneratedItems.add(Livingrock); // this is not working + } +} diff --git a/src/main/java/gregtech/api/enums/MaterialsKevlar.java b/src/main/java/gregtech/api/enums/MaterialsKevlar.java new file mode 100644 index 0000000000..6612bc8b65 --- /dev/null +++ b/src/main/java/gregtech/api/enums/MaterialsKevlar.java @@ -0,0 +1,604 @@ +package gregtech.api.enums; + +import java.util.Arrays; + +import gregtech.api.objects.MaterialStack; + +public class MaterialsKevlar { + + public static Materials DiphenylmethaneDiisocyanate = new MaterialBuilder( + 796, + TextureSet.SET_DULL, + "4,4'-Diphenylmethane Diisocyanate").setName("DiphenylmethaneDiisocyanate") + .addDustItems() + .setRGB(255, 230, 50) + .setColor(Dyes.dyeYellow) + .setMeltingPoint(310) + .setMaterialList( + new MaterialStack(Materials.Carbon, 15), + new MaterialStack(Materials.Hydrogen, 10), + new MaterialStack(Materials.Nitrogen, 2), + new MaterialStack(Materials.Oxygen, 2)) + .setAspects( + Arrays.asList( + new TC_Aspects.TC_AspectStack(TC_Aspects.TERRA, 1), + new TC_Aspects.TC_AspectStack(TC_Aspects.VENENUM, 1))) + .constructMaterial(); // C15H10N2O2 + public static Materials DiaminodiphenylmethanMixture = new MaterialBuilder( + 795, + TextureSet.SET_FLUID, + "Diaminodiphenylmethane Mixture").setName("DiaminodiphenylmethanMixture") + .addCell() + .addFluid() + .setRGB(255, 243, 122) + .setColor(Dyes.dyeYellow) + .setMeltingPoint(365) + .setMaterialList( + new MaterialStack(Materials.Carbon, 13), + new MaterialStack(Materials.Hydrogen, 14), + new MaterialStack(Materials.Nitrogen, 2)) + .setAspects( + Arrays.asList( + new TC_Aspects.TC_AspectStack(TC_Aspects.AQUA, 1), + new TC_Aspects.TC_AspectStack(TC_Aspects.VENENUM, 1))) + .constructMaterial(); // C13H14N2 + public static Materials DiphenylmethaneDiisocyanateMixture = new MaterialBuilder( + 794, + TextureSet.SET_FLUID, + "Diphenylmethane Diisocyanate Mixture").setName("DiphenylmethaneDiisocyanateMixture") + .addCell() + .addFluid() + .setRGB(255, 230, 50) + .setColor(Dyes.dyeYellow) + .setMeltingPoint(310) + .setMaterialList( + new MaterialStack(Materials.Carbon, 15), + new MaterialStack(Materials.Hydrogen, 10), + new MaterialStack(Materials.Nitrogen, 2), + new MaterialStack(Materials.Oxygen, 2)) + .setAspects( + Arrays.asList( + new TC_Aspects.TC_AspectStack(TC_Aspects.AQUA, 1), + new TC_Aspects.TC_AspectStack(TC_Aspects.VENENUM, 1))) + .constructMaterial(); // C15H10N2O2 + public static Materials Butyraldehyde = new MaterialBuilder(793, TextureSet.SET_FLUID, "Butyraldehyde") + .setName("Butyraldehyde") + .addCell() + .addFluid() + .setRGB(255, 255, 255) + .setColor(Dyes.dyeWhite) + .setMeltingPoint(176) + .setMaterialList( + new MaterialStack(Materials.Carbon, 4), + new MaterialStack(Materials.Hydrogen, 8), + new MaterialStack(Materials.Oxygen, 1)) + .setAspects( + Arrays.asList( + new TC_Aspects.TC_AspectStack(TC_Aspects.AQUA, 1), + new TC_Aspects.TC_AspectStack(TC_Aspects.IGNIS, 1))) + .constructMaterial(); // C4H8O + public static Materials Isobutyraldehyde = new MaterialBuilder(792, TextureSet.SET_FLUID, "Isobutyraldehyde") + .setName("Isobutyraldehyde") + .addCell() + .addFluid() + .setRGB(255, 255, 255) + .setColor(Dyes.dyeWhite) + .setMeltingPoint(208) + .setExtraData(1) + .setMaterialList( + new MaterialStack(Materials.Carbon, 4), + new MaterialStack(Materials.Hydrogen, 8), + new MaterialStack(Materials.Oxygen, 1)) + .setAspects( + Arrays.asList( + new TC_Aspects.TC_AspectStack(TC_Aspects.AQUA, 1), + new TC_Aspects.TC_AspectStack(TC_Aspects.IGNIS, 1))) + .constructMaterial(); // C4H8O + public static Materials NickelTetracarbonyl = new MaterialBuilder(791, TextureSet.SET_FLUID, "Nickel Tetracarbonyl") + .setName("NickelTetracarbonyl") + .addCell() + .addFluid() + .setRGB(255, 255, 255) + .setColor(Dyes.dyeWhite) + .setMeltingPoint(256) + .setMaterialList( + new MaterialStack(Materials.Carbon, 4), + new MaterialStack(Materials.Nickel, 1), + new MaterialStack(Materials.Oxygen, 4)) + .setAspects( + Arrays.asList( + new TC_Aspects.TC_AspectStack(TC_Aspects.AQUA, 1), + new TC_Aspects.TC_AspectStack(TC_Aspects.METALLUM, 1))) + .constructMaterial(); // C4NiO4 + public static Materials KevlarCatalyst = new MaterialBuilder(790, TextureSet.SET_DULL, "Polyurethane Catalyst A") + .setName("PolyurethaneCatalystADust") + .addDustItems() + .setRGB(50, 50, 50) + .setColor(Dyes.dyeBlack) + .setMeltingPoint(300) + .setAspects( + Arrays.asList( + new TC_Aspects.TC_AspectStack(TC_Aspects.AQUA, 1), + new TC_Aspects.TC_AspectStack(TC_Aspects.LUCRUM, 1))) + .constructMaterial(); + public static Materials EthyleneOxide = new MaterialBuilder(789, TextureSet.SET_FLUID, "Ethylene Oxide") + .setName("EthyleneOxide") + .addCell() + .addGas() + .setRGB(255, 255, 255) + .setColor(Dyes.dyeWhite) + .setMeltingPoint(160) + .setMaterialList( + new MaterialStack(Materials.Carbon, 2), + new MaterialStack(Materials.Hydrogen, 4), + new MaterialStack(Materials.Oxygen, 1)) + .setAspects( + Arrays.asList( + new TC_Aspects.TC_AspectStack(TC_Aspects.AQUA, 1), + new TC_Aspects.TC_AspectStack(TC_Aspects.IGNIS, 1))) + .constructMaterial(); // C2H4O + public static Materials SiliconOil = new MaterialBuilder(788, TextureSet.SET_FLUID, "Silicon Oil") + .setName("SiliconOil") + .addCell() + .addFluid() + .setRGB(255, 255, 255) + .setColor(Dyes.dyeWhite) + .setMeltingPoint(473) + .setAspects( + Arrays.asList( + new TC_Aspects.TC_AspectStack(TC_Aspects.AQUA, 1), + new TC_Aspects.TC_AspectStack(TC_Aspects.MACHINA, 1))) + .constructMaterial(); + public static Materials Ethyleneglycol = new MaterialBuilder(787, TextureSet.SET_FLUID, "Ethylene Glycol") + .setName("EthyleneGlycol") + .addCell() + .addFluid() + .setRGB(255, 255, 255) + .setColor(Dyes.dyeWhite) + .setMeltingPoint(260) + .setMaterialList( + new MaterialStack(Materials.Carbon, 2), + new MaterialStack(Materials.Hydrogen, 6), + new MaterialStack(Materials.Oxygen, 2)) + .setAspects( + Arrays.asList( + new TC_Aspects.TC_AspectStack(TC_Aspects.AQUA, 1), + new TC_Aspects.TC_AspectStack(TC_Aspects.IGNIS, 1))) + .constructMaterial(); // C2H6O2 + public static Materials Acetaldehyde = new MaterialBuilder(786, TextureSet.SET_FLUID, "Acetaldehyde") + .setName("Acetaldehyde") + .addCell() + .addGas() + .setRGB(255, 255, 255) + .setColor(Dyes.dyeWhite) + .setMeltingPoint(150) + .setMaterialList( + new MaterialStack(Materials.Carbon, 2), + new MaterialStack(Materials.Hydrogen, 4), + new MaterialStack(Materials.Oxygen, 1)) + .setAspects( + Arrays.asList( + new TC_Aspects.TC_AspectStack(TC_Aspects.AQUA, 1), + new TC_Aspects.TC_AspectStack(TC_Aspects.IGNIS, 1))) + .constructMaterial(); // C2H4O + public static Materials Pentaerythritol = new MaterialBuilder(785, TextureSet.SET_DULL, "Pentaerythritol") + .setName("Pentaerythritol") + .addDustItems() + .setRGB(255, 255, 255) + .setColor(Dyes.dyeWhite) + .setMeltingPoint(533) + .setMaterialList( + new MaterialStack(Materials.Carbon, 5), + new MaterialStack(Materials.Hydrogen, 12), + new MaterialStack(Materials.Oxygen, 4)) + .setAspects( + Arrays.asList( + new TC_Aspects.TC_AspectStack(TC_Aspects.TERRA, 1), + new TC_Aspects.TC_AspectStack(TC_Aspects.LUCRUM, 1))) + .constructMaterial(); // C5H12O4 + public static Materials PolyurethaneResin = new MaterialBuilder(784, TextureSet.SET_FLUID, "Polyurethane Resin") + .setName("PolyurethaneResin") + .addCell() + .addFluid() + .setRGB(230, 230, 120) + .setColor(Dyes.dyeYellow) + .constructMaterial(); + public static Materials NMethylIIPyrrolidone = new MaterialBuilder( + 783, + TextureSet.SET_FLUID, + "N-Methyl-2-pyrrolidone").setName("NMethylpyrolidone") + .addCell() + .addFluid() + .setRGB(255, 255, 255) + .setColor(Dyes.dyeWhite) + .setMeltingPoint(249) + .setMaterialList( + new MaterialStack(Materials.Carbon, 5), + new MaterialStack(Materials.Hydrogen, 9), + new MaterialStack(Materials.Nitrogen, 1), + new MaterialStack(Materials.Oxygen, 1)) + .setAspects( + Arrays.asList( + new TC_Aspects.TC_AspectStack(TC_Aspects.TERRA, 1), + new TC_Aspects.TC_AspectStack(TC_Aspects.VENENUM, 1))) + .constructMaterial(); // C5H9NO + public static Materials TerephthaloylChloride = new MaterialBuilder( + 782, + TextureSet.SET_POWDER, + "Terephthaloyl Chloride").setName("TerephthaloylChloride") + .addDustItems() + .setRGB(0, 255, 12) + .setColor(Dyes.dyeGreen) + .setMeltingPoint(355) + .setMaterialList( + new MaterialStack(Materials.Carbon, 8), + new MaterialStack(Materials.Hydrogen, 4), + new MaterialStack(Materials.Chlorine, 2), + new MaterialStack(Materials.Oxygen, 2)) + .setAspects( + Arrays.asList( + new TC_Aspects.TC_AspectStack(TC_Aspects.TERRA, 1), + new TC_Aspects.TC_AspectStack(TC_Aspects.VITREUS, 1))) + .constructMaterial(); // C8H4Cl2O2 + public static Materials Acetylene = new MaterialBuilder(781, TextureSet.SET_FLUID, "Acetylene").setName("Acetylene") + .addCell() + .addGas() + .setRGB(255, 255, 255) + .setColor(Dyes.dyeWhite) + .setMeltingPoint(192) + .setMaterialList(new MaterialStack(Materials.Carbon, 2), new MaterialStack(Materials.Hydrogen, 2)) + .constructMaterial(); // C2H2 + // TODO + // Add + // to + // JUPITER + // Athmosphere + // and + // Enceladus + // and + // to + // moon + // of + // Saturn + public static Materials IVNitroaniline = new MaterialBuilder(780, TextureSet.SET_FLUID, "4-Nitroaniline") + .setName("4Nitroaniline") + .addCell() + .addFluid() + .setRGB(255, 135, 51) + .setColor(Dyes.dyeOrange) + .setMeltingPoint(420) + .setMaterialList( + new MaterialStack(Materials.Carbon, 6), + new MaterialStack(Materials.Hydrogen, 6), + new MaterialStack(Materials.Nitrogen, 2), + new MaterialStack(Materials.Oxygen, 2)) + .setAspects( + Arrays.asList( + new TC_Aspects.TC_AspectStack(TC_Aspects.TERRA, 1), + new TC_Aspects.TC_AspectStack(TC_Aspects.VITREUS, 1))) + .constructMaterial(); // C6H6N2O2 + public static Materials ParaPhenylenediamine = new MaterialBuilder( + 779, + TextureSet.SET_POWDER, + "para-Phenylenediamine").setName("pPhenylenediamine") + .addDustItems() + .setRGB(251, 236, 93) + .setColor(Dyes.dyeYellow) + .setMeltingPoint(293) + .setMaterialList( + new MaterialStack(Materials.Carbon, 6), + new MaterialStack(Materials.Hydrogen, 8), + new MaterialStack(Materials.Nitrogen, 2)) + .setAspects( + Arrays.asList( + new TC_Aspects.TC_AspectStack(TC_Aspects.TERRA, 1), + new TC_Aspects.TC_AspectStack(TC_Aspects.VITREUS, 1))) + .constructMaterial(); // C6H6N2 + public static Materials Methylamine = new MaterialBuilder(778, TextureSet.SET_FLUID, "Methylamine") + .setName("Methylamine") + .addCell() + .addGas() + .setRGB(65, 68, 105) + .setColor(Dyes.dyeGray) + .setMeltingPoint(180) + .setExtraData(1) + .setMaterialList( + new MaterialStack(Materials.Carbon, 1), + new MaterialStack(Materials.Hydrogen, 5), + new MaterialStack(Materials.Nitrogen, 1)) + .constructMaterial(); // CH5N + public static Materials Trimethylamine = new MaterialBuilder(777, TextureSet.SET_FLUID, "Trimethylamine") + .setName("Trimethylamine") + .addCell() + .addGas() + .setRGB(105, 68, 105) + .setColor(Dyes.dyeGray) + .setMeltingPoint(156) + .setExtraData(1) + .setMaterialList( + new MaterialStack(Materials.Carbon, 3), + new MaterialStack(Materials.Hydrogen, 9), + new MaterialStack(Materials.Nitrogen, 1)) + .constructMaterial(); // C3H9N + public static Materials GammaButyrolactone = new MaterialBuilder(776, TextureSet.SET_FLUID, "gamma-Butyrolactone") + .setName("GammaButyrolactone") + .addCell() + .addFluid() + .setRGB(255, 255, 151) + .setColor(Dyes.dyeYellow) + .setMeltingPoint(229) + .setMaterialList( + new MaterialStack(Materials.Carbon, 4), + new MaterialStack(Materials.Hydrogen, 6), + new MaterialStack(Materials.Oxygen, 2)) + .constructMaterial(); // C4H6O2 + public static Materials CalciumCarbide = new MaterialBuilder(775, TextureSet.SET_DULL, "Calcium Carbide") + .setName("CacliumCarbide") + .addDustItems() + .setRGB(235, 235, 235) + .setColor(Dyes.dyeGray) + .setMeltingPoint(2430) + .setMaterialList(new MaterialStack(Materials.Calcium, 1), new MaterialStack(Materials.Carbon, 2)) + .constructMaterial(); // CaC2 + public static Materials LiquidCrystalKevlar = new MaterialBuilder( + 774, + TextureSet.SET_FLUID, + "Liquid Crystal Kevlar").setName("LiquidCrystalKevlar") + .addCell() + .addFluid() + .setRGB(240, 240, 120) + .setColor(Dyes.dyeYellow) + .constructMaterial(); // [-CO-C6H4-CO-NH-C6H4-NH-]n + public static Materials IIButinIIVdiol = new MaterialBuilder(773, TextureSet.SET_POWDER, "2-Butin-1,4-diol") + .setName("2Butin14diol") + .addDustItems() + .setRGB(247, 247, 180) + .setColor(Dyes.dyeYellow) + .setMeltingPoint(331) + .setMaterialList( + new MaterialStack(Materials.Carbon, 4), + new MaterialStack(Materials.Hydrogen, 6), + new MaterialStack(Materials.Oxygen, 2)) + .constructMaterial(); // C4H6O2 + public static Materials NickelAluminide = new MaterialBuilder(772, TextureSet.SET_METALLIC, "Nickel Aluminide") + .setName("NickelAluminide") + .addDustItems() + .addMetalItems() + .setRGB(230, 230, 230) + .setColor(Dyes.dyeGray) + .setMeltingPoint(1668) + .setBlastFurnaceTemp(1668) + .setBlastFurnaceRequired(true) + .setMaterialList(new MaterialStack(Materials.Nickel, 1), new MaterialStack(Materials.Aluminium, 3)) + .constructMaterial() + .disableAutoGeneratedBlastFurnaceRecipes(); // NiAl3 + public static Materials RaneyNickelActivated = new MaterialBuilder(771, TextureSet.SET_POWDER, "Raney Nickel") + .setName("RaneyNickelActivated") + .addDustItems() + .setRGB(230, 230, 230) + .setColor(Dyes.dyeGray) + .setMeltingPoint(1955) + .setMaterialList(new MaterialStack(Materials.Nickel, 1), new MaterialStack(Materials.Aluminium, 1)) + .constructMaterial(); // NiAl + public static Materials BismuthIIIOxide = new MaterialBuilder(769, TextureSet.SET_POWDER, "Bismuth Oxide") + .setName("BismuthIIIOxide") + .addDustItems() + .setRGB(50, 50, 50) + .setColor(Dyes.dyeBlack) + .setMeltingPoint(1090) + .setMaterialList(new MaterialStack(Materials.Bismuth, 2), new MaterialStack(Materials.Oxygen, 3)) + .constructMaterial(); // Bi2O3 + public static Materials ThionylChloride = new MaterialBuilder(768, TextureSet.SET_FLUID, "Thionyl Chloride") + .setName("ThionylChloride") + .addCell() + .addFluid() + .setRGB(255, 255, 255) + .setColor(Dyes.dyeWhite) + .constructMaterial(); // SOCl2 + public static Materials SulfurDichloride = new MaterialBuilder(767, TextureSet.SET_FLUID, "Sulfur Dichloride") + .setName("SulfurDichloride") + .addCell() + .addFluid() + .setRGB(200, 0, 0) + .setColor(Dyes.dyeRed) + .constructMaterial(); // SCl2 + public static Materials DimethylTerephthalate = new MaterialBuilder( + 766, + TextureSet.SET_FLUID, + "Dimethyl Terephthalate").setName("DimethylTerephthalate") + .addCell() + .addFluid() + .setRGB(255, 255, 255) + .setColor(Dyes.dyeWhite) + .setMeltingPoint(415) + .setMaterialList( + new MaterialStack(Materials.Carbon, 10), + new MaterialStack(Materials.Hydrogen, 10), + new MaterialStack(Materials.Oxygen, 4)) + .constructMaterial(); // C10H10O4 + public static Materials Kevlar = new MaterialBuilder(765, TextureSet.SET_DULL, "Kevlar").setName("Kevlar") + .addDustItems() + .addMetalItems() + .addGearItems() + .setRGB(240, 240, 120) + .setColor(Dyes.dyeYellow) + .constructMaterial(); + public static Materials TerephthalicAcid = new MaterialBuilder(764, TextureSet.SET_FLUID, "Terephthalic Acid") + .setName("TerephthalicAcid") + .addCell() + .addFluid() + .setRGB(255, 255, 255) + .setColor(Dyes.dyeWhite) + .setMeltingPoint(480) + .setMaterialList( + new MaterialStack(Materials.Carbon, 8L), + new MaterialStack(Materials.Hydrogen, 6), + new MaterialStack(Materials.Oxygen, 4)) + .constructMaterial(); // C9H6O6 + public static Materials IIIDimethylbenzene = new MaterialBuilder(763, TextureSet.SET_FLUID, "1,3-Dimethylbenzene") + .addCell() + .addFluid() + .setRGB(112, 146, 74) + .setColor(Dyes.dyeLime) + .setMeltingPoint(225) + .setMaterialList(new MaterialStack(Materials.Carbon, 8), new MaterialStack(Materials.Hydrogen, 10)) + .addElectrolyzerRecipe() + .constructMaterial(); // C8H10 + public static Materials IVDimethylbenzene = new MaterialBuilder(762, TextureSet.SET_FLUID, "1,4-Dimethylbenzene") + .addCell() + .addFluid() + .setRGB(122, 136, 84) + .setColor(Dyes.dyeLime) + .setMeltingPoint(286) + .setMaterialList(new MaterialStack(Materials.Carbon, 8), new MaterialStack(Materials.Hydrogen, 10)) + .addElectrolyzerRecipe() + .constructMaterial(); // C8H10 + public static Materials CobaltIINaphthenate = new MaterialBuilder(761, TextureSet.SET_DULL, "Cobalt II Naphthenate") + .setName("Cobalt(II)Naphthenate") + .addDustItems() + .setRGB(143, 95, 39) + .setColor(Dyes.dyeBrown) + .setMeltingPoint(413) + .setMaterialList( + new MaterialStack(Materials.Cobalt, 1), + new MaterialStack(Materials.Carbon, 22), + new MaterialStack(Materials.Hydrogen, 14), + new MaterialStack(Materials.Oxygen, 4)) + .constructMaterial(); // CoC22H14O4 + public static Materials NaphthenicAcid = new MaterialBuilder(760, TextureSet.SET_FLUID, "Naphthenic Acid") + .setName("NaphthenicAcid") + .addCell() + .addFluid() + .setRGB(255, 255, 255) + .setColor(Dyes.dyeWhite) + .setFuelType(MaterialBuilder.SEMIFLUID) + .setFuelPower(80) + .constructMaterial(); + public static Materials CobaltIIHydroxide = new MaterialBuilder(759, TextureSet.SET_POWDER, "Cobalt II Hydroxide") + .setName("CobaltIIHydroxide") + .addDustItems() + .setRGB(229, 140, 239) + .setColor(Dyes.dyePurple) + .setMeltingPoint(441) + .setMaterialList( + new MaterialStack(Materials.Cobalt, 1), + new MaterialStack(Materials.Hydrogen, 2), + new MaterialStack(Materials.Oxygen, 2)) + .constructMaterial(); // CoH2O2 + public static Materials CobaltIIAcetate = new MaterialBuilder(758, TextureSet.SET_POWDER, "Cobalt II Acetate") + .setName("Cobalt(II)Acetate") + .addDustItems() + .setRGB(219, 162, 229) + .setColor(Dyes.dyePurple) + .setMeltingPoint(413) + .setMaterialList( + new MaterialStack(Materials.Carbon, 4L), + new MaterialStack(Materials.Hydrogen, 6), + new MaterialStack(Materials.Cobalt, 1), + new MaterialStack(Materials.Oxygen, 4)) + .constructMaterial(); // C4H6CoO4 + public static Materials CobaltIINitrate = new MaterialBuilder(757, TextureSet.SET_POWDER, "Cobalt II Nitrate") + .setName("Cobalt(II)Nitrate") + .addDustItems() + .setRGB(170, 0, 0) + .setColor(Dyes.dyeRed) + .setMeltingPoint(373) + .setMaterialList( + new MaterialStack(Materials.Cobalt, 1), + new MaterialStack(Materials.Nitrogen, 2), + new MaterialStack(Materials.Oxygen, 6)) + .constructMaterial(); // Co(NO3)2 + public static Materials OrganorhodiumCatalyst = new MaterialBuilder( + 756, + TextureSet.SET_POWDER, + "Organorhodium Catalyst").setName("OrganorhodiumCatalyst") + .addDustItems() + .setRGB(170, 0, 0) + .setColor(Dyes.dyeRed) + .setMeltingPoint(373) + .setMaterialList(new MaterialStack(Materials.Cobalt, 1), new MaterialStack(Materials.NitricAcid, 2)) + .constructMaterial(); // RhHCO(P(C6H5)3)3 + public static Materials SodiumBorohydride = new MaterialBuilder(755, TextureSet.SET_POWDER, "Sodium Borohydride") + .setName("SodiumBorohydride") + .addDustItems() + .setRGB(255, 255, 255) + .setColor(Dyes.dyeWhite) + .setMeltingPoint(673) + .setMaterialList( + new MaterialStack(Materials.Sodium, 1), + new MaterialStack(Materials.Boron, 1), + new MaterialStack(Materials.Hydrogen, 4)) + .constructMaterial(); // NaBH4 + public static Materials RhodiumChloride = new MaterialBuilder(754, TextureSet.SET_POWDER, "Rhodium Chloride") + .setName("RhodiumChloride") + .addDustItems() + .setRGB(128, 0, 0) + .setColor(Dyes.dyeRed) + .setMeltingPoint(723) + .constructMaterial(); // RHCL3 + public static Materials Triphenylphosphene = new MaterialBuilder(753, TextureSet.SET_POWDER, "Triphenylphosphine") + .setName("Triphenylphosphene") + .addDustItems() + .setRGB(255, 255, 255) + .setColor(Dyes.dyeWhite) + .setMeltingPoint(353) + .setMaterialList( + new MaterialStack(Materials.Carbon, 18L), + new MaterialStack(Materials.Hydrogen, 15L), + new MaterialStack(Materials.Phosphorus, 1L)) + .constructMaterial(); // C18H15P + public static Materials PhosphorusTrichloride = new MaterialBuilder( + 752, + TextureSet.SET_FLUID, + "Phosphorus Trichloride").setName("PhosphorusTrichloride") + .addCell() + .addFluid() + .setRGB(255, 255, 255) + .setColor(Dyes.dyeWhite) + .setMeltingPoint(179) + .setMaterialList(new MaterialStack(Materials.Phosphorus, 1L), new MaterialStack(Materials.Chlorine, 3L)) + .constructMaterial(); // PCL3 + public static Materials SodiumHydride = new MaterialBuilder(751, TextureSet.SET_POWDER, "Sodium Hydride") + .setName("SodiumHydride") + .addDustItems() + .setRGB(192, 192, 192) + .setColor(Dyes.dyeLightGray) + .setMeltingPoint(911) + .setMaterialList(new MaterialStack(Materials.Sodium, 1L), new MaterialStack(Materials.Hydrogen, 1L)) + .constructMaterial(); // NaH + public static Materials TrimethylBorate = new MaterialBuilder(750, TextureSet.SET_FLUID, "Trimethyl Borate") + .setName("TrimethylBorate") + .addCell() + .addFluid() + .setRGB(255, 255, 255) + .setColor(Dyes.dyeWhite) + .setMeltingPoint(239) + .setMaterialList( + new MaterialStack(Materials.Carbon, 3L), + new MaterialStack(Materials.Hydrogen, 9L), + new MaterialStack(Materials.Boron, 1L), + new MaterialStack(Materials.Oxygen, 3L)) + .constructMaterial(); // C3H9BO3 + public static Materials SodiumMethoxide = new MaterialBuilder(749, TextureSet.SET_POWDER, "Sodium Methoxide") + .setName("SodiumMethoxide") + .addDustItems() + .setRGB(255, 255, 255) + .setColor(Dyes.dyeWhite) + .setMeltingPoint(400) + .setMaterialList( + new MaterialStack(Materials.Carbon, 1L), + new MaterialStack(Materials.Hydrogen, 3L), + new MaterialStack(Materials.Oxygen, 1L), + new MaterialStack(Materials.Sodium, 1L)) + .constructMaterial(); // CH3NaO + + // H3RhCl6 + + /** + * This method is called by Materials. It can be safely called multiple times. + * It exists to allow Materials ensure this class is initialized. + */ + public static void init() { + // no-op. all work is done by <clinit> + } +} diff --git a/src/main/java/gregtech/api/enums/MaterialsOreAlum.java b/src/main/java/gregtech/api/enums/MaterialsOreAlum.java new file mode 100644 index 0000000000..270d794f94 --- /dev/null +++ b/src/main/java/gregtech/api/enums/MaterialsOreAlum.java @@ -0,0 +1,80 @@ +package gregtech.api.enums; + +public class MaterialsOreAlum { + + public static Materials BauxiteSlurry = new MaterialBuilder(409, TextureSet.SET_FLUID, "Bauxite Slurry") + .setName("BauxiteSlurry") + .addCell() + .addFluid() + .setRGB(37, 67, 168) + .setMeltingPoint(295) + .setColor(Dyes.dyeBlue) + .constructMaterial(); + public static Materials HeatedBauxiteSlurry = new MaterialBuilder( + 410, + TextureSet.SET_FLUID, + "Heated Bauxite Slurry").setName("HeadedBauxiteSlurry") + .addCell() + .addFluid() + .setRGB(55, 92, 212) + .setLiquidTemperature(533) + .setMeltingPoint(295) + .setColor(Dyes.dyeBlue) + .constructMaterial(); + public static Materials SluiceJuice = new MaterialBuilder(411, TextureSet.SET_FLUID, "Sluice Juice") + .setName("SluiceJuice") + .addCell() + .addFluid() + .setRGB(92, 60, 36) + .setLiquidTemperature(295) + .setMeltingPoint(295) + .setColor(Dyes.dyeGray) + .constructMaterial(); + public static Materials SluiceSand = new MaterialBuilder(412, TextureSet.SET_FINE, "Sluice Sand") + .setName("SluiceSand") + .addDustItems() + .setRGB(165, 165, 120) + .setColor(Dyes.dyeGray) + .constructMaterial(); + public static Materials BauxiteSlag = new MaterialBuilder(413, TextureSet.SET_FINE, "Bauxite Slag") + .setName("BauxiteSlag") + .addDustItems() + .setRGB(110, 31, 31) + .setColor(Dyes.dyeRed) + .constructMaterial(); + public static Materials IlmeniteSlag = new MaterialBuilder(414, TextureSet.SET_FINE, "Ilmenite Slag") + .setName("IlmeniteSlag") + .addDustItems() + .setRGB(163, 38, 38) + .setColor(Dyes.dyeBrown) + .constructMaterial(); + public static Materials GreenSapphireJuice = new MaterialBuilder(415, TextureSet.SET_FLUID, "Green Sapphire Juice") + .setName("GreenSapphireJuice") + .addCell() + .addFluid() + .setRGB(100, 200, 130) + .setColor(Dyes.dyeGreen) + .constructMaterial(); + public static Materials SapphireJuice = new MaterialBuilder(416, TextureSet.SET_FLUID, "Sapphire Juice") + .setName("SapphireJuice") + .addCell() + .addFluid() + .setRGB(100, 100, 200) + .setColor(Dyes.dyeBlue) + .constructMaterial(); + public static Materials RubyJuice = new MaterialBuilder(417, TextureSet.SET_FLUID, "Ruby Juice") + .setName("RubyJuice") + .addCell() + .addFluid() + .setRGB(255, 100, 100) + .setColor(Dyes.dyeRed) + .constructMaterial(); + + /** + * called by Materials. Can be safely called multiple times. exists to allow Materials ensure this class is + * initialized + */ + public static void init() { + // no-op. all work is done by <clinit> + } +} diff --git a/src/main/java/gregtech/api/enums/MaterialsUEVplus.java b/src/main/java/gregtech/api/enums/MaterialsUEVplus.java new file mode 100644 index 0000000000..1ff8cc2fb0 --- /dev/null +++ b/src/main/java/gregtech/api/enums/MaterialsUEVplus.java @@ -0,0 +1,600 @@ +package gregtech.api.enums; + +import java.util.Collections; + +public class MaterialsUEVplus { + + public static Materials DimensionallyTranscendentCrudeCatalyst = new Materials( + 748, + TextureSet.SET_FLUID, + 1.0F, + 0, + 2, + 16, + 10, + 20, + 20, + 1, + "DimensionallyTranscendentCrudeCatalyst", + "Dimensionally Transcendent Crude Catalyst", + 0, + 0, + 25_000_000, + 1, + false, + true, + 1, + 1, + 1, + Dyes.dyeCyan).setHasCorrespondingFluid(true); + public static Materials DimensionallyTranscendentProsaicCatalyst = new Materials( + 747, + TextureSet.SET_FLUID, + 1.0F, + 0, + 2, + 16, + 10, + 20, + 20, + 1, + "DimensionallyTranscendentProsaicCatalyst", + "Dimensionally Transcendent Prosaic Catalyst", + 0, + 0, + 50_000_000, + 1, + false, + true, + 1, + 1, + 1, + Dyes.dyeGreen).setHasCorrespondingFluid(true); + public static Materials DimensionallyTranscendentResplendentCatalyst = new Materials( + 746, + TextureSet.SET_FLUID, + 1.0F, + 0, + 2, + 16, + 10, + 20, + 20, + 1, + "DimensionallyTranscendentResplendentCatalyst", + "Dimensionally Transcendent Resplendent Catalyst", + 0, + 0, + 75_000_000, + 1, + false, + true, + 1, + 1, + 1, + Dyes.dyeLime).setHasCorrespondingFluid(true); + public static Materials DimensionallyTranscendentExoticCatalyst = new Materials( + 745, + TextureSet.SET_FLUID, + 1.0F, + 0, + 2, + 16, + 10, + 20, + 20, + 1, + "DimensionallyTranscendentExoticCatalyst", + "Dimensionally Transcendent Exotic Catalyst", + 0, + 0, + 100_000_000, + 1, + false, + true, + 1, + 1, + 1, + Dyes.dyeMagenta).setHasCorrespondingFluid(true); + public static Materials DimensionallyTranscendentStellarCatalyst = new Materials( + 130, + TextureSet.SET_FLUID, + 1.0F, + 0, + 2, + 16, + 10, + 20, + 20, + 1, + "DimensionallyTranscendentStellarCatalyst", + "Dimensionally Transcendent Stellar Catalyst", + 0, + 0, + 100_000_000, + 1, + false, + true, + 1, + 1, + 1, + Dyes.dyeOrange).setHasCorrespondingFluid(true); + + public static Materials ExcitedDTCC = new Materials( + 109, + TextureSet.SET_FLUID, + 1.0F, + 0, + 2, + 16, + 10, + 20, + 20, + 1, + "ExcitedDTCC", + "Excited Dimensionally Transcendent Crude Catalyst", + -1, + -1, + 500000000, + 1, + false, + true, + 1, + 1, + 1, + Dyes.dyeCyan); + public static Materials ExcitedDTPC = new Materials( + 113, + TextureSet.SET_FLUID, + 1.0F, + 0, + 2, + 16, + 35, + 59, + 41, + 1, + "ExcitedDTPC", + "Excited Dimensionally Transcendent Prosaic Catalyst", + -1, + -1, + 500000000, + 1, + false, + true, + 1, + 1, + 1, + Dyes.dyeGreen); + public static Materials ExcitedDTRC = new Materials( + 121, + TextureSet.SET_FLUID, + 1.0F, + 0, + 2, + 16, + 38, + 20, + 56, + 1, + "ExcitedDTRC", + "Excited Dimensionally Transcendent Resplendent Catalyst", + -1, + -1, + 500000000, + 1, + false, + true, + 1, + 1, + 1, + Dyes.dyeLime); + public static Materials ExcitedDTEC = new Materials( + 126, + TextureSet.SET_FLUID, + 1.0F, + 0, + 2, + 16, + 240, + 240, + 41, + 1, + "ExcitedDTEC", + "Excited Dimensionally Transcendent Exotic Catalyst", + -1, + -1, + 500000000, + 1, + false, + true, + 1, + 1, + 1, + Dyes.dyeMagenta); + public static Materials ExcitedDTSC = new Materials( + 127, + TextureSet.SET_FLUID, + 1.0F, + 0, + 2, + 16, + 126, + 75, + 11, + 1, + "ExcitedDTSC", + "Excited Dimensionally Transcendent Stellar Catalyst", + -1, + -1, + 500000000, + 1, + false, + true, + 1, + 1, + 1, + Dyes.dyeOrange); + + public static Materials DimensionallyTranscendentResidue = new Materials( + 589, + TextureSet.SET_FLUID, + 1.0F, + 0, + 2, + 16, + 0, + 0, + 0, + 1, + "DimensionallyTranscendentResidue", + "Dimensionally Transcendent Residue", + -1, + -1, + 25, + 1, + false, + true, + 1, + 1, + 1, + Dyes.dyeBlack); + + public static Materials SpaceTime = new Materials( + 588, + new TextureSet("spacetime", true), + 320.0F, + 4 * 2621440, + 25, + 1 | 2 | 64 | 128, + 255, + 255, + 255, + 0, + "SpaceTime", + "SpaceTime", + -1, + -1, + 0, + 0, + false, + true, + 2, + 1, + 1, + Dyes._NULL, + Collections.singletonList(new TC_Aspects.TC_AspectStack(TC_Aspects.AQUA, 1))) + .setProcessingMaterialTierEU(TierEU.RECIPE_UEV) + .disableAutoGeneratedBlastFurnaceRecipes() + .disableAutoGeneratedVacuumFreezerRecipe(); + public static Materials TranscendentMetal = new Materials( + 581, + TextureSet.SET_METALLIC, + 290.0F, + 3 * 2621440, + 22, + 1 | 2 | 64 | 128, + 50, + 50, + 50, + 0, + "TranscendentMetal", + "Transcendent Metal", + -1, + -1, + 0, + 3000, + true, + true, + 200, + 1000, + 1000, + Dyes.dyeBlack, + Collections.singletonList(new TC_Aspects.TC_AspectStack(TC_Aspects.AQUA, 1))) + .disableAutoGeneratedBlastFurnaceRecipes() + .disableAutoGeneratedVacuumFreezerRecipe() + .setProcessingMaterialTierEU(TierEU.RECIPE_UHV); + public static Materials MagnetohydrodynamicallyConstrainedStarMatter = new Materials( + 583, + new TextureSet("MagnetohydrodynamicallyConstrainedStarMatter", true), + 320.0F, + 4 * 2621440, + 25, + 1 | 2 | 64 | 128, + 255, + 255, + 255, + 0, + "MagnetohydrodynamicallyConstrainedStarMatter", + "Magnetohydrodynamically Constrained Star Matter", + -1, + -1, + 0, + 0, + false, + true, + 2, + 1, + 1, + Dyes._NULL, + Collections.singletonList(new TC_Aspects.TC_AspectStack(TC_Aspects.AQUA, 1))) + .setProcessingMaterialTierEU(TierEU.RECIPE_UIV); + public static Materials RawStarMatter = new Materials( + 584, + TextureSet.SET_FLUID, + 1.0F, + 0, + 2, + 16 | 32, + 100, + 1, + 255, + 255, + "RawStarMatter", + "Condensed Raw Stellar Plasma Mixture", + -1, + -1, + 0, + 0, + false, + false, + 200, + 1, + 1, + Dyes.dyePurple); + public static Materials WhiteDwarfMatter = new Materials( + 585, + new TextureSet("WhiteDwarfMatter", true), + 1.0F, + 0, + 2, + 1 | 2 | 64 | 128, + 255, + 255, + 255, + 0, + "WhiteDwarfMatter", + "White Dwarf Matter", + -1, + -1, + 0, + 0, + false, + false, + 200, + 1, + 1, + Dyes.dyePurple).setHasCorrespondingFluid(true) + .setProcessingMaterialTierEU(TierEU.RECIPE_UEV) + .disableAutoGeneratedBlastFurnaceRecipes() + .disableAutoGeneratedVacuumFreezerRecipe(); + public static Materials BlackDwarfMatter = new Materials( + 586, + TextureSet.SET_METALLIC, + 1.0F, + 0, + 2, + 1 | 2 | 64 | 128, + 0, + 0, + 0, + 255, + "BlackDwarfMatter", + "Black Dwarf Matter", + -1, + -1, + 0, + 0, + false, + false, + 200, + 1, + 1, + Dyes.dyePurple).setHasCorrespondingFluid(true) + .setProcessingMaterialTierEU(TierEU.RECIPE_UEV) + .disableAutoGeneratedBlastFurnaceRecipes() + .disableAutoGeneratedVacuumFreezerRecipe(); + + public static Materials Time = new Materials( + 587, + TextureSet.SET_FLUID, + 1.0F, + 0, + 2, + 16 | 32, + 100, + 1, + 255, + 255, + "temporalFluid", + "Tachyon Rich Temporal Fluid", + -1, + -1, + 0, + 0, + false, + false, + 200, + 1, + 1, + Dyes.dyePurple); + public static Materials Space = new Materials( + 106, + TextureSet.SET_FLUID, + 1.0F, + 0, + 2, + 16 | 32, + 100, + 1, + 255, + 255, + "spatialFluid", + "Spatially Enlarged Fluid", + -1, + -1, + 0, + 0, + false, + false, + 200, + 1, + 1, + Dyes.dyePurple); + + public static Materials Universium = new Materials( + 139, + new TextureSet("universium", true), + 1.0F, + 4 * 2621440, + 25, + 1 | 2 | 64 | 128, + 38, + 49, + 69, + 255, + "Universium", + "Universium", + -1, + -1, + 0, + 0, + false, + true, + 2, + 1, + 1, + Dyes._NULL, + Collections.singletonList(new TC_Aspects.TC_AspectStack(TC_Aspects.AQUA, 1))) + .setProcessingMaterialTierEU(TierEU.RECIPE_UMV); + + public static Materials Eternity = new Materials( + 141, + new TextureSet("eternity", true), + 1.0F, + 8 * 2621440, + 26, + 1 | 2 | 64 | 128, + 255, + 255, + 255, + 0, + "Eternity", + "Eternity", + -1, + -1, + 0, + 14000, + true, + false, + 2, + 1, + 1, + Dyes._NULL, + Collections.singletonList(new TC_Aspects.TC_AspectStack(TC_Aspects.AQUA, 1))) + .disableAutoGeneratedBlastFurnaceRecipes() + .disableAutoGeneratedVacuumFreezerRecipe() + .setProcessingMaterialTierEU(TierEU.RECIPE_UMV); + + public static Materials PrimordialMatter = new Materials( + 142, + TextureSet.SET_FLUID, + 1.0F, + 0, + 2, + 16, + 255, + 255, + 255, + 0, + "PrimordialMatter", + "Liquid Primordial Matter", + -1, + -1, + 2_000_000_000, + 1, + false, + true, + 1, + 1, + 1, + Dyes.dyeBlue); + + public static Materials MagMatter = new Materials( + 143, + new TextureSet("magmatter", true), + 1.0F, + 64 * 2621440, + 26, + 1 | 2 | 64 | 128, + 255, + 255, + 255, + 0, + "Magmatter", + "Magmatter", + -1, + -1, + 0, + 25000, + true, + false, + 2, + 1, + 1, + Dyes._NULL, + Collections.singletonList(new TC_Aspects.TC_AspectStack(TC_Aspects.AQUA, 1))) + .setProcessingMaterialTierEU(TierEU.RECIPE_UMV); + + public static Materials QuarkGluonPlasma = new Materials( + 144, + TextureSet.SET_FLUID, + 1.0F, + 0, + 2, + 16, + 255, + 255, + 255, + 0, + "QuarkGluonPlasma", + "Degenerate Quark Gluon Plasma", + -1, + -1, + 2_000_000_000, + 1, + false, + true, + 1, + 1, + 1, + Dyes._NULL); + + /** + * called by Materials. Can be safely called multiple times. exists to allow Materials ensure this class is + * initialized + */ + public static void init() { + // no-op. all work is done by <clinit> + } +} diff --git a/src/main/java/gregtech/api/enums/MetaTileEntityIDs.java b/src/main/java/gregtech/api/enums/MetaTileEntityIDs.java new file mode 100644 index 0000000000..d32cb781e2 --- /dev/null +++ b/src/main/java/gregtech/api/enums/MetaTileEntityIDs.java @@ -0,0 +1,688 @@ +package gregtech.api.enums; + +/* + * No more magic numbers about TE's IDs. Yay!!! + * The entries are sorted by ID, so if you need to take one, + * please, pretty please, insert it at the correct place. + */ +public enum MetaTileEntityIDs { + + HULL_BRONZE(1), + HULL_BRICKED_BRONZE(2), + HULL_STEEL(3), + HULL_WROUGHT_IRON(4), + HULL_ULV(10), + HULL_LV(11), + HULL_MV(12), + HULL_HV(13), + HULL_EV(14), + HULL_IV(15), + HULL_LuV(16), + HULL_ZPM(17), + HULL_UV(18), + HULL_UHV(19), + transformer_LV_ULV(20), + transformer_MV_LV(21), + transformer_HV_MV(22), + transformer_EV_HV(23), + transformer_IV_EV(24), + transformer_LuV_IV(25), + transformer_ZPM_LuV(26), + transformer_UV_ZPM(27), + transformer_UHV_UV(28), + DYNAMO_HATCH_ULV(30), + DYNAMO_HATCH_LV(31), + DYNAMO_HATCH_MV(32), + DYNAMO_HATCH_HV(33), + DYNAMO_HATCH_EV(34), + DYNAMO_HATCH_IV(35), + DYNAMO_HATCH_LuV(36), + DYNAMO_HATCH_ZPM(37), + DYNAMO_HATCH_UV(38), + DYNAMO_HATCH_UHV(39), + ENERGY_HATCH_ULV(40), + ENERGY_HATCH_LV(41), + ENERGY_HATCH_MV(42), + ENERGY_HATCH_HV(43), + ENERGY_HATCH_EV(44), + ENERGY_HATCH_IV(45), + ENERGY_HATCH_LuV(46), + ENERGY_HATCH_ZPM(47), + ENERGY_HATCH_UV(48), + ENERGY_HATCH_UHV(49), + INPUT_HATCH_ULV(50), + INPUT_HATCH_LV(51), + INPUT_HATCH_MV(52), + INPUT_HATCH_HV(53), + INPUT_HATCH_EV(54), + INPUT_HATCH_IV(55), + INPUT_HATCH_LuV(56), + INPUT_HATCH_ZPM(57), + INPUT_HATCH_UV(58), + INPUT_HATCH_UHV(59), + OUTPUT_HATCH_ULV(60), + OUTPUT_HATCH_LV(61), + OUTPUT_HATCH_MV(62), + OUTPUT_HATCH_HV(63), + OUTPUT_HATCH_EV(64), + OUTPUT_HATCH_IV(65), + OUTPUT_HATCH_LuV(66), + OUTPUT_HATCH_ZPM(67), + OUTPUT_HATCH_UV(68), + OUTPUT_HATCH_UHV(69), + INPUT_BUS_ULV(70), + INPUT_BUS_LV(71), + INPUT_BUS_MV(72), + INPUT_BUS_HV(73), + INPUT_BUS_EV(74), + INPUT_BUS_IV(75), + INPUT_BUS_LuV(76), + INPUT_BUS_ZPM(77), + INPUT_BUS_UV(78), + INPUT_BUS_UHV(79), + OUTPUT_BUS_ULV(80), + OUTPUT_BUS_LV(81), + OUTPUT_BUS_MV(82), + OUTPUT_BUS_HV(83), + OUTPUT_BUS_EV(84), + OUTPUT_BUS_IV(85), + OUTPUT_BUS_LuV(86), + OUTPUT_BUS_ZPM(87), + OUTPUT_BUS_UV(88), + OUTPUT_BUS_UHV(89), + MAINTENANCE_HATCH(90), + MUFFLER_HATCH_LV(91), + MUFFLER_HATCH_MV(92), + MUFFLER_HATCH_HV(93), + MUFFLER_HATCH_EV(94), + MUFFLER_HATCH_IV(95), + MUFFLER_HATCH_LuV(96), + MUFFLER_HATCH_ZPM(97), + MUFFLER_HATCH_UV(98), + MUFFLER_HATCH_UHV(99), + SMALL_COAL_BOILER(100), + HIGH_PRESSURE_COAL_BOILER(101), + HIGH_PRESSURE_LAVA_BOILER(102), + STEAM_FURNACE(103), + HP_STEAM_FURNACE(104), + SIMPLE_SOLAR_BOILER(105), + STEAM_MACERATOR(106), + HP_STEAM_MACERATOR(107), + STEAM_EXTRACTOR(109), + HP_STEAM_EXTRACTOR(110), + AUTO_MAINTENANCE_HATCH(111), + STEAM_FORGE_HAMMER(112), + HP_STEAM_FORGE_HAMMER(113), + HIGH_PRESSURE_SOLAR_BOILER(114), + STEAM_COMPRESSOR(115), + HP_STEAM_COMPRESSOR(116), + STEAM_ALLOY_SMELTER(118), + HP_STEAM_ALLOY_SMELTER(119), + QUANTUM_TANK_LV(120), + QUANTUM_TANK_MV(121), + QUANTUM_TANK_HV(122), + QUANTUM_TANK_EV(123), + QUANTUM_TANK_IV(124), + QUANTUM_CHEST_LV(125), + QUANTUM_CHEST_MV(126), + QUANTUM_CHEST_HV(127), + QUANTUM_CHEST_EV(128), + QUANTUM_CHEST_IV(129), + SUPER_TANK_LV(130), + SUPER_TANK_MV(131), + SUPER_TANK_HV(132), + SUPER_TANK_EV(133), + SUPER_TANK_IV(134), + SUPER_CHEST_LV(135), + SUPER_CHEST_MV(136), + SUPER_CHEST_HV(137), + SUPER_CHEST_EV(138), + SUPER_CHEST_IV(139), + BRICKED_BLAST_FURNACE_CONTROLLER(140), + MULTILOCK_PUMP_MKII_CONTROLLER(141), + MULTILOCK_PUMP_MKIII_CONTROLLER(142), + CONCRETE_BACKFILLER_I_CONTROLLER(143), + CONCRETE_BACKFILLER_II_CONTROLLER(144), + DATA_ACCESS_HATCH(145), + ADVANCED_DATA_ACCESS_HATCH(146), + AUTOMATABLE_DATA_ACCESS_HATCH(147), + MULTIBLOCK_PUMP_INFINITE_CONTROLLER(148), + MULTILOCK_PUMP_MKIV_CONTROLLER(149), + LOCKER_ULV(150), + LOCKER_LV(151), + LOCKER_MV(152), + LOCKER_HV(153), + LOCKER_EV(154), + LOCKER_IV(155), + LOCKER_LuV(156), + LOCKER_ZPM(157), + LOCKER_UV(158), + LOCKER_UHV(159), + BATTERY_BUFFER_1_BY_1_ULV(160), + BATTERY_BUFFER_1_BY_1_LV(161), + BATTERY_BUFFER_1_BY_1_MV(162), + BATTERY_BUFFER_1_BY_1_HV(163), + BATTERY_BUFFER_1_BY_1_EV(164), + BATTERY_BUFFER_1_BY_1_IV(165), + BATTERY_BUFFER_1_BY_1_LuV(166), + BATTERY_BUFFER_1_BY_1_ZPM(167), + BATTERY_BUFFER_1_BY_1_UV(168), + BATTERY_BUFFER_1_BY_1_UHV(169), + BATTERY_BUFFER_2_BY_2_ULV(170), + BATTERY_BUFFER_2_BY_2_LV(171), + BATTERY_BUFFER_2_BY_2_MV(172), + BATTERY_BUFFER_2_BY_2_HV(173), + BATTERY_BUFFER_2_BY_2_EV(174), + BATTERY_BUFFER_2_BY_2_IV(175), + BATTERY_BUFFER_2_BY_2_LuV(176), + BATTERY_BUFFER_2_BY_2_ZPM(177), + BATTERY_BUFFER_2_BY_2_UV(178), + BATTERY_BUFFER_2_BY_2_UHV(179), + BATTERY_BUFFER_3_BY_3_ULV(180), + BATTERY_BUFFER_3_BY_3_LV(181), + BATTERY_BUFFER_3_BY_3_MV(182), + BATTERY_BUFFER_3_BY_3_HV(183), + BATTERY_BUFFER_3_BY_3_EV(184), + BATTERY_BUFFER_3_BY_3_IV(185), + BATTERY_BUFFER_3_BY_3_LuV(186), + BATTERY_BUFFER_3_BY_3_ZPM(187), + BATTERY_BUFFER_3_BY_3_UV(188), + BATTERY_BUFFER_3_BY_3_UHV(189), + BATTERY_BUFFER_4_BY_4_ULV(190), + BATTERY_BUFFER_4_BY_4_LV(191), + BATTERY_BUFFER_4_BY_4_MV(192), + BATTERY_BUFFER_4_BY_4_HV(193), + BATTERY_BUFFER_4_BY_4_EV(194), + BATTERY_BUFFER_4_BY_4_IV(195), + BATTERY_BUFFER_4_BY_4_LuV(196), + BATTERY_BUFFER_4_BY_4_ZPM(197), + BATTERY_BUFFER_4_BY_4_UV(198), + BATTERY_BUFFER_4_BY_4_UHV(199), + QUADRUPLE_INPUT_HATCHES_EV(200), + ALLOY_SMELTER_LV(201), + ALLOY_SMELTER_MV(202), + ALLOY_SMELTER_HV(203), + ALLOY_SMELTER_EV(204), + ALLOY_SMELTER_IV(205), + WIRELESS_HATCH_ENERGY_ULV(206), + WIRELESS_HATCH_ENERGY_LV(207), + WIRELESS_HATCH_ENERGY_MV(208), + WIRELESS_HATCH_ENERGY_HV(209), + ASSEMBLER_LV(211), + ASSEMBLER_MV(212), + ASSEMBLER_HV(213), + ASSEMBLER_EV(214), + ASSEMBLER_IV(215), + WIRELESS_HATCH_ENERGY_EV(216), + WIRELESS_HATCH_ENERGY_IV(217), + WIRELESS_HATCH_ENERGY_LuV(218), + WIRELESS_HATCH_ENERGY_ZPM(219), + BENDING_MACHINE_LV(221), + BENDING_MACHINE_MV(222), + BENDING_MACHINE_HV(223), + BENDING_MACHINE_EV(224), + BENDING_MACHINE_IV(225), + WIRELESS_HATCH_ENERGY_UV(227), + WIRELESS_HATCH_ENERGY_UHV(229), + CANNER_LV(231), + CANNER_MV(232), + CANNER_HV(233), + CANNER_EV(234), + CANNER_IV(235), + COMPRESSOR_LV(241), + COMPRESSOR_MV(242), + COMPRESSOR_HV(243), + COMPRESSOR_EV(244), + COMPRESSOR_IV(245), + CUTTING_MACHINE_LV(251), + CUTTING_MACHINE_MV(252), + CUTTING_MACHINE_HV(253), + CUTTING_MACHINE_EV(254), + CUTTING_MACHINE_IV(255), + ELECTRIC_FURNACE_LV(261), + ELECTRIC_FURNACE_MV(262), + ELECTRIC_FURNACE_HV(263), + ELECTRIC_FURNACE_EV(264), + ELECTRIC_FURNACE_IV(265), + WIRELESS_HATCH_ENERGY_UEV(266), + WIRELESS_HATCH_ENERGY_UIV(267), + WIRELESS_HATCH_ENERGY_UMV(268), + WIRELESS_HATCH_ENERGY_UXV(269), + EXTRACTOR_LV(271), + EXTRACTOR_MV(272), + EXTRACTOR_HV(273), + EXTRACTOR_EV(274), + EXTRACTOR_IV(275), + EXTRUDER_LV(281), + EXTRUDER_MV(282), + EXTRUDER_HV(283), + EXTRUDER_EV(284), + EXTRUDER_IV(285), + WIRELESS_HATCH_ENERGY_MAX(286), + WIRELESS_DYNAMO_ENERGY_HATCH_ULV(287), + WIRELESS_DYNAMO_ENERGY_HATCH_LV(288), + WIRELESS_DYNAMO_ENERGY_HATCH_MV(289), + LATHE_LV(291), + LATHE_MV(292), + LATHE_HV(293), + LATHE_EV(294), + LATHE_IV(295), + WIRELESS_DYNAMO_ENERGY_HATCH_HV(296), + WIRELESS_DYNAMO_ENERGY_HATCH_EV(297), + WIRELESS_DYNAMO_ENERGY_HATCH_IV(298), + WIRELESS_DYNAMO_ENERGY_HATCH_LuV(299), + MACERATOR_LV(301), + MACERATOR_MV(302), + MACERATOR_HV(303), + MACERATOR_EV(304), + MACERATOR_IV(305), + WIRELESS_DYNAMO_ENERGY_HATCH_ZPM(310), + MICROWAVE_OVEN_LV(311), + MICROWAVE_OVEN_MV(312), + MICROWAVE_OVEN_HV(313), + MICROWAVE_OVEN_EV(314), + MICROWAVE_OVEN_IV(315), + WIRELESS_DYNAMO_ENERGY_HATCH_UV(316), + WIRELESS_DYNAMO_ENERGY_HATCH_UHV(317), + WIRELESS_DYNAMO_ENERGY_HATCH_UEV(318), + WIRELESS_DYNAMO_ENERGY_HATCH_UIV(319), + PRINTER_LV(321), + PRINTER_MV(322), + PRINTER_HV(323), + PRINTER_EV(324), + PRINTER_IV(325), + PRINTER_LuV(326), + PRINTER_ZPM(327), + PRINTER_UV(328), + RECYCLER_LV(331), + RECYCLER_MV(332), + RECYCLER_HV(333), + RECYCLER_EV(334), + RECYCLER_IV(335), + SCANNER_LV(341), + SCANNER_MV(342), + SCANNER_HV(343), + SCANNER_EV(344), + SCANNER_IV(345), + WIRELESS_DYNAMO_ENERGY_HATCH_UMV(346), + WIRELESS_DYNAMO_ENERGY_HATCH_UXV(347), + WIRELESS_DYNAMO_ENERGY_HATCH_MAX(348), + ADVANCED_DEBUG_STRUCTURE_WRITTER(349), + WIREMILL_LV(351), + WIREMILL_MV(352), + WIREMILL_HV(353), + WIREMILL_EV(354), + WIREMILL_IV(355), + PCB_FACTORY_CONTROLLER(356), + NANO_FORGE_CONTROLLER(357), + CENTRIFUGE_LV(361), + CENTRIFUGE_MV(362), + CENTRIFUGE_HV(363), + CENTRIFUGE_EV(364), + CENTRIFUGE_IV(365), + ELECTROLYSER_LV(371), + ELECTROLYSER_MV(372), + ELECTROLYSER_HV(373), + ELECTROLYSER_EV(374), + ELECTROLYSER_IV(375), + THERMAL_CENTRIFUGE_LV(381), + THERMAL_CENTRIFUGE_MV(382), + THERMAL_CENTRIFUGE_HV(383), + THERMAL_CENTRIFUGE_EV(384), + THERMAL_CENTRIFUGE_IV(385), + ORE_WASHER_LV(391), + ORE_WASHER_MV(392), + ORE_WASHER_HV(393), + ORE_WASHER_EV(394), + ORE_WASHER_IV(395), + PACKAGER_LV(401), + PACKAGER_MV(402), + PACKAGER_HV(403), + PACKAGER_EV(404), + PACKAGER_IV(405), + PACKAGER_LuV(406), + PACKAGER_ZPM(407), + PACKAGER_UV(408), + UNPACKAGER_LV(411), + UNPACKAGER_MV(412), + UNPACKAGER_HV(413), + UNPACKAGER_EV(414), + UNPACKAGER_IV(415), + UNPACKAGER_LuV(416), + UNPACKAGER_ZPM(417), + UNPACKAGER_UV(418), + CHEMICAL_REACTOR_LV(421), + CHEMICAL_REACTOR_MV(422), + CHEMICAL_REACTOR_HV(423), + CHEMICAL_REACTOR_EV(424), + CHEMICAL_REACTOR_IV(425), + FLUID_CANNER_LV(431), + FLUID_CANNER_MV(432), + FLUID_CANNER_HV(433), + FLUID_CANNER_EV(434), + FLUID_CANNER_IV(435), + ROCK_BREAKER_LV(441), + ROCK_BREAKER_MV(442), + ROCK_BREAKER_HV(443), + ROCK_BREAKER_EV(444), + ROCK_BREAKER_IV(445), + MASS_FABRICATOR_LV(461), + MASS_FABRICATOR_MV(462), + MASS_FABRICATOR_HV(463), + MASS_FABRICATOR_EV(464), + MASS_FABRICATOR_IV(465), + MATTER_AMPLIFIER_LV(471), + MATTER_AMPLIFIER_MV(472), + MATTER_AMPLIFIER_HV(473), + MATTER_AMPLIFIER_EV(474), + MATTER_AMPLIFIER_IV(475), + REPLICATOR_LV(481), + REPLICATOR_MV(482), + REPLICATOR_HV(483), + REPLICATOR_EV(484), + REPLICATOR_IV(485), + BREWERY_LV(491), + BREWERY_MV(492), + BREWERY_HV(493), + BREWERY_EV(494), + BREWERY_IV(495), + FERMENTER_LV(501), + FERMENTER_MV(502), + FERMENTER_HV(503), + FERMENTER_EV(504), + FERMENTER_IV(505), + FLUID_EXTRACTOR_LV(511), + FLUID_EXTRACTOR_MV(512), + FLUID_EXTRACTOR_HV(513), + FLUID_EXTRACTOR_EV(514), + FLUID_EXTRACTOR_IV(515), + FLUID_SOLIDIFIER_LV(521), + FLUID_SOLIDIFIER_MV(522), + FLUID_SOLIDIFIER_HV(523), + FLUID_SOLIDIFIER_EV(524), + FLUID_SOLIDIFIER_IV(525), + DISTILLERY_LV(531), + DISTILLERY_MV(532), + DISTILLERY_HV(533), + DISTILLERY_EV(534), + DISTILLERY_IV(535), + CHEMICAL_BATH_LV(541), + CHEMICAL_BATH_MV(542), + CHEMICAL_BATH_HV(543), + CHEMICAL_BATH_EV(544), + CHEMICAL_BATH_IV(545), + POLARIZER_LV(551), + POLARIZER_MV(552), + POLARIZER_HV(553), + POLARIZER_EV(554), + POLARIZER_IV(555), + ELECTROMAGNETIC_SEPARATOR_LV(561), + ELECTROMAGNETIC_SEPARATOR_MV(562), + ELECTROMAGNETIC_SEPARATOR_HV(563), + ELECTROMAGNETIC_SEPARATOR_EV(564), + ELECTROMAGNETIC_SEPARATOR_IV(565), + AUTOCLAVE_LV(571), + AUTOCLAVE_MV(572), + AUTOCLAVE_HV(573), + AUTOCLAVE_EV(574), + AUTOCLAVE_IV(575), + MIXER_LV(581), + MIXER_MV(582), + MIXER_HV(583), + MIXER_EV(584), + MIXER_IV(585), + LASER_ENGRAVER_LV(591), + LASER_ENGRAVER_MV(592), + LASER_ENGRAVER_HV(593), + LASER_ENGRAVER_EV(594), + LASER_ENGRAVER_IV(595), + FORMING_PRESS_LV(601), + FORMING_PRESS_MV(602), + FORMING_PRESS_HV(603), + FORMING_PRESS_EV(604), + FORMING_PRESS_IV(605), + FORGE_HAMMER_LV(611), + FORGE_HAMMER_MV(612), + FORGE_HAMMER_HV(613), + FORGE_HAMMER_EV(614), + FORGE_HAMMER_IV(615), + FLUID_HEATER_LV(621), + FLUID_HEATER_MV(622), + FLUID_HEATER_HV(623), + FLUID_HEATER_EV(624), + FLUID_HEATER_IV(625), + SLICER_LV(631), + SLICER_MV(632), + SLICER_HV(633), + SLICER_EV(634), + SLICER_IV(635), + SIFTER_LV(641), + SIFTER_MV(642), + SIFTER_HV(643), + SIFTER_EV(644), + SIFTER_IV(645), + ARC_FURNACE_LV(651), + ARC_FURNACE_MV(652), + ARC_FURNACE_HV(653), + ARC_FURNACE_EV(654), + ARC_FURNACE_IV(655), + PLASMA_ARC_FURNACE_LV(661), + PLASMA_ARC_FURNACE_MV(662), + PLASMA_ARC_FURNACE_HV(663), + PLASMA_ARC_FURNACE_EV(664), + PLASMA_ARC_FURNACE_IV(665), + OVEN_LV(671), + OVEN_MV(672), + OVEN_HV(673), + OVEN_EV(674), + OVEN_IV(675), + MINER_LV(679), + MINER_MV(680), + MINER_HV(681), + BATTERY_CHARGER_4_BY_4_ULV(690), + BATTERY_CHARGER_4_BY_4_LV(691), + BATTERY_CHARGER_4_BY_4_MV(692), + BATTERY_CHARGER_4_BY_4_HV(693), + BATTERY_CHARGER_4_BY_4_EV(694), + BATTERY_CHARGER_4_BY_4_IV(695), + BATTERY_CHARGER_4_BY_4_LuV(696), + BATTERY_CHARGER_4_BY_4_ZPM(697), + BATTERY_CHARGER_4_BY_4_UV(698), + BATTERY_CHARGER_4_BY_4_UHV(699), + QUADRUPLE_INPUT_HATCHES_IV(710), + QUADRUPLE_INPUT_HATCHES_LuV(711), + QUADRUPLE_INPUT_HATCHES_ZPM(712), + QUADRUPLE_INPUT_HATCHES_UV(713), + QUADRUPLE_INPUT_HATCHES_UHV(714), + QUADRUPLE_INPUT_HATCHES_UEV(715), + QUADRUPLE_INPUT_HATCHES_UIV(716), + QUADRUPLE_INPUT_HATCHES_UMV(717), + QUADRUPLE_INPUT_HATCHES_UXV(718), + QUADRUPLE_INPUT_HATCHES_MAX(719), + EBF_CONTROLLER(1000), + IMPLOSION_COMPRESSOR_CONTROLLER(1001), + VACUUM_FREEZER_CONTROLLER(1002), + MULTI_SMELTER_CONTROLLER(1003), + DTPF_CONTROLLER(1004), + LARGE_ADVANCED_GAS_TURBINE_CONTROLLER(1005), + TRANSCENDENT_PLASMA_MIXER_CONTROLLER(1006), + LARGE_BRONZE_BOILER_CONTROLLER(1020), + LARGE_STEEL_BOILER_CONTROLLER(1021), + LARGE_TITANIUM_BOILER_CONTROLLER(1022), + LARGE_TUNGSTENSTEEL_BOILER_CONTROLLER(1023), + COMBUSTION_GENERATOR_LV(1110), + COMBUSTION_GENERATOR_MV(1111), + COMBUSTION_GENERATOR_HV(1112), + GAS_TURBINE_LV(1115), + GAS_TURBINE_MV(1116), + GAS_TURBINE_HV(1117), + GAS_TURBINE_EV(1118), + GAS_TURBINE_IV(1119), + STEAM_TURBINE_LV(1120), + STEAM_TURBINE_MV(1121), + STEAM_TURBINE_HV(1122), + MAGIC_ENERGY_CONVERTER_LV(1123), + MAGIC_ENERGY_CONVERTER_MV(1124), + MAGIC_ENERGY_CONVERTER_HV(1125), + DISTILLATION_TOWER_CONTROLLER(1126), + MAGIC_ENERGY_ABSORBER_LV(1127), + MAGIC_ENERGY_ABSORBER_MV(1128), + MAGIC_ENERGY_ABSORBER_HV(1129), + MAGIC_ENERGY_ABSORBER_EV(1130), + LARGE_STEAM_TURBINE_CONTROLLER(1131), + INTEGRATED_ORE_FACTORY_CONTROLLER(1132), + MONSTER_REPELLATOR_LuV(1135), + MONSTER_REPELLATOR_ZPM(1136), + MONSTER_REPELLATOR_UV(1137), + PUMP_LV(1140), + PUMP_MV(1141), + PUMP_HV(1142), + PUMP_EV(1143), + PUMP_IV(1144), + TELEPORTER(1145), + MONSTER_REPELLATOR_LV(1146), + MONSTER_REPELLATOR_MV(1147), + MONSTER_REPELLATOR_HV(1148), + MONSTER_REPELLATOR_EV(1149), + MONSTER_REPELLATOR_IV(1150), + LARGE_GAS_TURBINE_CONTROLLER(1151), + LARGE_HP_STEAM_TURBINE_CONTROLLER(1152), + LARGE_PLASMA_TURBINE_CONTROLLER(1153), + LARGE_HEAT_EXCHANGER_CONTROLLER(1154), + CHARCOAL_PILE_IGNITER_CONTROLLER(1155), + MULTIBLOCK_PUMP_MKI_CONTROLLER(1157), + ORE_DRILL_MKI_CONTROLLER(1158), + PYROLYSE_OVEN_CONTROLLER(1159), + OIL_CRACKER_CONTROLLER(1160), + MICROWAVE_ENERGY_TRANSMITTER_HV(1161), + MICROWAVE_ENERGY_TRANSMITTER_EV(1162), + MICROWAVE_ENERGY_TRANSMITTER_IV(1163), + MICROWAVE_ENERGY_TRANSMITTER_LuV(1164), + MICROWAVE_ENERGY_TRANSMITTER_ZPM(1165), + MICROWAVE_ENERGY_TRANSMITTER_UV(1166), + LCR_CONTROLLER(1169), + ASSEMBLING_LINE_CONTROLLER(1170), + COMBUSTION_ENGINE_CONTROLLER(1171), + CLEANROOM_CONTROLLER(1172), + ADVANCED_SEISMIC_PROSPECTOR_EV(1173), + LIGHTNING_ROD_HV(1174), + LIGHTNING_ROD_EV(1175), + LIGHTNING_ROD_IV(1176), + ORE_DRILL_MKII_CONTROLLER(1177), + ORE_DRILL_MKIII_CONTROLLER(1178), + ORE_DRILL_MKIV_CONTROLLER(1179), + CIRCUIT_ASSEMBLER_LV(1180), + CIRCUIT_ASSEMBLER_MV(1181), + CIRCUIT_ASSEMBLER_HV(1182), + CIRCUIT_ASSEMBLER_EV(1183), + CIRCUIT_ASSEMBLER_IV(1184), + CIRCUIT_ASSEMBLER_LuV(1185), + CIRCUIT_ASSEMBLER_ZPM(1186), + CIRCUIT_ASSEMBLER_UV(1187), + NAQUADAH_REACTOR_ZPM(1188), + NAQUADAH_REACTOR_UV(1189), + NAQUADAH_REACTOR_EV(1190), + NAQUADAH_REACTOR_IV(1191), + NAQUADAH_REACTOR_LuV(1192), + FUSION_CONTROLLER_MKI(1193), + FUSION_CONTROLLER_MKII(1194), + FUSION_CONTROLLER_MKIII(1195), + PLASMA_GENERATOR_IV(1196), + PLASMA_GENERATOR_LuV(1197), + PLASMA_GENERATOR_ZPM(1198), + PROCESSING_ARRAY_CONTROLLER(1199), + ADVANCED_SEISMIC_PROSPECTOR_LV(2102), + ADVANCED_SEISMIC_PROSPECTOR_MV(2103), + ADVANCED_SEISMIC_PROSPECTOR_HV(2104), + EXTREME_COMBUSTION_ENGINE_CONTROLLER(2105), + LONG_DISTANCE_PIPELINE_FLUID(2700), + LONG_DISTANCE_PIPELINE_ITEM(2701), + OUTPUT_BUS_ME(2710), + INPUT_BUS_ME_ADVANCED(2711), + INPUT_HATCH_ME_ADVANCED(2712), + OUTPUT_HATCH_ME(2713), + CRAFTING_INPUT_ME(2714), + CRAFTING_INPUT_ME_BUS(2715), + CRAFTING_INPUT_SLAVE(2716), + INPUT_HATCH_ME(2717), + INPUT_BUS_ME(2718), + CHEST_BUFFER_ULV(9230), + CHEST_BUFFER_LV(9231), + CHEST_BUFFER_MV(9232), + CHEST_BUFFER_HV(9233), + CHEST_BUFFER_EV(9234), + CHEST_BUFFER_IV(9235), + CHEST_BUFFER_LuV(9236), + CHEST_BUFFER_ZPM(9237), + CHEST_BUFFER_UV(9238), + CHEST_BUFFER_UHV(9239), + ITEM_FILTER_ULV(9240), + ITEM_FILTER_LV(9241), + ITEM_FILTER_MV(9242), + ITEM_FILTER_HV(9243), + ITEM_FILTER_EV(9244), + ITEM_FILTER_IV(9245), + ITEM_FILTER_LuV(9246), + ITEM_FILTER_ZPM(9247), + ITEM_FILTER_UV(9248), + ITEM_FILTER_UHV(9249), + TYPE_FILTER_ULV(9250), + TYPE_FILTER_LV(9251), + TYPE_FILTER_MV(9252), + TYPE_FILTER_HV(9253), + TYPE_FILTER_EV(9254), + TYPE_FILTER_IV(9255), + TYPE_FILTER_LuV(9256), + TYPE_FILTER_ZPM(9257), + TYPE_FILTER_UV(9258), + TYPE_FILTER_UHV(9259), + VOLTAGE_REGULATOR_ULV(9270), + VOLTAGE_REGULATOR_LV(9271), + VOLTAGE_REGULATOR_MV(9272), + VOLTAGE_REGULATOR_HV(9273), + VOLTAGE_REGULATOR_EV(9274), + VOLTAGE_REGULATOR_IV(9275), + VOLTAGE_REGULATOR_LuV(9276), + VOLTAGE_REGULATOR_ZPM(9277), + VOLTAGE_REGULATOR_UV(9278), + VOLTAGE_REGULATOR_UHV(9279), + SUPER_BUFFER_ULV(9300), + SUPER_BUFFER_LV(9301), + SUPER_BUFFER_MV(9302), + SUPER_BUFFER_HV(9303), + SUPER_BUFFER_EV(9304), + SUPER_BUFFER_IV(9305), + SUPER_BUFFER_LuV(9306), + SUPER_BUFFER_ZPM(9307), + SUPER_BUFFER_UV(9308), + SUPER_BUFFER_UHV(9309), + ITEM_DISTRIBUTOR_ULV(9320), + ITEM_DISTRIBUTOR_LV(9321), + ITEM_DISTRIBUTOR_MV(9322), + ITEM_DISTRIBUTOR_HV(9323), + ITEM_DISTRIBUTOR_EV(9324), + ITEM_DISTRIBUTOR_IV(9325), + ITEM_DISTRIBUTOR_LuV(9326), + ITEM_DISTRIBUTOR_ZPM(9327), + ITEM_DISTRIBUTOR_UV(9328), + ITEM_DISTRIBUTOR_UHV(9329), + RECIPE_FILTER_ULV(9330), + RECIPE_FILTER_LV(9331), + RECIPE_FILTER_MV(9332), + RECIPE_FILTER_HV(9333), + RECIPE_FILTER_EV(9334), + RECIPE_FILTER_IV(9335), + RECIPE_FILTER_LuV(9336), + RECIPE_FILTER_ZPM(9337), + RECIPE_FILTER_UV(9338), + RECIPE_FILTER_UHV(9339), + INDUSTRIAL_APIARY(9399), + Drone_Centre(9400), + DroneDownLink(9401); + + public final int ID; + + private MetaTileEntityIDs(int ID) { + this.ID = ID; + } +} diff --git a/src/main/java/gregtech/api/enums/Mods.java b/src/main/java/gregtech/api/enums/Mods.java new file mode 100644 index 0000000000..ee95a98874 --- /dev/null +++ b/src/main/java/gregtech/api/enums/Mods.java @@ -0,0 +1,387 @@ +package gregtech.api.enums; + +import java.util.Locale; + +import net.minecraft.util.ResourceLocation; + +import cpw.mods.fml.common.Loader; + +public enum Mods { + + AE2FluidCraft(Names.A_E2_FLUID_CRAFT), + AE2Stuff(Names.AE2STUFF), + AE2WCT(Names.AE2WCT), + AFSU(Names.A_F_S_U), + AdvancedSolarPanel(Names.ADVANCED_SOLAR_PANEL), + AdventureBackpack(Names.ADVENTURE_BACKPACK), + AppleCore(Names.APPLE_CORE), + AppliedEnergistics2(Names.APPLIED_ENERGISTICS2), + ArchitectureCraft(Names.ARCHITECTURE_CRAFT), + Aroma1997Core(Names.AROMA1997_CORE), + Automagy(Names.AUTOMAGY), + Avaritia(Names.AVARITIA), + AvaritiaAddons(Names.AVARITIA_ADDONS), + Backpack(Names.BACKPACK), + BartWorks(Names.BART_WORKS), + Baubles(Names.BAUBLES), + BetterBuildersWands(Names.BETTER_BUILDERS_WANDS), + BetterLoadingScreen(Names.BETTER_LOADING_SCREEN), + BetterQuesting(Names.BETTER_QUESTING), + BiblioCraft(Names.BIBLIO_CRAFT), + BiblioWoodsBoPEdition(Names.BIBLIO_WOODS_BO_P_EDITION), + BiblioWoodsForestryEdition(Names.BIBLIO_WOODS_FORESTRY_EDITION), + BiblioWoodsNaturaEdition(Names.BIBLIO_WOODS_NATURA_EDITION), + BinnieCore(Names.BINNIE_CORE), + BiomesOPlenty(Names.BIOMES_O_PLENTY), + BloodArsenal(Names.BLOOD_ARSENAL), + BloodMagic(Names.BLOOD_MAGIC), + Botania(Names.BOTANIA), + Botany(Names.BOTANY), + BuildCraftBuilders(Names.BUILD_CRAFT_BUILDERS), + BuildCraftCompat(Names.BUILD_CRAFT_COMPAT), + BuildCraftCore(Names.BUILD_CRAFT_CORE), + BuildCraftFactory(Names.BUILD_CRAFT_FACTORY), + BuildCraftRobotics(Names.BUILD_CRAFT_ROBOTICS), + BuildCraftSilicon(Names.BUILD_CRAFT_SILICON), + BuildCraftTransport(Names.BUILD_CRAFT_TRANSPORT), + COFHCore(Names.C_O_F_H_CORE), + CarpentersBlocks(Names.CARPENTERS_BLOCKS), + CatWalks(Names.CAT_WALKS), + Chisel(Names.CHISEL), + CompactKineticGenerators(Names.COMPACT_KINETIC_GENERATORS), + Computronics(Names.COMPUTRONICS), + CraftTweaker(Names.CRAFT_TWEAKER), + CropLoadCore(Names.CROP_LOAD_CORE), + CropsPlusPlus(Names.CROPS_PLUS_PLUS), + DraconicEvolution(Names.DRACONIC_EVOLUTION), + ElectroMagicTools(Names.ELECTRO_MAGIC_TOOLS), + EnderIO(Names.ENDER_I_O), + EnderStorage(Names.ENDER_STORAGE), + EnderZoo(Names.ENDER_ZOO), + EnhancedLootBags(Names.ENHANCED_LOOT_BAGS), + EternalSingularity(Names.ETERNAL_SINGULARITY), + ExtraBees(Names.EXTRA_BEES), + ExtraCells2(Names.EXTRA_CELLS2), + ExtraTrees(Names.EXTRA_TREES), + ExtraUtilities(Names.EXTRA_UTILITIES), + FloodLights(Names.FLOOD_LIGHTS), + ForbiddenMagic(Names.FORBIDDEN_MAGIC), + Forestry(Names.FORESTRY), + ForgeMicroblocks(Names.FORGE_MICROBLOCKS), + ForgeRelocation(Names.FORGE_RELOCATION), + GTNHIntergalactic(Names.G_T_N_H_INTERGALACTIC), + GTNHLanthanides(Names.G_T_N_H_LANTHANIDES), + GTPlusPlus(Names.G_T_PLUS_PLUS), + GTPlusPlusEverglades(Names.G_T_PLUS_PLUS_EVERGLADES), + Gadomancy(Names.GADOMANCY), + GalactiGreg(Names.GALACTI_GREG), + GalacticraftAmunRa(Names.GALACTICRAFT_AMUN_RA), + GalacticraftCore(Names.GALACTICRAFT_CORE), + GalacticraftMars(Names.GALACTICRAFT_MARS), + GalaxySpace(Names.GALAXY_SPACE), + Gendustry(Names.GENDUSTRY), + Genetics(Names.GENETICS), + GoodGenerator(Names.GOOD_GENERATOR), + GraviSuite(Names.GRAVI_SUITE), + GraviSuiteNEO(Names.GRAVI_SUITE_NEO), + GregTech(Names.GREG_TECH), + HardcoreEnderExpansion(Names.HARDCORE_ENDER_EXPANSION), + HodgePodge(Names.HODGE_PODGE), + HoloInventory(Names.HOLO_INVENTORY), + IC2CropPlugin(Names.I_C2_CROP_PLUGIN), + IC2NuclearControl(Names.I_C2_NUCLEAR_CONTROL), + IguanaTweaksTinkerConstruct(Names.IGUANA_TWEAKS_TINKER_CONSTRUCT), + IndustrialCraft2(Names.INDUSTRIAL_CRAFT2), + IronChests(Names.IRON_CHESTS), + IronChestsMinecarts(Names.IRON_CHESTS_MINECARTS), + IronTanks(Names.IRON_TANKS), + JABBA(Names.J_A_B_B_A), + KekzTech(Names.KEKZ_TECH), + KubaTech(Names.KUBA_TECH), + LogisticsPipes(Names.LOGISTICS_PIPES), + MCFrames(Names.MC_FRAMES), + MagicBees(Names.MAGIC_BEES), + MalisisDoors(Names.MALISIS_DOORS), + Mantle(Names.MANTLE), + MineAndBladeBattleGear2(Names.MINE_AND_BLADE_BATTLE_GEAR2), + Minecraft(Names.MINECRAFT), + NEICustomDiagrams(Names.N_E_I_CUSTOM_DIAGRAMS), + NEIOrePlugin(Names.N_E_I_ORE_PLUGIN), + Natura(Names.NATURA), + NaturesCompass(Names.NATURES_COMPASS), + NewHorizonsCoreMod(Names.NEW_HORIZONS_CORE_MOD), + NotEnoughItems(Names.NOT_ENOUGH_ITEMS), + OpenBlocks(Names.OPEN_BLOCKS), + OpenComputers(Names.OPEN_COMPUTERS), + OpenGlasses(Names.OPEN_GLASSES), + OpenModularTurrets(Names.OPEN_MODULAR_TURRETS), + OpenPrinters(Names.OPEN_PRINTERS), + OpenSecurity(Names.OPEN_SECURITY), + PamsHarvestCraft(Names.PAMS_HARVEST_CRAFT), + PamsHarvestTheNether(Names.PAMS_HARVEST_THE_NETHER), + PlayerAPI(Names.PLAYER_API), + ProjectBlue(Names.PROJECT_BLUE), + ProjectRedCore(Names.PROJECT_RED_CORE), + ProjectRedExpansion(Names.PROJECT_RED_EXPANSION), + ProjectRedExploration(Names.PROJECT_RED_EXPLORATION), + ProjectRedFabrication(Names.PROJECT_RED_FABRICATION), + ProjectRedIllumination(Names.PROJECT_RED_ILLUMINATION), + ProjectRedIntegration(Names.PROJECT_RED_INTEGRATION), + ProjectRedTransmission(Names.PROJECT_RED_TRANSMISSION), + ProjectRedTransportation(Names.PROJECT_RED_TRANSPORTATION), + QuestBook(Names.QUEST_BOOK), + RWG(Names.RWG), + Railcraft(Names.RAILCRAFT), + RandomThings(Names.RANDOM_THINGS), + RemoteIO(Names.REMOTE_IO), + SGCraft(Names.S_G_CRAFT), + SleepingBags(Names.SLEEPING_BAGS), + SpiceOfLife(Names.SPICE_OF_LIFE), + StevesAddons(Names.STEVES_ADDONS), + StevesCarts2(Names.STEVES_CARTS2), + StevesFactoryManager(Names.STEVES_FACTORY_MANAGER), + StorageDrawers(Names.STORAGE_DRAWERS), + StructureLib(Names.STRUCTURE_LIB), + SuperSolarPanels(Names.SUPER_SOLAR_PANELS), + TaintedMagic(Names.TAINTED_MAGIC), + TecTech(Names.TECTECH), + Thaumcraft(Names.THAUMCRAFT), + ThaumicBases(Names.THAUMIC_BASES), + ThaumicBoots(Names.THAUMIC_BOOTS), + ThaumicEnergistics(Names.THAUMIC_ENERGISTICS), + ThaumicExploration(Names.THAUMIC_EXPLORATION), + ThaumicHorizons(Names.THAUMIC_HORIZONS), + ThaumicMachina(Names.THAUMIC_MACHINA), + ThaumicTinkerer(Names.THAUMIC_TINKERER), + TinkerConstruct(Names.TINKER_CONSTRUCT), + TinkersDefence(Names.TINKERS_DEFENCE), + TinkersGregworks(Names.TINKERS_GREGWORKS), + TinkersMechworks(Names.TINKERS_MECHWORKS), + Translocator(Names.TRANSLOCATOR), + TravellersGear(Names.TRAVELLERS_GEAR), + TwilightForest(Names.TWILIGHT_FOREST), + UniversalSingularities(Names.UNIVERSAL_SINGULARITIES), + Waila(Names.WAILA), + WarpTheory(Names.WARP_THEORY), + WirelessRedstoneCBEAddons(Names.WIRELESS_REDSTONE_CBE_ADDONS), + WirelessRedstoneCBECore(Names.WIRELESS_REDSTONE_CBE_CORE), + WirelessRedstoneCBELogic(Names.WIRELESS_REDSTONE_CBE_LOGIC), + Witchery(Names.WITCHERY), + WitchingGadgets(Names.WITCHING_GADGETS), + ZTones(Names.Z_TONES), + + // Do we keep compat of those? + ArsMagica2(Names.ARS_MAGICA2), + GanysSurface(Names.GANYS_SURFACE), + IndustrialCraft2Classic(Names.INDUSTRIAL_CRAFT2_CLASSIC), + MagicalCrops(Names.MAGICAL_CROPS), + Metallurgy(Names.METALLURGY), + RotaryCraft(Names.ROTARY_CRAFT), + ThermalExpansion(Names.THERMAL_EXPANSION), + ThermalFondation(Names.THERMAL_FONDATION), + UndergroundBiomes(Names.UNDERGROUND_BIOMES), + + ; + + public static class Names { + + public static final String A_E2_FLUID_CRAFT = "ae2fc"; + public static final String AE2STUFF = "ae2stuff"; + public static final String AE2WCT = "ae2wct"; + public static final String A_F_S_U = "AFSU"; + public static final String ADVANCED_SOLAR_PANEL = "AdvancedSolarPanel"; + public static final String ADVENTURE_BACKPACK = "adventurebackpack"; + public static final String APPLE_CORE = "AppleCore"; + public static final String APPLIED_ENERGISTICS2 = "appliedenergistics2"; + public static final String ARCHITECTURE_CRAFT = "ArchitectureCraft"; + public static final String AROMA1997_CORE = "Aroma1997Core"; + public static final String AUTOMAGY = "Automagy"; + public static final String AVARITIA = "Avaritia"; + public static final String AVARITIA_ADDONS = "avaritiaddons"; + public static final String BACKPACK = "Backpack"; + public static final String BART_WORKS = "bartworks"; + public static final String BAUBLES = "Baubles"; + public static final String BETTER_BUILDERS_WANDS = "betterbuilderswands"; + public static final String BETTER_LOADING_SCREEN = "betterloadingscreen"; + public static final String BETTER_QUESTING = "betterquesting"; + public static final String BIBLIO_CRAFT = "BiblioCraft"; + public static final String BIBLIO_WOODS_BO_P_EDITION = "BiblioWoodsBoP"; + public static final String BIBLIO_WOODS_FORESTRY_EDITION = "BiblioWoodsForestry"; + public static final String BIBLIO_WOODS_NATURA_EDITION = "BiblioWoodsNatura"; + public static final String BINNIE_CORE = "BinnieCore"; + public static final String BIOMES_O_PLENTY = "BiomesOPlenty"; + public static final String BLOOD_ARSENAL = "BloodArsenal"; + public static final String BLOOD_MAGIC = "AWWayofTime"; + public static final String BOTANIA = "Botania"; + public static final String BOTANY = "Botany"; + public static final String BUILD_CRAFT_BUILDERS = "BuildCraft|Builders"; + public static final String BUILD_CRAFT_COMPAT = "BuildCraft|Compat"; + public static final String BUILD_CRAFT_CORE = "BuildCraft|Core"; + public static final String BUILD_CRAFT_FACTORY = "BuildCraft|Factory"; + public static final String BUILD_CRAFT_ROBOTICS = "BuildCraft|Robotics"; + public static final String BUILD_CRAFT_SILICON = "BuildCraft|Silicon"; + public static final String BUILD_CRAFT_TRANSPORT = "BuildCraft|Transport"; + public static final String C_O_F_H_CORE = "CoFHCore"; + public static final String CARPENTERS_BLOCKS = "CarpentersBlocks"; + public static final String CAT_WALKS = "catwalks"; + public static final String CHISEL = "chisel"; + public static final String COMPACT_KINETIC_GENERATORS = "compactkineticgenerators"; + public static final String COMPUTRONICS = "computronics"; + public static final String CRAFT_TWEAKER = "MineTweaker3"; + public static final String CROP_LOAD_CORE = "croploadcore"; + public static final String CROPS_PLUS_PLUS = "berriespp"; + public static final String DRACONIC_EVOLUTION = "DraconicEvolution"; + public static final String ELECTRO_MAGIC_TOOLS = "EMT"; + public static final String ENDER_I_O = "EnderIO"; + public static final String ENDER_STORAGE = "EnderStorage"; + public static final String ENDER_ZOO = "EnderZoo"; + public static final String ENHANCED_LOOT_BAGS = "enhancedlootbags"; + public static final String ETERNAL_SINGULARITY = "eternalsingularity"; + public static final String EXTRA_BEES = "ExtraBees"; + public static final String EXTRA_CELLS2 = "extracells"; + public static final String EXTRA_TREES = "ExtraTrees"; + public static final String EXTRA_UTILITIES = "ExtraUtilities"; + public static final String FLOOD_LIGHTS = "FloodLights"; + public static final String FORBIDDEN_MAGIC = "ForbiddenMagic"; + public static final String FORESTRY = "Forestry"; + public static final String FORGE_MICROBLOCKS = "ForgeMicroblock"; + public static final String FORGE_RELOCATION = "ForgeRelocation"; + public static final String G_T_N_H_INTERGALACTIC = "gtnhintergalactic"; + public static final String G_T_N_H_LANTHANIDES = "gtnhlanth"; + public static final String G_T_PLUS_PLUS = "miscutils"; + public static final String G_T_PLUS_PLUS_EVERGLADES = "ToxicEverglades"; + public static final String GADOMANCY = "gadomancy"; + public static final String GALACTI_GREG = "galacticgreg"; + public static final String GALACTICRAFT_AMUN_RA = "GalacticraftAmunRa"; + public static final String GALACTICRAFT_CORE = "GalacticraftCore"; + public static final String GALACTICRAFT_MARS = "GalacticraftMars"; + public static final String GALAXY_SPACE = "GalaxySpace"; + public static final String GENDUSTRY = "gendustry"; + public static final String GENETICS = "Genetics"; + public static final String GOOD_GENERATOR = "GoodGenerator"; + public static final String GRAVI_SUITE = "GraviSuite"; + public static final String GRAVI_SUITE_NEO = "gravisuiteneo"; + public static final String GREG_TECH = "gregtech"; + public static final String HARDCORE_ENDER_EXPANSION = "HardcoreEnderExpansion"; + public static final String HODGE_PODGE = "hodgepodge"; + public static final String HOLO_INVENTORY = "holoinventory"; + public static final String I_C2_CROP_PLUGIN = "Ic2Nei"; + public static final String I_C2_NUCLEAR_CONTROL = "IC2NuclearControl"; + public static final String IGUANA_TWEAKS_TINKER_CONSTRUCT = "IguanaTweaksTConstruct"; + public static final String INDUSTRIAL_CRAFT2 = "IC2"; + public static final String IRON_CHESTS = "IronChest"; + public static final String IRON_CHESTS_MINECARTS = "ironchestminecarts"; + public static final String IRON_TANKS = "irontank"; + public static final String J_A_B_B_A = "JABBA"; + public static final String KEKZ_TECH = "kekztech"; + public static final String KUBA_TECH = "kubatech"; + public static final String LOGISTICS_PIPES = "LogisticsPipes"; + public static final String MC_FRAMES = "MCFrames"; + public static final String MAGIC_BEES = "MagicBees"; + public static final String MALISIS_DOORS = "malisisdoors"; + public static final String MANTLE = "Mantle"; + public static final String MINE_AND_BLADE_BATTLE_GEAR2 = "battlegear2"; + public static final String MINECRAFT = "minecraft"; + public static final String N_E_I_CUSTOM_DIAGRAMS = "neicustomdiagram"; + public static final String N_E_I_ORE_PLUGIN = "gtneioreplugin"; + public static final String NATURA = "Natura"; + public static final String NATURES_COMPASS = "naturescompass"; + public static final String NEW_HORIZONS_CORE_MOD = "dreamcraft"; + public static final String NOT_ENOUGH_ITEMS = "NotEnoughItems"; + public static final String OPEN_BLOCKS = "OpenBlocks"; + public static final String OPEN_COMPUTERS = "OpenComputers"; + public static final String OPEN_GLASSES = "openglasses"; + public static final String OPEN_MODULAR_TURRETS = "openmodularturrets"; + public static final String OPEN_PRINTERS = "openprinter"; + public static final String OPEN_SECURITY = "opensecurity"; + public static final String PAMS_HARVEST_CRAFT = "harvestcraft"; + public static final String PAMS_HARVEST_THE_NETHER = "harvestthenether"; + public static final String PLAYER_API = "PlayerAPI"; + public static final String PROJECT_BLUE = "ProjectBlue"; + public static final String PROJECT_RED_CORE = "ProjRed|Core"; + public static final String PROJECT_RED_EXPANSION = "ProjRed|Expansion"; + public static final String PROJECT_RED_EXPLORATION = "ProjRed|Exploration"; + public static final String PROJECT_RED_FABRICATION = "ProjRed|Fabrication"; + public static final String PROJECT_RED_ILLUMINATION = "ProjRed|Illumination"; + public static final String PROJECT_RED_INTEGRATION = "ProjRed|Integration"; + public static final String PROJECT_RED_TRANSMISSION = "ProjRed|Transmission"; + public static final String PROJECT_RED_TRANSPORTATION = "ProjRed|Transportation"; + public static final String QUEST_BOOK = "questbook"; + public static final String RWG = "RWG"; + public static final String RAILCRAFT = "Railcraft"; + public static final String RANDOM_THINGS = "RandomThings"; + public static final String REMOTE_IO = "RIO"; + public static final String S_G_CRAFT = "SGCraft"; + public static final String SLEEPING_BAGS = "sleepingbag"; + public static final String SPICE_OF_LIFE = "SpiceOfLife"; + public static final String STEVES_ADDONS = "StevesAddons"; + public static final String STEVES_CARTS2 = "StevesCarts"; + public static final String STEVES_FACTORY_MANAGER = "StevesFactoryManager"; + public static final String STRUCTURE_LIB = "structurelib"; + public static final String STORAGE_DRAWERS = "StorageDrawers"; + public static final String SUPER_SOLAR_PANELS = "supersolarpanel"; + public static final String TAINTED_MAGIC = "TaintedMagic"; + public static final String TECTECH = "tectech"; + public static final String THAUMCRAFT = "Thaumcraft"; + public static final String THAUMIC_BASES = "thaumicbases"; + public static final String THAUMIC_ENERGISTICS = "thaumicenergistics"; + public static final String THAUMIC_EXPLORATION = "ThaumicExploration"; + public static final String THAUMIC_HORIZONS = "ThaumicHorizons"; + public static final String THAUMIC_BOOTS = "thaumicboots"; + public static final String THAUMIC_MACHINA = "ThaumicMachina"; + public static final String THAUMIC_TINKERER = "ThaumicTinkerer"; + public static final String TINKER_CONSTRUCT = "TConstruct"; + public static final String TINKERS_DEFENCE = "tinkersdefense"; + public static final String TINKERS_GREGWORKS = "TGregworks"; + public static final String TINKERS_MECHWORKS = "TMechworks"; + public static final String TRANSLOCATOR = "Translocator"; + public static final String TRAVELLERS_GEAR = "TravellersGear"; + public static final String TWILIGHT_FOREST = "TwilightForest"; + public static final String UNIVERSAL_SINGULARITIES = "universalsingularities"; + public static final String WAILA = "Waila"; + public static final String WARP_THEORY = "WarpTheory"; + public static final String WIRELESS_REDSTONE_CBE_ADDONS = "WR-CBE|Addons"; + public static final String WIRELESS_REDSTONE_CBE_CORE = "WR-CBE|Core"; + public static final String WIRELESS_REDSTONE_CBE_LOGIC = "WR-CBE|Logic"; + public static final String WITCHERY = "witchery"; + public static final String WITCHING_GADGETS = "WitchingGadgets"; + public static final String Z_TONES = "Ztones"; + + // Do we keep compat of those mods? + public static final String ARS_MAGICA2 = "arsmagica2"; + public static final String GANYS_SURFACE = "ganyssurface"; + public static final String INDUSTRIAL_CRAFT2_CLASSIC = "IC2-Classic-Spmod"; + public static final String MAGICAL_CROPS = "magicalcrops"; + public static final String METALLURGY = "Metallurgy"; + public static final String ROTARY_CRAFT = "RotaryCraft"; + public static final String THERMAL_EXPANSION = "ThermalExpansion"; + public static final String THERMAL_FONDATION = "ThermalFoundation"; + public static final String UNDERGROUND_BIOMES = "UndergroundBiomes"; + + } + + public final String ID; + public final String resourceDomain; + private Boolean modLoaded; + + Mods(String ID) { + this.ID = ID; + this.resourceDomain = ID.toLowerCase(Locale.ENGLISH); + } + + public boolean isModLoaded() { + if (this.modLoaded == null) { + this.modLoaded = Loader.isModLoaded(ID); + } + return this.modLoaded; + } + + public String getResourcePath(String... path) { + return this.getResourceLocation(path) + .toString(); + } + + public ResourceLocation getResourceLocation(String... path) { + return new ResourceLocation(this.resourceDomain, String.join("/", path)); + } +} diff --git a/src/main/java/gregtech/api/enums/OreDictNames.java b/src/main/java/gregtech/api/enums/OreDictNames.java new file mode 100644 index 0000000000..8bb86b5249 --- /dev/null +++ b/src/main/java/gregtech/api/enums/OreDictNames.java @@ -0,0 +1,77 @@ +package gregtech.api.enums; + +public enum OreDictNames { + craftingAnvil, + craftingBook, + craftingCentrifuge, + craftingChest, + craftingCompressor, + craftingConveyor, + craftingDiamondBlade, + craftingDrain, + craftingDuctTape, + craftingElectricFurnace, + craftingElectromagnet, + enderChest, // Vanilla OreDict Name + craftingEnergyCellUpgrade, + craftingEnergyMeter, + craftingExtractor, + craftingFeather, + craftingFurnace, + craftingFilter, + craftingGenerator, + craftingGeothermalGenerator, + craftingGrinder, + craftingInductionFurnace, + craftingIndustrialDiamond, + craftingIronFurnace, + craftingLensBlack, + craftingLensBlue, + craftingLensBrown, + craftingLensCyan, + craftingLensGray, + craftingLensGreen, + craftingLensLightBlue, + craftingLensLightGray, + craftingLensLime, + craftingLensMagenta, + craftingLensOrange, + craftingLensPink, + craftingLensPurple, + craftingLensRed, + craftingLensWhite, + craftingLensYellow, + craftingMacerator, + craftingMetalformer, + craftingPiston, + craftingPump, + craftingQuantumChestUpgrade, + craftingQuartz, + craftingRawMachineTier00, + craftingRawMachineTier01, + craftingRawMachineTier02, + craftingRawMachineTier03, + craftingRawMachineTier04, + craftingRecycler, + craftingRedstoneReceiver, + craftingRedstoneTorch, + craftingRedstoneTranceiver, + craftingRedstoneTransmitter, + craftingSafe, + craftingSteamTank, + craftingSteamUpgrade, + craftingSuperconductor, + craftingTank, + craftingTeleporter, + craftingThermalCentrifuge, + craftingTurbineBladeBronze, + craftingTurbineBladeCarbon, + craftingTurbineBladeMagnalium, + craftingTurbineBladeSteel, + craftingTurbineBladeTungstenSteel, + craftingWireCopper, + craftingWireGold, + craftingWireIron, + craftingWireTin, + craftingWorkBench, +} diff --git a/src/main/java/gregtech/api/enums/OrePrefixes.java b/src/main/java/gregtech/api/enums/OrePrefixes.java new file mode 100644 index 0000000000..bd30f03c3e --- /dev/null +++ b/src/main/java/gregtech/api/enums/OrePrefixes.java @@ -0,0 +1,1417 @@ +package gregtech.api.enums; + +import static gregtech.api.enums.GT_Values.B; +import static gregtech.api.enums.GT_Values.D2; +import static gregtech.api.enums.GT_Values.M; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; + +import net.minecraft.item.ItemStack; + +import com.google.common.collect.ImmutableList; + +import gregtech.api.enums.TC_Aspects.TC_AspectStack; +import gregtech.api.interfaces.ICondition; +import gregtech.api.interfaces.IOreRecipeRegistrator; +import gregtech.api.interfaces.ISubTagContainer; +import gregtech.api.objects.GT_ArrayList; +import gregtech.api.objects.GT_ItemStack; +import gregtech.api.objects.ItemData; +import gregtech.api.objects.MaterialStack; +import gregtech.api.util.GT_Log; +import gregtech.api.util.GT_Utility; +import gregtech.loaders.materialprocessing.ProcessingModSupport; +import it.unimi.dsi.fastutil.objects.ObjectOpenCustomHashSet; +import it.unimi.dsi.fastutil.objects.ObjectSet; + +public enum OrePrefixes { + + @Deprecated + pulp("Pulps", "", "", false, false, false, false, false, false, false, false, false, false, + B[0] | B[1] | B[2] | B[3], -1, 64, -1), + @Deprecated + leaves("Leaves", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + @Deprecated + sapling("Saplings", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + @Deprecated + itemDust("Dusts", "", "", false, false, false, false, false, false, false, false, false, false, + B[0] | B[1] | B[2] | B[3], -1, 64, -1), + /** In case of an End-Ores Mod. Ore -> Material is a Oneway Operation! */ + oreBlackgranite("Black Granite Ores", "Granite ", " Ore", true, true, false, false, false, true, false, false, + false, true, B[3], -1, 64, -1), + /** In case of an End-Ores Mod. Ore -> Material is a Oneway Operation! */ + oreRedgranite("Red Granite Ores", "Granite ", " Ore", true, true, false, false, false, true, false, false, false, + true, B[3], -1, 64, -1), + /** In case of an End-Ores Mod. Ore -> Material is a Oneway Operation! */ + oreMarble("Marble Ores", "Marble ", " Ore", true, true, false, false, false, true, false, false, false, true, B[3], + -1, 64, -1), + /** In case of an End-Ores Mod. Ore -> Material is a Oneway Operation! */ + oreBasalt("Basalt Ores", "Basalt ", " Ore", true, true, false, false, false, true, false, false, false, true, B[3], + -1, 64, -1), + /** Prefix of the Nether-Ores Mod. Causes Ores to double. Ore -> Material is a Oneway Operation! */ + oreNetherrack("Netherrack Ores", "Nether ", " Ore", true, true, false, false, false, true, false, false, false, + true, B[3], -1, 64, -1), + /** Prefix of the Nether-Ores Mod. Causes Ores to double. Ore -> Material is a Oneway Operation! */ + oreNether("Nether Ores", "Nether ", " Ore", true, true, false, false, false, true, false, false, false, true, B[3], + -1, 64, -1), + @Deprecated + denseore("Dense Ores", "", "", false, false, false, false, false, true, false, false, false, true, B[3], -1, 64, + -1), + /** Prefix of the Dense-Ores Mod. Causes Ores to double. Ore -> Material is a Oneway Operation! */ + oreDense("Dense Ores", "Dense ", " Ore", true, true, false, false, false, true, false, false, false, true, B[3], -1, + 64, -1), + /** Prefix of TFC */ + oreRich("Rich Ores", "Rich ", " Ore", true, true, false, false, false, true, false, false, false, true, B[3], -1, + 64, -1), + /** Prefix of TFC */ + oreNormal("Normal Ores", "Normal ", " Ore", true, true, false, false, false, true, false, false, false, true, B[3], + -1, 64, -1), + /** Prefix of Railcraft. */ + oreSmall("Small Ores", "Small ", " Ore", true, true, false, false, false, true, false, false, false, true, B[3], -1, + 64, 67), + /** Prefix of Railcraft. */ + orePoor("Poor Ores", "Poor ", " Ore", true, true, false, false, false, true, false, false, false, true, B[3], -1, + 64, -1), + /** In case of an End-Ores Mod. Ore -> Material is a Oneway Operation! */ + oreEndstone("Endstone Ores", "End ", " Ore", true, true, false, false, false, true, false, false, false, true, B[3], + -1, 64, -1), + /** In case of an End-Ores Mod. Ore -> Material is a Oneway Operation! */ + oreEnd("End Ores", "End ", " Ore", true, true, false, false, false, true, false, false, false, true, B[3], -1, 64, + -1), + @Deprecated + oreGem("Ores", "", "", false, false, false, false, false, true, false, false, false, true, B[3], -1, 64, -1), + /** Regular Ore Prefix. Ore -> Material is a Oneway Operation! Introduced by Eloraam */ + ore("Ores", "", " Ore", true, true, false, false, false, true, false, false, false, true, B[3], -1, 64, 68), + crushedCentrifuged("Centrifuged Ores", "Centrifuged ", " Ore", true, true, false, false, false, false, false, true, + false, true, B[3], -1, 64, 7), + crushedPurified("Purified Ores", "Purified ", " Ore", true, true, false, false, false, false, false, true, false, + true, B[3], -1, 64, 6), + crushed("Crushed Ores", "Crushed ", " Ore", true, true, false, false, false, false, false, true, false, true, B[3], + -1, 64, 5), + rawOre("Raw Ore", "Raw ", " Ore", true, true, false, false, false, false, false, false, false, true, B[3], -1, 64, + 64), + + /** Introduced by Mekanism */ + shard("Crystallised Shards", "", "", true, true, false, false, false, false, false, false, false, true, B[3], -1, + 64, -1), + clump("Clumps", "", "", true, true, false, false, false, false, false, false, false, true, B[3], -1, 64, -1), + reduced("Reduced Gravels", "", "", true, true, false, false, false, false, false, false, false, true, B[3], -1, 64, + -1), + crystalline("Crystallised Metals", "", "", true, true, false, false, false, false, false, false, false, true, B[3], + -1, 64, -1), + cleanGravel("Clean Gravels", "", "", true, true, false, false, false, false, false, false, false, true, B[3], -1, + 64, -1), + dirtyGravel("Dirty Gravels", "", "", true, true, false, false, false, false, false, false, false, true, B[3], -1, + 64, -1), + /** A quintuple Ingot. */ + ingotQuintuple("5x Ingots", "Quintuple ", " Ingot", true, true, false, false, false, false, true, true, false, + false, B[1], M * 5, 64, 16), + /** A quadruple Ingot. */ + ingotQuadruple("4x Ingots", "Quadruple ", " Ingot", true, true, false, false, false, false, true, true, false, + false, B[1], M * 4, 64, 15), + @Deprecated + ingotQuad("4x Ingots", "Quadruple ", " Ingot", false, false, false, false, false, false, false, false, false, false, + B[1], -1, 64, 15), + /** A triple Ingot. */ + ingotTriple("3x Ingots", "Triple ", " Ingot", true, true, false, false, false, false, true, false, false, false, + B[1], M * 3, 64, 14), + /** A double Ingot. Introduced by TerraFirmaCraft */ + ingotDouble("2x Ingots", "Double ", " Ingot", true, true, false, false, false, false, true, true, false, false, + B[1], M * 2, 64, 13), + /** A hot Ingot, which has to be cooled down by a Vacuum Freezer. */ + ingotHot("Hot Ingots", "Hot ", " Ingot", true, true, false, false, false, false, false, true, false, false, B[1], + M * 1, 64, 12), + /** A regular Ingot. Introduced by Eloraam */ + ingot("Ingots", "", " Ingot", true, true, false, false, false, false, false, true, false, false, B[1], M * 1, 64, + 11), + /** A regular Gem worth one small Dust. Introduced by TerraFirmaCraft */ + gemChipped("Chipped Gemstones", "Chipped ", "", true, true, true, false, false, false, true, true, false, false, + B[2], M / 4, 64, 59), + /** A regular Gem worth two small Dusts. Introduced by TerraFirmaCraft */ + gemFlawed("Flawed Gemstones", "Flawed ", "", true, true, true, false, false, false, true, true, false, false, B[2], + M / 2, 64, 60), + /** A regular Gem worth two Dusts. Introduced by TerraFirmaCraft */ + gemFlawless("Flawless Gemstones", "Flawless ", "", true, true, true, false, false, false, true, true, false, false, + B[2], M * 2, 64, 61), + /** A regular Gem worth four Dusts. Introduced by TerraFirmaCraft */ + gemExquisite("Exquisite Gemstones", "Exquisite ", "", true, true, true, false, false, false, true, true, false, + false, B[2], M * 4, 64, 62), + /** A regular Gem worth one Dust. Introduced by Eloraam */ + gem("Gemstones", "", "", true, true, true, false, false, false, true, true, false, false, B[2], M * 1, 64, 8), + @Deprecated + dustDirty("Impure Dusts", "", "", false, false, false, false, false, false, false, false, false, true, B[3], -1, 64, + 3), + /** 1/9th of a Dust. */ + dustTiny("Tiny Dusts", "Tiny Pile of ", " Dust", true, true, false, false, false, false, false, true, false, false, + B[0] | B[1] | B[2] | B[3], M / 9, 64, 0), + /** 1/4th of a Dust. */ + dustSmall("Small Dusts", "Small Pile of ", " Dust", true, true, false, false, false, false, false, true, false, + false, B[0] | B[1] | B[2] | B[3], M / 4, 64, 1), + /** Dust with impurities. 1 Unit of Main Material and 1/9 - 1/4 Unit of secondary Material */ + dustImpure("Impure Dusts", "Impure Pile of ", " Dust", true, true, false, false, false, false, false, true, false, + true, B[3], M * 1, 64, 3), + dustRefined("Refined Dusts", "Refined Pile of ", " Dust", true, true, false, false, false, false, false, true, + false, true, B[3], M * 1, 64, 2), + dustPure("Purified Dusts", "Purified Pile of ", " Dust", true, true, false, false, false, false, false, true, false, + true, B[3], M * 1, 64, 4), + /** Pure Dust worth of one Ingot or Gem. Introduced by Alblaka. */ + dust("Dusts", "", " Dust", true, true, false, false, false, false, false, true, false, false, + B[0] | B[1] | B[2] | B[3], M * 1, 64, 2), + /** A Nugget. Introduced by Eloraam */ + nugget("Nuggets", "", " Nugget", true, true, false, false, false, false, false, true, false, false, B[1], M / 9, 64, + 9), + /** Special Alloys have this prefix. */ + plateAlloy("Alloy Plates", "", "", true, false, false, false, false, false, false, false, false, false, B[1], -1, + 64, 17), + plateSteamcraft("Steamcraft Plates", "", "", false, false, false, false, false, false, false, false, false, false, + B[1], -1, 64, 17), + /** 9 Plates combined in one Item. */ + plateDense("Dense Plates", "Dense ", " Plate", true, true, false, false, false, false, true, true, false, false, + B[1], M * 9, 64, 22), + plateQuintuple("5x Plates", "Quintuple ", " Plate", true, true, false, false, false, false, true, true, false, + false, B[1], M * 5, 64, 21), + plateQuadruple("4x Plates", "Quadruple ", " Plate", true, true, false, false, false, false, true, true, false, + false, B[1], M * 4, 64, 20), + @Deprecated + plateQuad("4x Plates", "", "", false, false, false, false, false, false, false, false, false, false, B[1], -1, 64, + 20), + plateTriple("3x Plates", "Triple ", " Plate", true, true, false, false, false, false, true, true, false, false, + B[1], M * 3, 64, 19), + plateDouble("2x Plates", "Double ", " Plate", true, true, false, false, false, false, true, true, false, false, + B[1], M * 2, 64, 18), + plate("Plates", "", " Plate", true, true, false, false, false, false, true, true, false, false, B[1] | B[2], M * 1, + 64, 17), + /** Casing made of 1/2 Ingot/Dust */ + itemCasing("Casings", "", " Casing", true, true, false, false, false, false, true, true, false, false, B[1] | B[2], + M / 2, 64, 10), + /** Foil made of 1/4 Ingot/Dust. */ + foil("Foils", "", " Foil", true, true, false, false, false, false, true, true, false, false, B[1], M / 4, 64, 29), + /** Stick made of an Ingot. */ + stickLong("Long Sticks/Rods", "Long ", " Rod", true, true, false, false, false, false, true, true, false, false, + B[1] | B[2], M * 1, 64, 54), + /** Stick made of half an Ingot. Introduced by Eloraam */ + stick("Sticks/Rods", "", " Rod", true, true, false, false, false, false, true, true, false, false, B[1] | B[2], + M / 2, 64, 23), + /** consisting out of one Nugget. */ + round("Rounds", "", " Round", true, true, false, false, false, false, true, true, false, false, B[1], M / 9, 64, + 25), + /** consisting out of 1/8 Ingot or 1/4 Stick. */ + bolt("Bolts", "", " Bolt", true, true, false, false, false, false, true, true, false, false, B[1] | B[2], M / 8, 64, + 26), + /** contain dusts */ + comb("Combs", "", " Comb", false, false, false, false, false, false, false, true, false, false, B[1] | B[2], M, 64, + 101), + /** consisting out of a Bolt. */ + screw("Screws", "", " Screw", true, true, false, false, false, false, true, true, false, false, B[1] | B[2], M / 9, + 64, 27), + /** consisting out of 1/2 Stick. */ + ring("Rings", "", " Ring", true, true, false, false, false, false, true, true, false, false, B[1], M / 4, 64, 28), + /** consisting out of 1 Fine Wire. */ + springSmall("Small Springs", "Small ", " Spring", true, true, false, false, false, false, true, true, false, false, + B[1], M / 4, 64, 55), + /** consisting out of 2 Sticks. */ + spring("Springs", "", " Spring", true, true, false, false, false, false, true, true, false, false, B[1], M * 1, 64, + 56), + /** consisting out of 1/8 Ingot or 1/4 Wire. */ + wireFine("Fine Wires", "Fine ", " Wire", true, true, false, false, false, false, true, true, false, false, B[1], + M / 8, 64, 51), + /** consisting out of 4 Plates, 1 Ring and 1 Screw. */ + rotor("Rotors", "", " Rotor", true, true, false, false, false, false, true, true, false, false, B[7], M * 4 + M / 4, + 64, 53), + gearGtSmall("Small Gears", "Small ", " Gear", true, true, false, false, false, false, true, true, false, false, + B[7], M * 1, 64, 52), + /** Introduced by me because BuildCraft has ruined the gear Prefix... */ + gearGt("Gears", "", " Gear", true, true, false, false, false, false, true, true, false, false, B[7], M * 4, 16, 63), + /** 3/4 of a Plate or Gem used to shape a Lense. Normally only used on Transparent Materials. */ + lens("Lenses", "", " Lens", true, true, false, false, false, false, true, true, false, false, B[2], (M * 3) / 4, 64, + 24), + /** consisting out of 16 Dusts. */ + crateGtDust("Crates of Dust", "Crate of ", " Dust", true, true, false, true, false, false, false, true, false, + false, B[0] | B[1] | B[2] | B[3], -1, 64, 96), + /** consisting out of 16 Plates. */ + crateGtPlate("Crates of Plates", "Crate of ", " Plate", true, true, false, true, false, false, false, true, false, + false, B[1] | B[2], -1, 64, 99), + /** consisting out of 16 Ingots. */ + crateGtIngot("Crates of Ingots", "Crate of ", " Ingot", true, true, false, true, false, false, false, true, false, + false, B[1], -1, 64, 97), + /** consisting out of 16 Gems. */ + crateGtGem("Crates of Gems", "Crate of ", " Gem", true, true, false, true, false, false, false, true, false, false, + B[2], -1, 64, 98), + /** Hot Cell full of Plasma, which can be used in the Plasma Generator. */ + cellPlasma("Cells of Plasma", "", " Plasma Cell", true, true, true, true, false, false, false, true, false, false, + B[5], M * 1, 64, 31), + /** Hot Cell full of molten stuff, which can be used in the Plasma Generator. */ + cellMolten("Cells of Molten stuff", "Molten ", " Cell", true, true, true, true, false, false, false, true, false, + false, 0, M * 1, 64, 31), + cell("Cells", "", " Cell", true, true, true, true, false, false, true, true, false, false, B[4] | B[8], M * 1, 64, + 30), + /** A vanilla Iron Bucket filled with the Material. */ + bucket("Buckets", "", " Bucket", true, true, true, true, false, false, true, false, false, false, B[4] | B[8], + M * 1, 64, -1), + /** An Iguana Tweaks Clay Bucket filled with the Material. */ + bucketClay("Clay Buckets", "", " Clay Bucket", true, true, true, true, false, false, true, false, false, false, + B[4] | B[8], M * 1, 64, -1), + /** Glass Bottle containing a Fluid. */ + bottle("Bottles", "", " Bottle", true, true, true, true, false, false, false, false, false, false, B[4] | B[8], -1, + 64, -1), + capsule("Capsules", "", " Capsule", false, true, true, true, false, false, false, false, false, false, B[4] | B[8], + M * 1, 64, -1), + crystal("Crystals", "", " Crystal", false, true, false, false, false, false, true, false, false, false, B[2], M * 1, + 64, -1), + bulletGtSmall("Small Bullets", "Small ", " Bullet", true, true, false, false, true, false, true, false, true, false, + B[6] | B[8], M / 9, 64, -1), + bulletGtMedium("Medium Bullets", "Medium ", " Bullet", true, true, false, false, true, false, true, false, true, + false, B[6] | B[8], M / 6, 64, -1), + bulletGtLarge("Large Bullets", "Large ", " Bullet", true, true, false, false, true, false, true, false, true, false, + B[6] | B[8], M / 3, 64, -1), + /** Arrow made of 1/4 Ingot/Dust + Wooden Stick. */ + arrowGtWood("Regular Arrows", "", " Arrow", true, true, false, false, true, false, true, false, true, false, B[6], + M / 4, 64, 57), + /** Arrow made of 1/4 Ingot/Dust + Plastic Stick. */ + arrowGtPlastic("Light Arrows", "Light ", " Arrow", true, true, false, false, true, false, true, false, true, false, + B[6], M / 4, 64, 58), + arrow("Arrows", "", "", false, false, true, false, false, false, false, false, true, false, B[6], -1, 64, 57), + /** consisting out of 1/4 Ingot. */ + toolHeadArrow("Arrow Heads", "", " Arrow Head", true, true, false, false, false, false, true, true, false, false, + B[6], M / 4, 64, 46), + /** consisting out of 2 Ingots. */ + toolHeadSword("Sword Blades", "", " Sword Blade", true, true, false, false, false, false, true, true, false, false, + B[6], M * 2, 64, 32), + /** consisting out of 3 Ingots. */ + toolHeadPickaxe("Pickaxe Heads", "", " Pickaxe Head", true, true, false, false, false, false, true, true, false, + false, B[6], M * 3, 64, 33), + /** consisting out of 1 Ingots. */ + toolHeadShovel("Shovel Heads", "", " Shovel Head", true, true, false, false, false, false, true, true, false, false, + B[6], M * 1, 64, 34), + /** consisting out of 1 Ingots. */ + toolHeadUniversalSpade("Universal Spade Heads", "", " Universal Spade Head", true, true, false, false, false, false, + true, true, false, false, B[6], M * 1, 64, 43), + /** consisting out of 3 Ingots. */ + toolHeadAxe("Axe Heads", "", " Axe Head", true, true, false, false, false, false, true, true, false, false, B[6], + M * 3, 64, 35), + /** consisting out of 2 Ingots. */ + toolHeadHoe("Hoe Heads", "", " Hoe Head", true, true, false, false, false, false, true, true, false, false, B[6], + M * 2, 64, 36), + /** consisting out of 3 Ingots. */ + toolHeadSense("Sense Blades", "", " Sense Blade", true, true, false, false, false, false, true, true, false, false, + B[6], M * 3, 64, 44), + /** consisting out of 2 Ingots. */ + toolHeadFile("File Heads", "", " File Head", true, true, false, false, false, false, true, true, false, false, B[6], + M * 2, 64, 38), + /** consisting out of 6 Ingots. */ + toolHeadHammer("Hammer Heads", "", " Hammer Head", true, true, false, false, false, false, true, true, false, false, + B[6], M * 6, 64, 37), + /** consisting out of 4 Ingots. */ + toolHeadPlow("Plow Heads", "", " Plow Head", true, true, false, false, false, false, true, true, false, false, B[6], + M * 4, 64, 45), + /** consisting out of 2 Ingots. */ + toolHeadSaw("Saw Blades", "", " Saw Blade", true, true, false, false, false, false, true, true, false, false, B[6], + M * 2, 64, 39), + /** consisting out of 4 Ingots. */ + toolHeadBuzzSaw("Buzzsaw Blades", "", " Buzzsaw Blade", true, true, false, false, false, false, true, true, false, + false, B[6], M * 4, 64, 48), + /** consisting out of 1 Ingots. */ + toolHeadScrewdriver("Screwdriver Tips", "", " Screwdriver Tip", true, true, false, false, false, false, true, false, + false, false, B[6], M * 1, 64, 47), + /** consisting out of 4 Ingots. */ + toolHeadDrill("Drill Tips", "", " Drill Tip", true, true, false, false, false, false, true, true, false, false, + B[6], M * 4, 64, 40), + /** consisting out of 2 Ingots. */ + toolHeadChainsaw("Chainsaw Tips", "", " Chainsaw Tip", true, true, false, false, false, false, true, true, false, + false, B[6], M * 2, 64, 41), + /** consisting out of 4 Ingots. */ + toolHeadWrench("Wrench Tips", "", " Wrench Tip", true, true, false, false, false, false, true, true, false, false, + B[6], M * 4, 64, 42), + /** consisting out of 6 Ingots. */ + turbineBlade("Turbine Blades", "", " Turbine Blade", true, true, false, false, false, false, true, true, false, + false, B[6], M * 6, 64, 100), + /** vanilly Sword */ + toolSword("Swords", "", "", false, true, false, false, false, false, true, false, true, false, B[6], M * 2, 1, -1), + /** vanilly Pickaxe */ + toolPickaxe("Pickaxes", "", "", false, true, false, false, false, false, true, false, true, false, B[6], M * 3, 1, + -1), + /** vanilly Shovel */ + toolShovel("Shovels", "", "", false, true, false, false, false, false, true, false, true, false, B[6], M * 1, 1, + -1), + /** vanilly Axe */ + toolAxe("Axes", "", "", false, true, false, false, false, false, true, false, true, false, B[6], M * 3, 1, -1), + /** vanilly Hoe */ + toolHoe("Hoes", "", "", false, true, false, false, false, false, true, false, true, false, B[6], M * 2, 1, -1), + /** vanilly Shears */ + toolShears("Shears", "", "", false, true, false, false, false, false, true, false, true, false, B[6], M * 2, 1, -1), + /** + * toolPot, toolSkillet, toolSaucepan, toolBakeware, toolCuttingboard, toolMortarandpestle, toolMixingbowl, + * toolJuicer + */ + tool("Tools", "", "", false, false, false, false, false, false, false, false, true, false, B[6], -1, 1, -1), + compressedCobblestone("9^X Compressed Cobblestones", "", "", false, false, false, false, false, false, false, false, + false, false, 0, -1, 64, -1), + compressedStone("9^X Compressed Stones", "", "", false, false, false, false, false, false, false, false, false, + false, 0, -1, 64, -1), + compressedDirt("9^X Compressed Dirt", "", "", false, false, false, false, false, false, false, false, false, false, + 0, -1, 64, -1), + compressedGravel("9^X Compressed Gravel", "", "", false, false, false, false, false, false, false, false, false, + false, 0, -1, 64, -1), + compressedSand("9^X Compressed Sand", "", "", false, false, false, false, false, false, false, false, false, false, + 0, -1, 64, -1), + /** Compressed Material, worth 1 Unit. Introduced by Galacticraft */ + compressed("Compressed Materials", "Compressed ", "", true, true, false, false, false, false, true, false, false, + false, 0, M * 3, 64, -1), + glass("Glasses", "", "", false, false, true, false, true, false, false, false, false, false, 0, -1, 64, -1), + paneGlass("Glass Panes", "", "", false, false, true, false, false, true, false, false, false, false, 0, -1, 64, -1), + blockGlass("Glass Blocks", "", "", false, false, true, false, false, true, false, false, false, false, 0, -1, 64, + -1), + blockWool("Wool Blocks", "", "", false, false, true, false, false, true, false, false, false, false, 0, -1, 64, -1), + /** IGNORE */ + block_("Random Blocks", "", "", false, false, false, false, false, true, false, false, false, false, 0, -1, 64, -1), + /** Storage Block consisting out of 9 Ingots/Gems/Dusts. Introduced by CovertJaguar */ + block("Storage Blocks", "Block of ", "", true, true, false, false, false, true, true, false, false, false, 0, M * 9, + 64, 71), + /** Special Prefix used mainly for the Crafting Handler. */ + craftingTool("Crafting Tools", "", "", false, false, false, false, false, false, false, false, true, false, 0, -1, + 64, -1), + /** Special Prefix used mainly for the Crafting Handler. */ + crafting("Crafting Ingredients", "", "", false, false, false, false, false, false, false, false, false, false, 0, + -1, 64, -1), + /** Special Prefix used mainly for the Crafting Handler. */ + craft("Crafting Stuff?", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, + -1), + /** Prefix used for Logs. Usually as "logWood". Introduced by Eloraam */ + log("Logs", "", "", false, false, false, false, false, true, false, false, false, false, 0, -1, 64, -1), + /** Prefix used for Slabs. Usually as "slabWood" or "slabStone". Introduced by SirSengir */ + slab("Slabs", "", "", false, false, false, false, false, true, false, false, false, false, 0, -1, 64, -1), + /** Prefix used for Stairs. Usually as "stairWood" or "stairStone". Introduced by SirSengir */ + stair("Stairs", "", "", false, false, false, false, false, true, false, false, false, false, 0, -1, 64, -1), + /** Prefix used for Fences. Usually as "fenceWood". Introduced by Forge */ + fence("Fences", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + /** Prefix for Planks. Usually "plankWood". Introduced by Eloraam */ + plank("Planks", "", "", false, false, false, false, false, true, false, false, false, false, 0, -1, 64, -1), + /** Prefix for Saplings. */ + treeSapling("Saplings", "", "", false, false, true, false, false, true, false, false, false, false, 0, -1, 64, -1), + /** Prefix for Leaves. */ + treeLeaves("Leaves", "", "", false, false, true, false, false, true, false, false, false, false, 0, -1, 64, -1), + /** Prefix for Tree Parts. */ + tree("Tree Parts", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + /** Cobblestone Prefix for all Cobblestones. */ + stoneCobble("Cobblestones", "", "", false, false, true, false, false, true, false, false, false, false, 0, -1, 64, + -1), + /** Smoothstone Prefix. */ + stoneSmooth("Smoothstones", "", "", false, false, true, false, false, true, false, false, false, false, 0, -1, 64, + -1), + /** Mossy Stone Bricks. */ + stoneMossyBricks("mossy Stone Bricks", "", "", false, false, true, false, false, true, false, false, false, false, + 0, -1, 64, -1), + /** Mossy Cobble. */ + stoneMossy("Mossy Stones", "", "", false, false, true, false, false, true, false, false, false, false, 0, -1, 64, + -1), + @Deprecated + stoneBricksMossy("Mossy Stone Bricks", "", "", false, false, false, false, false, true, false, false, false, false, + 0, -1, 64, -1), + /** Stone Bricks. */ + stoneBricks("Stone Bricks", "", "", false, false, true, false, false, true, false, false, false, false, 0, -1, 64, + -1), + @Deprecated + stoneBrick("Stone Bricks", "", "", false, false, false, false, false, true, false, false, false, false, 0, -1, 64, + -1), + /** Cracked Bricks. */ + stoneCracked("Cracked Stones", "", "", false, false, true, false, false, true, false, false, false, false, 0, -1, + 64, -1), + /** Chiseled Stone. */ + stoneChiseled("Chiseled Stones", "", "", false, false, true, false, false, true, false, false, false, false, 0, -1, + 64, -1), + /** Prefix to determine which kind of Rock this is. */ + stone("Stones", "", "", false, true, true, false, true, true, false, false, false, false, 0, -1, 64, -1), + cobblestone("Cobblestones", "", "", false, true, true, false, false, true, false, false, false, false, 0, -1, 64, + -1), + /** Prefix to determine which kind of Rock this is. */ + rock("Rocks", "", "", false, true, true, false, true, true, false, false, false, false, 0, -1, 64, -1), + record("Records", "", "", false, false, true, false, false, false, false, false, false, false, 0, -1, 1, -1), + rubble("Rubbles", "", "", true, true, true, false, false, false, false, false, false, false, 0, -1, 64, -1), + scraps("Scraps", "", "", true, true, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + scrap("Scraps", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + /** IGNORE */ + item_("Items", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + /** Random Item. Introduced by Alblaka */ + item("Items", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + /** Used for Books of any kind. */ + book("Books", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + /** Used for Papers of any kind. */ + paper("Papers", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + /** Used for the 16 dyes. Introduced by Eloraam */ + dye("Dyes", "", "", false, false, true, false, false, false, false, false, false, false, 0, -1, 64, -1), + /** Used for the 16 colors of Stained Clay. Introduced by Forge */ + stainedClay("Stained Clays", "", "", false, false, true, false, false, true, false, false, false, false, 0, -1, 64, + -1), + /** vanilly Helmet */ + armorHelmet("Helmets", "", "", false, true, false, false, false, false, true, false, true, false, B[6], M * 5, 1, + -1), + /** vanilly Chestplate */ + armorChestplate("Chestplates", "", "", false, true, false, false, false, false, true, false, true, false, B[6], + M * 8, 1, -1), + /** vanilly Pants */ + armorLeggings("Leggings", "", "", false, true, false, false, false, false, true, false, true, false, B[6], M * 7, 1, + -1), + /** vanilly Boots */ + armorBoots("Boots", "", "", false, true, false, false, false, false, true, false, true, false, B[6], M * 4, 1, -1), + armor("Armor Parts", "", "", false, false, false, false, false, false, false, false, true, false, B[6], -1, 1, -1), + frameGt("Frame Boxes", "", "", true, true, false, false, true, false, true, false, false, false, 0, M * 2, 64, 83), + pipeTiny("Tiny Pipes", "Tiny ", " Pipe", true, true, false, false, true, false, true, false, false, false, 0, M / 2, + 64, 78), + pipeSmall("Small Pipes", "Small ", " Pipe", true, true, false, false, true, false, true, false, false, false, 0, + M * 1, 64, 79), + pipeMedium("Medium Pipes", "Medium ", " Pipe", true, true, false, false, true, false, true, false, false, false, 0, + M * 3, 64, 80), + pipeLarge("Large pipes", "Large ", " Pipe", true, true, false, false, true, false, true, false, false, false, 0, + M * 6, 64, 81), + pipeHuge("Huge Pipes", "Huge ", " Pipe", true, true, false, false, true, false, true, false, false, false, 0, + M * 12, 64, 82), + pipeQuadruple("Quadruple Pipes", "Quadruple ", " Pipe", true, true, false, false, true, false, true, false, false, + false, 0, M * 12, 64, 84), + pipeNonuple("Nonuple Pipes", "Nonuple ", " Pipe", true, true, false, false, true, false, true, false, false, false, + 0, M * 9, 64, 85), + pipeRestrictiveTiny("Tiny Restrictive Pipes", "Tiny Restrictive ", " Pipe", true, true, false, false, true, false, + true, false, false, false, 0, M / 2, 64, 78), + pipeRestrictiveSmall("Small Restrictive Pipes", "Small Restrictive ", " Pipe", true, true, false, false, true, + false, true, false, false, false, 0, M * 1, 64, 79), + pipeRestrictiveMedium("Medium Restrictive Pipes", "Medium Restrictive ", " Pipe", true, true, false, false, true, + false, true, false, false, false, 0, M * 3, 64, 80), + pipeRestrictiveLarge("Large Restrictive Pipes", "Large Restrictive ", " Pipe", true, true, false, false, true, + false, true, false, false, false, 0, M * 6, 64, 81), + pipeRestrictiveHuge("Huge Restrictive Pipes", "Huge Restrictive ", " Pipe", true, true, false, false, true, false, + true, false, false, false, 0, M * 12, 64, 82), + pipe("Pipes", "", " Pipe", true, false, false, false, false, false, false, false, false, false, 0, -1, 64, 77), + wireGt16("16x Wires", "16x ", " Wire", true, true, false, false, false, false, true, false, false, false, 0, M * 8, + 64, -1), + wireGt12("12x Wires", "12x ", " Wire", true, true, false, false, false, false, true, false, false, false, 0, M * 6, + 64, -1), + wireGt08("8x Wires", "8x ", " Wire", true, true, false, false, false, false, true, false, false, false, 0, M * 4, + 64, -1), + wireGt04("4x Wires", "4x ", " Wire", true, true, false, false, false, false, true, false, false, false, 0, M * 2, + 64, -1), + wireGt02("2x Wires", "2x ", " Wire", true, true, false, false, false, false, true, false, false, false, 0, M * 1, + 64, -1), + wireGt01("1x Wires", "1x ", " Wire", true, true, false, false, false, false, true, false, false, false, 0, M / 2, + 64, -1), + cableGt16("16x Cables", "16x ", " Cable", true, true, false, false, false, false, true, false, false, false, 0, + M * 8, 64, -1), + cableGt12("12x Cables", "12x ", " Cable", true, true, false, false, false, false, true, false, false, false, 0, + M * 6, 64, -1), + cableGt08("8x Cables", "8x ", " Cable", true, true, false, false, false, false, true, false, false, false, 0, M * 4, + 64, -1), + cableGt04("4x Cables", "4x ", " Cable", true, true, false, false, false, false, true, false, false, false, 0, M * 2, + 64, -1), + cableGt02("2x Cables", "2x ", " Cable", true, true, false, false, false, false, true, false, false, false, 0, M * 1, + 64, -1), + cableGt01("1x Cables", "1x ", " Cable", true, true, false, false, false, false, true, false, false, false, 0, M / 2, + 64, -1), + + /* + * Electric Components. usual Materials for this are: Primitive (Tier 1) Basic (Tier 2) as used by UE as well : IC2 + * Circuit and RE-Battery Good (Tier 3) Advanced (Tier 4) as used by UE as well : Advanced Circuit, Advanced Battery + * and Lithium Battery Data (Tier 5) : Data Storage Circuit Elite (Tier 6) as used by UE as well : Energy Crystal + * and Data Control Circuit Master (Tier 7) : Energy Flow Circuit and Lapotron Crystal Ultimate (Tier 8) : Data Orb + * and Lapotronic Energy Orb Infinite (Cheaty) + */ + batterySingleuse("Single Use Batteries", "", "", false, true, false, false, false, false, false, false, false, + false, 0, -1, 64, -1), + battery("Reusable Batteries", "", "", false, true, false, false, false, false, false, false, false, false, 0, -1, + 64, -1), + circuit("Circuits", "", "", true, true, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + /** Introduced by Buildcraft */ + chipset("Chipsets", "", "", true, true, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + /** A whole Computer. "computerMaster" = ComputerCube */ + computer("Computers", "", "", true, true, false, false, true, false, false, false, false, false, 0, -1, 64, -1), + + // random known prefixes without special abilities. + skull("Skulls", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + plating("Platings", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + dinosaur("Dinosaurs", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + travelgear("Travel Gear", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, + -1), + bauble("Baubles", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + cluster("Clusters", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + grafter("Grafters", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + scoop("Scoops", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + frame("Frames", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + tome("Tomes", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + junk("Junk", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + bee("Bees", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + rod("Rods", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + dirt("Dirts", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + sand("Sands", "", "", false, false, true, false, false, true, false, false, false, false, 0, -1, 64, -1), + grass("Grasses", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + gravel("Gravels", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + mushroom("Mushrooms", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + /** Introduced by Eloraam */ + wood("Woods", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + drop("Drops", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + fuel("Fuels", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + panel("Panels", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + brick("Bricks", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + chunk("Chunks", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + wire("Wires", "", "", false, false, false, false, true, false, false, false, false, false, 0, -1, 64, -1), + seed("Seeds", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + reed("Reeds", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + sheetDouble("2x Sheets", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, + -1), + sheet("Sheets", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + crop("Crops", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + plant("Plants", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + coin("Coins", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + lumar("Lumars", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + ground("Grounded Stuff", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, + -1), + cable("Cables", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + component("Components", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, + -1), + wax("Waxes", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + wall("Walls", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + tube("Tubes", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + list("Lists", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + food("Foods", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + /** Introduced by SirSengir */ + gear("Gears", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + coral("Corals", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + flower("Flowers", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + storage("Storages", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + material("Materials", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + plasma("Plasmas", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + element("Elements", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + molecule("Molecules", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + wafer("Wafers", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + orb("Orbs", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + handle("Handles", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + blade("Blades", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + head("Heads", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + motor("Motors", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + bit("Bits", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + shears("Shears", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + turbine("Turbines", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + fertilizer("Fertilizers", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, + -1), + chest("Chests", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + raw("Raw Things", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + stainedGlass("Stained Glasses", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, + 64, -1), + mystic("Mystic Stuff", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + mana("Mana Stuff", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + rune("Runes", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + petal("Petals", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + pearl("Pearls", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + powder("Powders", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + soulsand("Soulsands", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + obsidian("Obsidians", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + glowstone("Glowstones", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, + -1), + beans("Beans", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + br("br", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + essence("Essences", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + alloy("Alloys", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + cooking("Cooked Things", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, + -1), + elven("Elven Stuff", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + reactor("Reactors", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + mffs("MFFS", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + projred("Project Red", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + ganys("Ganys Stuff", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + liquid("Liquids", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + bars("Bars", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + bar("Bars", "", "", false, false, false, false, false, false, false, false, false, false, 0, -1, 64, -1), + /** Reverse Head consisting out of 6 Ingots. */ + toolHeadMallet("Mallet Heads", "", " Mallet Head", true, true, false, false, false, false, true, true, false, false, + B[6], M * 6, 64, 127), + /** Reverse Stick made of half an Ingot. Introduced by Eloraam */ + handleMallet("Mallet Handle", "", " Handle", true, true, false, false, false, false, true, true, false, false, + B[1] | B[2], M / 2, 64, 126), + + // Cracked fluids + cellHydroCracked1("Cells", "Lightly Hydro-Cracked ", " Cell", true, true, true, true, false, false, false, true, + false, false, 0, M * 1, 64, 30), + cellHydroCracked2("Cells", "Moderately Hydro-Cracked ", " Cell", true, true, true, true, false, false, false, true, + false, false, 0, M * 1, 64, 30), + cellHydroCracked3("Cells", "Severely Hydro-Cracked ", " Cell", true, true, true, true, false, false, false, true, + false, false, 0, M * 1, 64, 30), + cellSteamCracked1("Cells", "Lightly Steam-Cracked ", " Cell", true, true, true, true, false, false, false, true, + false, false, 0, M * 1, 64, 30), + cellSteamCracked2("Cells", "Moderately Steam-Cracked ", " Cell", true, true, true, true, false, false, false, true, + false, false, 0, M * 1, 64, 30), + cellSteamCracked3("Cells", "Severely Steam-Cracked ", " Cell", true, true, true, true, false, false, false, true, + false, false, 0, M * 1, 64, 30), + + componentCircuit("Circuit Parts", "", "", true, true, false, false, false, false, false, false, false, false, 0, -1, + 64, -1), + + apiaryUpgrade("Industrial Apiary Upgrade", "", "", false, false, true, false, false, false, false, false, false, + false, 0, -1, 64, -1), + beeComb("Bee Combs", "", "", true, false, true, false, false, false, false, false, false, false, 0, -1, 64, -1), + nanite("Nanites", "", " Nanites", true, true, true, false, false, false, false, false, false, false, 0, -1, 64, 50), + // migrated from GT++ + milled("Milled Ores", "Milled ", " Ore", true, true, false, false, false, false, false, false, false, true, B[3], + -1, 64, -1), + // migrated from bartworks + blockCasing("A Casing block for a Multiblock-Machine", "Bolted ", " Casing", true, true, true, true, false, true, + false, true, false, false, 0, M * 9, 64, -1), + blockCasingAdvanced("An Advanced Casing block for a Multiblock-Machine", "Rebolted ", " Casing", true, true, true, + true, false, true, false, true, false, false, 0, M * 9, 64, -1), + capsuleMolten("Capsule of Molten stuff", "Molten ", " Capsule", true, true, true, true, false, false, false, true, + false, false, 0, M * 1, 64, -1); + + public static final ImmutableList<OrePrefixes> CELL_TYPES = ImmutableList.of( + cell, + cellMolten, + cellPlasma, + cellHydroCracked1, + cellHydroCracked2, + cellHydroCracked3, + cellSteamCracked1, + cellSteamCracked2, + cellSteamCracked3); + + static { + pulp.mPrefixInto = dust; + oreGem.mPrefixInto = ore; + leaves.mPrefixInto = treeLeaves; + sapling.mPrefixInto = treeSapling; + itemDust.mPrefixInto = dust; + dustDirty.mPrefixInto = dustImpure; + denseore.mPrefixInto = oreDense; + ingotQuad.mPrefixInto = ingotQuadruple; + plateQuad.mPrefixInto = plateQuadruple; + stoneBrick.mPrefixInto = stoneBricks; + stoneBricksMossy.mPrefixInto = stoneMossyBricks; + + ingotHot.mHeatDamage = 3.0F; + cellMolten.mHeatDamage = 3; + cellPlasma.mHeatDamage = 6.0F; + + block.ignoreMaterials( + Materials.Ice, + Materials.Snow, + Materials.Concrete, + Materials.Glass, + Materials.Glowstone, + Materials.DarkIron, + Materials.Marble, + Materials.Quartz, + Materials.CertusQuartz, + Materials.Limestone); + ingot.ignoreMaterials(Materials.Brick, Materials.NetherBrick); + + dust.addFamiliarPrefix(dustTiny); + dust.addFamiliarPrefix(dustSmall); + dustTiny.addFamiliarPrefix(dust); + dustTiny.addFamiliarPrefix(dustSmall); + dustSmall.addFamiliarPrefix(dust); + dustSmall.addFamiliarPrefix(dustTiny); + + ingot.addFamiliarPrefix(nugget); + nugget.addFamiliarPrefix(ingot); + + for (OrePrefixes tPrefix1 : values()) if (tPrefix1.name() + .startsWith("ore")) + for (OrePrefixes tPrefix2 : values()) if (tPrefix2.name() + .startsWith("ore")) tPrefix1.addFamiliarPrefix(tPrefix2); + + // These are only the important ones. + gem.mNotGeneratedItems.add(Materials.Coal); + gem.mNotGeneratedItems.add(Materials.Charcoal); + gem.mNotGeneratedItems.add(Materials.NetherStar); + gem.mNotGeneratedItems.add(Materials.Diamond); + gem.mNotGeneratedItems.add(Materials.Emerald); + gem.mNotGeneratedItems.add(Materials.NetherQuartz); + gem.mNotGeneratedItems.add(Materials.EnderPearl); + gem.mNotGeneratedItems.add(Materials.EnderEye); + gem.mNotGeneratedItems.add(Materials.Flint); + gem.mNotGeneratedItems.add(Materials.Lapis); + dust.mNotGeneratedItems.add(Materials.Bone); + dust.mNotGeneratedItems.add(Materials.Redstone); + dust.mNotGeneratedItems.add(Materials.Glowstone); + dust.mNotGeneratedItems.add(Materials.Gunpowder); + dust.mNotGeneratedItems.add(Materials.Sugar); + dust.mNotGeneratedItems.add(Materials.Blaze); + stick.mNotGeneratedItems.add(Materials.Wood); + stick.mNotGeneratedItems.add(Materials.Bone); + stick.mNotGeneratedItems.add(Materials.Blaze); + ingot.mNotGeneratedItems.add(Materials.Iron); + ingot.mNotGeneratedItems.add(Materials.Gold); + ingot.mNotGeneratedItems.add(Materials.Brick); + ingot.mNotGeneratedItems.add(Materials.BrickNether); + ingot.mNotGeneratedItems.add(Materials.WoodSealed); + ingot.mNotGeneratedItems.add(Materials.Wood); + + frame.mNotGeneratedItems.add(MaterialsUEVplus.Universium); + frameGt.mNotGeneratedItems.add(MaterialsUEVplus.Universium); + + plateDouble.mNotGeneratedItems.add(MaterialsUEVplus.MagnetohydrodynamicallyConstrainedStarMatter); + plateTriple.mNotGeneratedItems.add(MaterialsUEVplus.MagnetohydrodynamicallyConstrainedStarMatter); + plateQuadruple.mNotGeneratedItems.add(MaterialsUEVplus.MagnetohydrodynamicallyConstrainedStarMatter); + plateQuintuple.mNotGeneratedItems.add(MaterialsUEVplus.MagnetohydrodynamicallyConstrainedStarMatter); + cell.mNotGeneratedItems.add(MaterialsUEVplus.MagnetohydrodynamicallyConstrainedStarMatter); + ingotDouble.mNotGeneratedItems.add(MaterialsUEVplus.MagnetohydrodynamicallyConstrainedStarMatter); + ingotTriple.mNotGeneratedItems.add(MaterialsUEVplus.MagnetohydrodynamicallyConstrainedStarMatter); + ingotQuadruple.mNotGeneratedItems.add(MaterialsUEVplus.MagnetohydrodynamicallyConstrainedStarMatter); + ingotQuintuple.mNotGeneratedItems.add(MaterialsUEVplus.MagnetohydrodynamicallyConstrainedStarMatter); + turbineBlade.mNotGeneratedItems.add(MaterialsUEVplus.MagnetohydrodynamicallyConstrainedStarMatter); + dust.mNotGeneratedItems.add(MaterialsUEVplus.MagnetohydrodynamicallyConstrainedStarMatter); + dustSmall.mNotGeneratedItems.add(MaterialsUEVplus.MagnetohydrodynamicallyConstrainedStarMatter); + dustTiny.mNotGeneratedItems.add(MaterialsUEVplus.MagnetohydrodynamicallyConstrainedStarMatter); + + // ingot.mNotGeneratedItems.add(Materials.Ichorium); + nugget.mNotGeneratedItems.add(Materials.Gold); + plate.mNotGeneratedItems.add(Materials.Paper); + cell.mNotGeneratedItems.add(Materials.Empty); + cell.mNotGeneratedItems.add(Materials.Water); + cell.mNotGeneratedItems.add(Materials.Lava); + cell.mNotGeneratedItems.add(Materials.ConstructionFoam); + cell.mNotGeneratedItems.add(Materials.UUMatter); + cell.mNotGeneratedItems.add(Materials.CoalFuel); + bucket.mNotGeneratedItems.add(Materials.Empty); + bucket.mNotGeneratedItems.add(Materials.Lava); + bucket.mNotGeneratedItems.add(Materials.Milk); + bucket.mNotGeneratedItems.add(Materials.Water); + bucketClay.mNotGeneratedItems.add(Materials.Empty); + bucketClay.mNotGeneratedItems.add(Materials.Lava); + bucketClay.mNotGeneratedItems.add(Materials.Milk); + bucketClay.mNotGeneratedItems.add(Materials.Water); + bottle.mNotGeneratedItems.add(Materials.Empty); + bottle.mNotGeneratedItems.add(Materials.Water); + bottle.mNotGeneratedItems.add(Materials.Milk); + block.mNotGeneratedItems.add(Materials.Iron); + block.mNotGeneratedItems.add(Materials.Gold); + block.mNotGeneratedItems.add(Materials.Lapis); + block.mNotGeneratedItems.add(Materials.Emerald); + block.mNotGeneratedItems.add(Materials.Redstone); + block.mNotGeneratedItems.add(Materials.Diamond); + block.mNotGeneratedItems.add(Materials.Coal); + toolHeadArrow.mNotGeneratedItems.add(Materials.Glass); + toolHeadArrow.mNotGeneratedItems.add(MaterialsUEVplus.TranscendentMetal); + arrowGtPlastic.mNotGeneratedItems.add(MaterialsUEVplus.TranscendentMetal); + arrow.mNotGeneratedItems.add(MaterialsUEVplus.TranscendentMetal); + arrowGtWood.mNotGeneratedItems.add(MaterialsUEVplus.TranscendentMetal); + stickLong.mNotGeneratedItems.add(Materials.Obsidian); + dust.mNotGeneratedItems.add(Materials.CertusQuartzCharged); + + // ----- + + dustImpure.mGeneratedItems.add(Materials.GraniteRed); + dustImpure.mGeneratedItems.add(Materials.GraniteBlack); + dustImpure.mGeneratedItems.add(Materials.Quartzite); + dustImpure.mGeneratedItems.add(Materials.Flint); + dustImpure.mGeneratedItems.add(Materials.Redrock); + dustImpure.mGeneratedItems.add(Materials.Basalt); + dustImpure.mGeneratedItems.add(Materials.Marble); + dustImpure.mGeneratedItems.add(Materials.Netherrack); + dustImpure.mGeneratedItems.add(Materials.Endstone); + dustImpure.mGeneratedItems.add(Materials.Stone); + + plate.mGeneratedItems.add(Materials.Redstone); + plate.mGeneratedItems.add(Materials.Concrete); + plate.mGeneratedItems.add(Materials.GraniteRed); + plate.mGeneratedItems.add(Materials.GraniteBlack); + plate.mGeneratedItems.add(Materials.Basalt); + plate.mGeneratedItems.add(Materials.Marble); + plate.mGeneratedItems.add(Materials.Glowstone); + plate.mGeneratedItems.add(Materials.Electrotine); + plate.mGeneratedItems.add(Materials.Obsidian); + + ingotHot.mGeneratedItems.add(MaterialsUEVplus.TranscendentMetal); + + plate.mGeneratedItems.add(Materials.Paper); + plateDouble.mGeneratedItems.add(Materials.Paper); + plateTriple.mGeneratedItems.add(Materials.Paper); + plateQuadruple.mGeneratedItems.add(Materials.Paper); + plateQuintuple.mGeneratedItems.add(Materials.Paper); + ring.mGeneratedItems.add(Materials.Paper); + + lens.mGeneratedItems.add(Materials.EnderPearl); + lens.mGeneratedItems.add(Materials.EnderEye); + + stickLong.mGeneratedItems.add(Materials.Blaze); + + nanite.mGeneratedItems.add(Materials.Carbon); + nanite.mGeneratedItems.add(Materials.Gold); + nanite.mGeneratedItems.add(Materials.Iron); + nanite.mGeneratedItems.add(Materials.Copper); + nanite.mGeneratedItems.add(Materials.Silver); + nanite.mGeneratedItems.add(MaterialsUEVplus.TranscendentMetal); + nanite.mGeneratedItems.add(Materials.Neutronium); + nanite.mGeneratedItems.add(MaterialsUEVplus.Universium); + nanite.mGeneratedItems.add(MaterialsUEVplus.WhiteDwarfMatter); + nanite.mGeneratedItems.add(MaterialsUEVplus.BlackDwarfMatter); + nanite.mGeneratedItems.add(Materials.Glowstone); + nanite.mGeneratedItems.add(MaterialsUEVplus.Eternity); + // ----- + + gear.mGeneratedItems.add(MaterialsUEVplus.MagnetohydrodynamicallyConstrainedStarMatter); + ingot.mGeneratedItems.add(MaterialsUEVplus.MagnetohydrodynamicallyConstrainedStarMatter); + toolHeadHammer.mGeneratedItems.add(MaterialsUEVplus.MagnetohydrodynamicallyConstrainedStarMatter); + frame.mGeneratedItems.add(MaterialsUEVplus.MagnetohydrodynamicallyConstrainedStarMatter); + frameGt.mGeneratedItems.add(MaterialsUEVplus.MagnetohydrodynamicallyConstrainedStarMatter); + + dust.mGeneratedItems.addAll(dustPure.mGeneratedItems); + dust.mGeneratedItems.addAll(dustImpure.mGeneratedItems); + dust.mGeneratedItems.addAll(dustRefined.mGeneratedItems); + dustTiny.mGeneratedItems.addAll(dust.mGeneratedItems); + dustSmall.mGeneratedItems.addAll(dust.mGeneratedItems); + crateGtDust.mGeneratedItems.addAll(dust.mGeneratedItems); + crateGtIngot.mGeneratedItems.addAll(ingot.mGeneratedItems); + crateGtGem.mGeneratedItems.addAll(gem.mGeneratedItems); + crateGtPlate.mGeneratedItems.addAll(plate.mGeneratedItems); + // ----- + + toolHeadFile.mCondition = new ICondition.And<>( + new ICondition.Not<>(SubTag.NO_SMASHING), + new ICondition.Not<>(SubTag.BOUNCY)); + toolHeadSaw.mCondition = new ICondition.And<>( + new ICondition.Not<>(SubTag.NO_SMASHING), + new ICondition.Not<>(SubTag.BOUNCY)); + toolHeadDrill.mCondition = new ICondition.And<>( + new ICondition.Not<>(SubTag.NO_SMASHING), + new ICondition.Not<>(SubTag.BOUNCY)); + toolHeadChainsaw.mCondition = new ICondition.And<>( + new ICondition.Not<>(SubTag.NO_SMASHING), + new ICondition.Not<>(SubTag.BOUNCY)); + toolHeadWrench.mCondition = new ICondition.And<>( + new ICondition.Not<>(SubTag.NO_SMASHING), + new ICondition.Not<>(SubTag.BOUNCY)); + toolHeadBuzzSaw.mCondition = new ICondition.And<>( + new ICondition.Not<>(SubTag.NO_SMASHING), + new ICondition.Not<>(SubTag.BOUNCY)); + turbineBlade.mCondition = new ICondition.And<>( + new ICondition.Not<>(SubTag.NO_SMASHING), + new ICondition.Not<>(SubTag.BOUNCY)); + + rotor.mCondition = new ICondition.Nor<>(SubTag.CRYSTAL, SubTag.STONE, SubTag.BOUNCY); + + spring.mCondition = new ICondition.Or<>( + SubTag.STRETCHY, + SubTag.BOUNCY, + new ICondition.Not<>(SubTag.NO_SMASHING)); + springSmall.mCondition = new ICondition.Or<>( + SubTag.STRETCHY, + SubTag.BOUNCY, + new ICondition.Not<>(SubTag.NO_SMASHING)); + + gemChipped.mCondition = new ICondition.And<>( + SubTag.TRANSPARENT, + SubTag.CRYSTAL, + new ICondition.Not<>(SubTag.QUARTZ), + new ICondition.Not<>(SubTag.PEARL), + new ICondition.Not<>(SubTag.MAGICAL)); + gemFlawed.mCondition = new ICondition.And<>( + SubTag.TRANSPARENT, + SubTag.CRYSTAL, + new ICondition.Not<>(SubTag.QUARTZ), + new ICondition.Not<>(SubTag.PEARL), + new ICondition.Not<>(SubTag.MAGICAL)); + gemFlawless.mCondition = new ICondition.And<>( + SubTag.TRANSPARENT, + SubTag.CRYSTAL, + new ICondition.Not<>(SubTag.QUARTZ), + new ICondition.Not<>(SubTag.PEARL), + new ICondition.Not<>(SubTag.MAGICAL)); + gemExquisite.mCondition = new ICondition.And<>( + SubTag.TRANSPARENT, + SubTag.CRYSTAL, + new ICondition.Not<>(SubTag.QUARTZ), + new ICondition.Not<>(SubTag.PEARL), + new ICondition.Not<>(SubTag.MAGICAL)); + + lens.mCondition = new ICondition.Or<>( + SubTag.MAGICAL, + new ICondition.And<>(SubTag.TRANSPARENT, SubTag.HAS_COLOR)); + + plateDouble.mCondition = new ICondition.Or<>( + SubTag.PAPER, + new ICondition.Not<>(SubTag.NO_SMASHING), + SubTag.STRETCHY); + plateTriple.mCondition = new ICondition.Or<>( + SubTag.PAPER, + new ICondition.Not<>(SubTag.NO_SMASHING), + SubTag.STRETCHY); + plateQuadruple.mCondition = new ICondition.Or<>( + SubTag.PAPER, + new ICondition.Not<>(SubTag.NO_SMASHING), + SubTag.STRETCHY); + plateQuintuple.mCondition = new ICondition.Or<>( + SubTag.PAPER, + new ICondition.Not<>(SubTag.NO_SMASHING), + SubTag.STRETCHY); + + plateDense.mCondition = new ICondition.Or<>(new ICondition.Not<>(SubTag.NO_SMASHING), SubTag.STRETCHY); + + ingotDouble.mCondition = new ICondition.Or<>(new ICondition.Not<>(SubTag.NO_SMASHING), SubTag.STRETCHY); + ingotTriple.mCondition = new ICondition.Or<>(new ICondition.Not<>(SubTag.NO_SMASHING), SubTag.STRETCHY); + ingotQuadruple.mCondition = new ICondition.Or<>(new ICondition.Not<>(SubTag.NO_SMASHING), SubTag.STRETCHY); + ingotQuintuple.mCondition = new ICondition.Or<>(new ICondition.Not<>(SubTag.NO_SMASHING), SubTag.STRETCHY); + + wireFine.mCondition = SubTag.METAL; + + // ----- + + pipeRestrictiveTiny.mSecondaryMaterial = new MaterialStack(Materials.Steel, ring.mMaterialAmount); + pipeRestrictiveSmall.mSecondaryMaterial = new MaterialStack(Materials.Steel, ring.mMaterialAmount * 2); + pipeRestrictiveMedium.mSecondaryMaterial = new MaterialStack(Materials.Steel, ring.mMaterialAmount * 3); + pipeRestrictiveLarge.mSecondaryMaterial = new MaterialStack(Materials.Steel, ring.mMaterialAmount * 4); + pipeRestrictiveHuge.mSecondaryMaterial = new MaterialStack(Materials.Steel, ring.mMaterialAmount * 5); + cableGt16.mSecondaryMaterial = new MaterialStack(Materials.Rubber, plate.mMaterialAmount * 5); + cableGt12.mSecondaryMaterial = new MaterialStack(Materials.Rubber, plate.mMaterialAmount * 4); + cableGt08.mSecondaryMaterial = new MaterialStack(Materials.Rubber, plate.mMaterialAmount * 3); + cableGt04.mSecondaryMaterial = new MaterialStack(Materials.Rubber, plate.mMaterialAmount * 2); + cableGt02.mSecondaryMaterial = new MaterialStack(Materials.Rubber, plate.mMaterialAmount); + cableGt01.mSecondaryMaterial = new MaterialStack(Materials.Rubber, plate.mMaterialAmount); + bucket.mSecondaryMaterial = new MaterialStack(Materials.Iron, ingot.mMaterialAmount * 3); + bucketClay.mSecondaryMaterial = new MaterialStack(Materials.Clay, dust.mMaterialAmount * 5); + CELL_TYPES + .forEach(prefix -> prefix.mSecondaryMaterial = new MaterialStack(Materials.Tin, plate.mMaterialAmount * 2)); + oreRedgranite.mSecondaryMaterial = new MaterialStack(Materials.GraniteRed, dust.mMaterialAmount); + oreBlackgranite.mSecondaryMaterial = new MaterialStack(Materials.GraniteBlack, dust.mMaterialAmount); + oreNetherrack.mSecondaryMaterial = new MaterialStack(Materials.Netherrack, dust.mMaterialAmount); + oreNether.mSecondaryMaterial = new MaterialStack(Materials.Netherrack, dust.mMaterialAmount); + oreEndstone.mSecondaryMaterial = new MaterialStack(Materials.Endstone, dust.mMaterialAmount); + oreEnd.mSecondaryMaterial = new MaterialStack(Materials.Endstone, dust.mMaterialAmount); + oreMarble.mSecondaryMaterial = new MaterialStack(Materials.Marble, dust.mMaterialAmount); + oreBasalt.mSecondaryMaterial = new MaterialStack(Materials.Basalt, dust.mMaterialAmount); + oreDense.mSecondaryMaterial = new MaterialStack(Materials.Stone, dust.mMaterialAmount); + orePoor.mSecondaryMaterial = new MaterialStack(Materials.Stone, dust.mMaterialAmount * 2); + oreSmall.mSecondaryMaterial = new MaterialStack(Materials.Stone, dust.mMaterialAmount * 2); + oreNormal.mSecondaryMaterial = new MaterialStack(Materials.Stone, dust.mMaterialAmount * 2); + rawOre.mSecondaryMaterial = new MaterialStack(Materials.Stone, dust.mMaterialAmount); + oreRich.mSecondaryMaterial = new MaterialStack(Materials.Stone, dust.mMaterialAmount * 2); + ore.mSecondaryMaterial = new MaterialStack(Materials.Stone, dust.mMaterialAmount); + crushed.mSecondaryMaterial = new MaterialStack(Materials.Stone, dust.mMaterialAmount); + toolHeadChainsaw.mSecondaryMaterial = new MaterialStack( + Materials.Steel, + plate.mMaterialAmount * 4 + ring.mMaterialAmount * 2); + toolHeadWrench.mSecondaryMaterial = new MaterialStack( + Materials.Steel, + ring.mMaterialAmount + screw.mMaterialAmount * 2); + arrowGtWood.mSecondaryMaterial = new MaterialStack(Materials.Wood, stick.mMaterialAmount); + arrowGtPlastic.mSecondaryMaterial = new MaterialStack(Materials.Plastic, stick.mMaterialAmount); + bulletGtSmall.mSecondaryMaterial = new MaterialStack(Materials.Brass, ingot.mMaterialAmount / 9); + bulletGtMedium.mSecondaryMaterial = new MaterialStack(Materials.Brass, ingot.mMaterialAmount / 6); + bulletGtLarge.mSecondaryMaterial = new MaterialStack(Materials.Brass, ingot.mMaterialAmount / 3); + } + + public final ArrayList<ItemStack> mPrefixedItems = new GT_ArrayList<>(false, 16); + public final short mTextureIndex; + public final String mRegularLocalName, mLocalizedMaterialPre, mLocalizedMaterialPost; + public final boolean mIsUsedForOreProcessing, mIsEnchantable, mIsUnificatable, mIsMaterialBased, mIsSelfReferencing, + mIsContainer, mDontUnificateActively, mIsUsedForBlocks, mAllowNormalRecycling, mGenerateDefaultItem; + public final List<TC_AspectStack> mAspects = new ArrayList<>(); + public final Collection<OrePrefixes> mFamiliarPrefixes = new HashSet<>(); + /** + * Used to determine the amount of Material this Prefix contains. Multiply or Divide GregTech_API.MATERIAL_UNIT to + * get the Amounts in comparision to one Ingot. 0 = Null Negative = Undefined Amount + */ + public final long mMaterialAmount; + + public final Collection<Materials> mDisabledItems = new HashSet<>(), mNotGeneratedItems = new HashSet<>(), + mIgnoredMaterials = new HashSet<>(), mGeneratedItems = new HashSet<>(); + private final ArrayList<IOreRecipeRegistrator> mOreProcessing = new ArrayList<>(); + public ItemStack mContainerItem = null; + public ICondition<ISubTagContainer> mCondition = null; + public byte mDefaultStackSize = 64; + public MaterialStack mSecondaryMaterial = null; + public OrePrefixes mPrefixInto = this; + public float mHeatDamage = 0.0F; // Negative for Frost Damage + private final ObjectSet<ItemStack> mContainsTestCache = new ObjectOpenCustomHashSet<>( + 512, + 0.5f, + GT_ItemStack.ITEMSTACK_HASH_STRATEGY2); + public static final List<OrePrefixes> mPreventableComponents = new LinkedList<>( + Arrays.asList( + OrePrefixes.gem, + OrePrefixes.ingotHot, + OrePrefixes.ingotDouble, + OrePrefixes.ingotTriple, + OrePrefixes.ingotQuadruple, + OrePrefixes.ingotQuintuple, + OrePrefixes.plate, + OrePrefixes.plateDouble, + OrePrefixes.plateTriple, + OrePrefixes.plateQuadruple, + OrePrefixes.plateQuintuple, + OrePrefixes.plateDense, + OrePrefixes.stick, + OrePrefixes.round, + OrePrefixes.bolt, + OrePrefixes.screw, + OrePrefixes.ring, + OrePrefixes.foil, + OrePrefixes.toolHeadSword, + OrePrefixes.toolHeadPickaxe, + OrePrefixes.toolHeadShovel, + OrePrefixes.toolHeadAxe, + OrePrefixes.toolHeadHoe, + OrePrefixes.toolHeadHammer, + OrePrefixes.toolHeadFile, + OrePrefixes.toolHeadSaw, + OrePrefixes.toolHeadDrill, + OrePrefixes.toolHeadChainsaw, + OrePrefixes.toolHeadWrench, + OrePrefixes.toolHeadUniversalSpade, + OrePrefixes.toolHeadSense, + OrePrefixes.toolHeadPlow, + OrePrefixes.toolHeadArrow, + OrePrefixes.toolHeadBuzzSaw, + OrePrefixes.turbineBlade, + OrePrefixes.wireFine, + OrePrefixes.gearGtSmall, + OrePrefixes.rotor, + OrePrefixes.stickLong, + OrePrefixes.springSmall, + OrePrefixes.spring, + OrePrefixes.arrowGtWood, + OrePrefixes.arrowGtPlastic, + OrePrefixes.gemChipped, + OrePrefixes.gemFlawed, + OrePrefixes.gemFlawless, + OrePrefixes.gemExquisite, + OrePrefixes.gearGt, + OrePrefixes.crateGtDust, + OrePrefixes.crateGtIngot, + OrePrefixes.crateGtGem, + OrePrefixes.crateGtPlate, + OrePrefixes.itemCasing, + OrePrefixes.nanite)); + /** + * Yes this Value can be changed to add Bits for the MetaGenerated-Item-Check. + */ + public int mMaterialGenerationBits = 0; + + OrePrefixes(String aRegularLocalName, String aLocalizedMaterialPre, String aLocalizedMaterialPost, + boolean aIsUnificatable, boolean aIsMaterialBased, boolean aIsSelfReferencing, boolean aIsContainer, + boolean aDontUnificateActively, boolean aIsUsedForBlocks, boolean aAllowNormalRecycling, + boolean aGenerateDefaultItem, boolean aIsEnchantable, boolean aIsUsedForOreProcessing, + int aMaterialGenerationBits, long aMaterialAmount, int aDefaultStackSize, int aTextureindex) { + mIsUnificatable = aIsUnificatable; + mIsMaterialBased = aIsMaterialBased; + mIsSelfReferencing = aIsSelfReferencing; + mIsContainer = aIsContainer; + mDontUnificateActively = aDontUnificateActively; + mIsUsedForBlocks = aIsUsedForBlocks; + mAllowNormalRecycling = aAllowNormalRecycling; + mGenerateDefaultItem = aGenerateDefaultItem; + mIsEnchantable = aIsEnchantable; + mIsUsedForOreProcessing = aIsUsedForOreProcessing; + mMaterialGenerationBits = aMaterialGenerationBits; + mMaterialAmount = aMaterialAmount; + mRegularLocalName = aRegularLocalName; + mLocalizedMaterialPre = aLocalizedMaterialPre; + mLocalizedMaterialPost = aLocalizedMaterialPost; + mDefaultStackSize = (byte) aDefaultStackSize; + mTextureIndex = (short) aTextureindex; + + if (name().startsWith("ore")) { + new TC_AspectStack(TC_Aspects.TERRA, 1).addToAspectList(mAspects); + } else if (name().startsWith("wire") || name().startsWith("cable")) { + new TC_AspectStack(TC_Aspects.ELECTRUM, 1).addToAspectList(mAspects); + } else if (name().startsWith("dust")) { + new TC_AspectStack(TC_Aspects.PERDITIO, 1).addToAspectList(mAspects); + } else if (name().startsWith("crushed")) { + new TC_AspectStack(TC_Aspects.PERFODIO, 1).addToAspectList(mAspects); + } else if (name().startsWith("ingot") || name().startsWith("nugget")) { + new TC_AspectStack(TC_Aspects.METALLUM, 1).addToAspectList(mAspects); + } else if (name().startsWith("armor")) { + new TC_AspectStack(TC_Aspects.TUTAMEN, 1).addToAspectList(mAspects); + } else if (name().startsWith("stone")) { + new TC_AspectStack(TC_Aspects.TERRA, 1).addToAspectList(mAspects); + } else if (name().startsWith("pipe")) { + new TC_AspectStack(TC_Aspects.ITER, 1).addToAspectList(mAspects); + } else if (name().startsWith("gear")) { + new TC_AspectStack(TC_Aspects.MOTUS, 1).addToAspectList(mAspects); + new TC_AspectStack(TC_Aspects.MACHINA, 1).addToAspectList(mAspects); + } else if (name().startsWith("frame") || name().startsWith("plate")) { + new TC_AspectStack(TC_Aspects.FABRICO, 1).addToAspectList(mAspects); + } else if (name().startsWith("tool")) { + new TC_AspectStack(TC_Aspects.INSTRUMENTUM, 2).addToAspectList(mAspects); + } else if (name().startsWith("gem") || name().startsWith("crystal") || name().startsWith("lens")) { + new TC_AspectStack(TC_Aspects.VITREUS, 1).addToAspectList(mAspects); + } else if (name().startsWith("crate")) { + new TC_AspectStack(TC_Aspects.ITER, 2).addToAspectList(mAspects); + } else if (name().startsWith("circuit")) { + new TC_AspectStack(TC_Aspects.COGNITIO, 1).addToAspectList(mAspects); + } else if (name().startsWith("computer")) { + new TC_AspectStack(TC_Aspects.COGNITIO, 4).addToAspectList(mAspects); + } else if (name().startsWith("battery")) { + new TC_AspectStack(TC_Aspects.ELECTRUM, 1).addToAspectList(mAspects); + } + } + + public static boolean isInstanceOf(String aName, OrePrefixes aPrefix) { + return aName != null && aName.startsWith(aPrefix.toString()); + } + + public void disableComponent(Materials aMaterial) { + if (!this.mDisabledItems.contains(aMaterial)) this.mDisabledItems.add(aMaterial); + } + + public static OrePrefixes getOrePrefix(String aOre) { + for (OrePrefixes tPrefix : values()) if (aOre.startsWith(tPrefix.toString())) { + if (tPrefix == oreNether && aOre.equals("oreNetherQuartz")) return ore; + if (tPrefix == oreNether && aOre.equals("oreNetherStar")) return ore; + if (tPrefix == oreBasalt && aOre.equals("oreBasalticMineralSand")) return ore; + if (tPrefix == stickLong && aOre.equals("stickLongasssuperconductornameforuvwire")) return stick; + if (tPrefix == stickLong && aOre.equals("stickLongasssuperconductornameforuhvwire")) return stick; + return tPrefix; + } + return null; + } + + public static String stripPrefix(String aOre) { + for (OrePrefixes tPrefix : values()) { + if (aOre.startsWith(tPrefix.toString())) { + return aOre.replaceFirst(tPrefix.toString(), ""); + } + } + return aOre; + } + + public static String replacePrefix(String aOre, OrePrefixes aPrefix) { + for (OrePrefixes tPrefix : values()) { + if (aOre.startsWith(tPrefix.toString())) { + return aOre.replaceFirst(tPrefix.toString(), aPrefix.toString()); + } + } + return ""; + } + + public static OrePrefixes getPrefix(String aPrefixName) { + return getPrefix(aPrefixName, null); + } + + public static OrePrefixes getPrefix(String aPrefixName, OrePrefixes aReplacement) { + Object tObject = GT_Utility.getFieldContent(OrePrefixes.class, aPrefixName, false, false); + if (tObject instanceof OrePrefixes) return (OrePrefixes) tObject; + return aReplacement; + } + + public static Materials getMaterial(String aOre) { + return Materials.get(stripPrefix(aOre)); + } + + public static Materials getMaterial(String aOre, OrePrefixes aPrefix) { + return Materials.get(aOre.replaceFirst(aPrefix.toString(), "")); + } + + public static Materials getRealMaterial(String aOre, OrePrefixes aPrefix) { + return Materials.getRealMaterial(aOre.replaceFirst(aPrefix.toString(), "")); + } + + public void enableComponent(Materials aMaterial) { + this.mDisabledItems.remove(aMaterial); + } + + public boolean add(ItemStack aStack) { + if (aStack == null) return false; + if (!contains(aStack)) { + mPrefixedItems.add(aStack); + // It's now in there... so update the cache + mContainsTestCache.add(aStack); + } + return true; + } + + public boolean contains(ItemStack aStack) { + return !GT_Utility.isStackInvalid(aStack) && mContainsTestCache.contains(aStack); + } + + public boolean containsUnCached(ItemStack aStack) { + // In case someone needs this + for (ItemStack tStack : mPrefixedItems) { + if (GT_Utility.areStacksEqual(aStack, tStack, !tStack.hasTagCompound())) { + return true; + } + } + return false; + } + + public boolean doGenerateItem(Materials aMaterial) { + return aMaterial != null && aMaterial != Materials._NULL + && ((aMaterial.mTypes & mMaterialGenerationBits) != 0 || mGeneratedItems.contains(aMaterial)) + && !mNotGeneratedItems.contains(aMaterial) + && !mDisabledItems.contains(aMaterial) + && (mCondition == null || mCondition.isTrue(aMaterial)); + } + + public boolean ignoreMaterials(Materials... aMaterials) { + for (Materials tMaterial : aMaterials) if (tMaterial != null) mIgnoredMaterials.add(tMaterial); + return true; + } + + public boolean isIgnored(Materials aMaterial) { + if (aMaterial != null && (!aMaterial.mUnificatable || aMaterial != aMaterial.mMaterialInto)) return true; + return mIgnoredMaterials.contains(aMaterial); + } + + public boolean addFamiliarPrefix(OrePrefixes aPrefix) { + if (aPrefix == null || mFamiliarPrefixes.contains(aPrefix) || aPrefix == this) return false; + return mFamiliarPrefixes.add(aPrefix); + } + + public boolean add(IOreRecipeRegistrator aRegistrator) { + if (aRegistrator == null) return false; + return mOreProcessing.add(aRegistrator); + } + + public void processOre(Materials aMaterial, String aOreDictName, String aModName, ItemStack aStack) { + + if (aMaterial == null) { + return; + } + + if (aMaterial.contains(SubTag.NO_RECIPES)) { + return; + } + + if ((aMaterial != Materials._NULL || mIsSelfReferencing || !mIsMaterialBased) + && GT_Utility.isStackValid(aStack)) { + // if (Materials.mPreventableComponents.contains(this) && !this.mDynamicItems.contains(aMaterial)) return; + for (IOreRecipeRegistrator tRegistrator : mOreProcessing) { + if (D2) GT_Log.ore.println( + "Processing '" + aOreDictName + + "' with the Prefix '" + + name() + + "' and the Material '" + + aMaterial.mName + + "' at " + + GT_Utility.getClassName(tRegistrator)); + tRegistrator.registerOre(this, aMaterial, aOreDictName, aModName, GT_Utility.copyAmount(1, aStack)); + } + } + } + + public Object get(Object aMaterial) { + if (aMaterial instanceof Materials) return new ItemData(this, (Materials) aMaterial); + return name() + aMaterial; + } + + public String getDefaultLocalNameForItem(Materials aMaterial) { + return aMaterial.getDefaultLocalizedNameForItem(getDefaultLocalNameFormatForItem(aMaterial)); + } + + @SuppressWarnings("incomplete-switch") + public String getDefaultLocalNameFormatForItem(Materials aMaterial) { + // Certain Materials have slightly different Localizations. + switch (this) { + case crateGtDust -> { + return mLocalizedMaterialPre + OrePrefixes.dust.getDefaultLocalNameFormatForItem(aMaterial); + } + case crateGtIngot -> { + return mLocalizedMaterialPre + OrePrefixes.ingot.getDefaultLocalNameFormatForItem(aMaterial); + } + case crateGtGem -> { + return mLocalizedMaterialPre + OrePrefixes.gem.getDefaultLocalNameFormatForItem(aMaterial); + } + case crateGtPlate -> { + return mLocalizedMaterialPre + OrePrefixes.plate.getDefaultLocalNameFormatForItem(aMaterial); + } + } + switch (aMaterial.mName) { + case "Glass", "BorosilicateGlass" -> { + if (name().startsWith("gem")) return mLocalizedMaterialPre + "%material" + " Crystal"; + if (name().startsWith("plate")) return mLocalizedMaterialPre + "%material" + " Pane"; + if (name().startsWith("ingot")) return mLocalizedMaterialPre + "%material" + " Bar"; + if (name().startsWith("nugget")) return mLocalizedMaterialPre + "%material" + " Chip"; + } + case "Wheat" -> { + if (name().startsWith("dust")) return mLocalizedMaterialPre + "Flour"; + } + case "Ice" -> { + if (name().startsWith("dust")) return mLocalizedMaterialPre + "Crushed Ice"; + } + case "Wood", "WoodSealed" -> { + if (name().startsWith("bolt")) return "Short " + "%material" + " Stick"; + if (name().startsWith("stick")) return mLocalizedMaterialPre + "%material" + " Stick"; + if (name().startsWith("dust")) return mLocalizedMaterialPre + "%material" + " Pulp"; + if (name().startsWith("nugget")) return mLocalizedMaterialPre + "%material" + " Chip"; + if (name().startsWith("plate")) return mLocalizedMaterialPre + "%material" + " Plank"; + } + case "Plastic", "Rubber", "Polyethylene", "Epoxid", "EpoxidFiberReinforced", "Polydimethylsiloxane", "Silicone", "Polysiloxane", "Polycaprolactam", "Polytetrafluoroethylene", "PolyvinylChloride", "Polystyrene", "StyreneButadieneRubber" -> { + if (name().startsWith("dust")) return mLocalizedMaterialPre + "%material" + " Pulp"; + if (name().startsWith("plate")) return mLocalizedMaterialPre + "%material" + " Sheet"; + if (name().startsWith("ingot")) return mLocalizedMaterialPre + "%material" + " Bar"; + if (name().startsWith("nugget")) return mLocalizedMaterialPre + "%material" + " Chip"; + if (name().startsWith("foil")) return "Thin " + "%material" + " Sheet"; + } + case "FierySteel" -> { + if (mIsContainer) return mLocalizedMaterialPre + "Fiery Blood" + mLocalizedMaterialPost; + } + case "Steeleaf" -> { + if (name().startsWith("ingot")) return mLocalizedMaterialPre + "%material"; + } + case "Bone" -> { + if (name().startsWith("dust")) return mLocalizedMaterialPre + "Bone Meal"; + } + case "Blaze", "Milk", "Cocoa", "Chocolate", "Coffee", "Chili", "Cheese", "Snow" -> { + if (name().startsWith("dust")) return mLocalizedMaterialPre + "%material" + " Powder"; + } + case "Paper" -> { + if (name().startsWith("dust")) return mLocalizedMaterialPre + "Chad"; + switch (this) { + case plate -> { + return "Sheet of Paper"; + } + case plateDouble -> { + return "Paperboard"; + } + case plateTriple -> { + return "Carton"; + } + case plateQuadruple -> { + return "Cardboard"; + } + case plateQuintuple -> { + return "Thick Cardboard"; + } + case plateDense -> { + return "Strong Cardboard"; + } + } + } + case "MeatRaw" -> { + if (name().startsWith("dust")) return mLocalizedMaterialPre + "Mince Meat"; + } + case "MeatCooked" -> { + if (name().startsWith("dust")) return mLocalizedMaterialPre + "Cooked Mince Meat"; + } + case "Ash", "DarkAsh", "Gunpowder", "Sugar", "Salt", "RockSalt", "VolcanicAsh", "RareEarth" -> { + if (name().startsWith("dust")) return mLocalizedMaterialPre + "%material"; + } + case "Vermiculite", "Bentonite", "Kaolinite", "Talc", "BasalticMineralSand", "GraniticMineralSand", "GlauconiteSand", "CassiteriteSand", "GarnetSand", "QuartzSand", "Pitchblende", "FullersEarth" -> { + if (name().startsWith("dust")) return mLocalizedMaterialPre + "%material"; + switch (this) { + case crushedCentrifuged, crushedPurified -> { + return mLocalizedMaterialPre + "%material"; + } + case crushed -> { + return "Ground " + "%material"; + } + } + } + } + if (ProcessingModSupport.aEnableThaumcraftMats) { + switch (aMaterial.mName) { + case "InfusedAir", "InfusedDull", "InfusedEarth", "InfusedEntropy", "InfusedFire", "InfusedOrder", "InfusedVis", "InfusedWater" -> { + if (name().startsWith("gem")) return mLocalizedMaterialPre + "Shard of " + "%material"; + if (name().startsWith("crystal")) return mLocalizedMaterialPre + "Shard of " + "%material"; + if (name().startsWith("plate")) return mLocalizedMaterialPre + "%material" + " Crystal Plate"; + if (name().startsWith("dust")) return mLocalizedMaterialPre + "%material" + " Crystal Powder"; + switch (this) { + case crushedCentrifuged, crushedPurified, crushed -> { + return mLocalizedMaterialPre + "%material" + " Crystals"; + } + } + } + } + } + // Use Standard Localization + return mLocalizedMaterialPre + "%material" + mLocalizedMaterialPost; + } +} diff --git a/src/main/java/gregtech/api/enums/ParticleFX.java b/src/main/java/gregtech/api/enums/ParticleFX.java new file mode 100644 index 0000000000..c692598b89 --- /dev/null +++ b/src/main/java/gregtech/api/enums/ParticleFX.java @@ -0,0 +1,53 @@ +package gregtech.api.enums; + +/** + * Enumerates known EntityFX particles + */ +public enum ParticleFX { + + HUGE_EXPLOSION("hugeexplosion"), + LARGE_EXPLODE("largeexplode"), + FIREWORKS_SPARK("fireworksSpark"), + BUBBLE("bubble"), + SUSPENDED("suspended"), + DEPTH_SUSPEND("depthsuspend"), + TOWN_AURA("townaura"), + CRIT("crit"), + MAGIC_CRIT("magicCrit"), + SMOKE("smoke"), + MOB_SPELL("mobSpell"), + MOB_SPELL_AMBIENT("mobSpellAmbient"), + SPELL("spell"), + INSTANT_SPELL("instantSpell"), + WITCH_MAGIC("witchMagic"), + NOTE("note"), + PORTAL("portal"), + ENCHANTMENT_TABLE("enchantmenttable"), + EXPLODE("explode"), + FLAME("flame"), + LAVA("lava"), + FOOTSTEP("footstep"), + SPLASH("splash"), + WAKE("wake"), + LARGE_SMOKE("largesmoke"), + CLOUD("cloud"), + RED_DUST("reddust"), + SNOWBALL_POOF("snowballpoof"), + DRIP_WATER("dripWater"), + DRIP_LAVA("dripLava"), + SNOW_SHOVEL("snowshovel"), + SLIME("slime"), + HEART("heart"), + ANGRY_VILLAGER("angryVillager"), + HAPPY_VILLAGER("happyVillager"); + + private final String identifier; + + ParticleFX(String name) { + this.identifier = name; + } + + public String toString() { + return this.identifier; + } +} diff --git a/src/main/java/gregtech/api/enums/SoundResource.java b/src/main/java/gregtech/api/enums/SoundResource.java new file mode 100644 index 0000000000..23887aebbf --- /dev/null +++ b/src/main/java/gregtech/api/enums/SoundResource.java @@ -0,0 +1,373 @@ +package gregtech.api.enums; + +import static gregtech.api.enums.Mods.GregTech; +import static gregtech.api.enums.Mods.IndustrialCraft2; + +import java.util.EnumSet; +import java.util.Locale; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import net.minecraft.util.ResourceLocation; + +import com.google.common.collect.Maps; + +/** + * Enumerates known sounds with id and resource-location + * + * <p> + * Note that the id serve no specific purpose, if for legacy compatibility of a plausible yet unimplemented network + * packet weight optimization. + * </p> + */ +public enum SoundResource { + + RANDOM_BREAK(0, "random.break"), + RANDOM_ANVIL_USE(1, "random.anvil_use"), + RANDOM_ANVIL_BREAK(2, "random.anvil_break"), + RANDOM_CLICK(3, "random.click"), + RANDOM_FIZZ(4, "random.fizz"), + RANDOM_EXPLODE(5, "random.explode"), + FIRE_IGNITE(6, "fire.ignite"), + + IC2_TOOLS_WRENCH(100, IndustrialCraft2.ID, "tools.Wrench"), + IC2_TOOLS_RUBBER_TRAMPOLINE(101, IndustrialCraft2.ID, "tools.RubberTrampoline"), + IC2_TOOLS_PAINTER(102, IndustrialCraft2.ID, "tools.Painter"), + IC2_TOOLS_BATTERY_USE(103, IndustrialCraft2.ID, "tools.BatteryUse"), + IC2_TOOLS_CHAINSAW_CHAINSAW_USE_ONE(104, IndustrialCraft2.ID, "tools.chainsaw.ChainsawUseOne"), + IC2_TOOLS_CHAINSAW_CHAINSAW_USE_TWO(105, IndustrialCraft2.ID, "tools.chainsaw.ChainsawUseTwo"), + IC2_TOOLS_DRILL_DRILL_SOFT(106, IndustrialCraft2.ID, "tools.drill.DrillSoft"), + IC2_TOOLS_DRILL_DRILL_HARD(107, IndustrialCraft2.ID, "tools.drill.DrillHard"), + IC2_TOOLS_OD_SCANNER(108, IndustrialCraft2.ID, "tools.ODScanner"), + IC2_TOOLS_INSULATION_CUTTERS(109, IndustrialCraft2.ID, "tools.InsulationCutters"), + + IC2_MACHINES_EXTRACTOR_OP(200, IndustrialCraft2.ID, "machines.ExtractorOp"), + IC2_MACHINES_MACERATOR_OP(201, IndustrialCraft2.ID, "machines.MaceratorOp"), + IC2_MACHINES_INDUCTION_LOOP(202, IndustrialCraft2.ID, "machines.InductionLoop"), + IC2_MACHINES_COMPRESSOR_OP(203, IndustrialCraft2.ID, "machines.CompressorOp"), + IC2_MACHINES_RECYCLER_OP(204, IndustrialCraft2.ID, "machines.RecyclerOp"), + IC2_MACHINES_MINER_OP(205, IndustrialCraft2.ID, "machines.MinerOp"), + IC2_MACHINES_PUMP_OP(206, IndustrialCraft2.ID, "machines.PumpOp"), + IC2_MACHINES_ELECTROFURNACE_LOOP(207, IndustrialCraft2.ID, "machines.ElectroFurnaceLoop"), + @Deprecated + DEPRECATED_DUPE_OF_IC2_MACHINES_INDUCTION_LOOP(208, IndustrialCraft2.ID, "machines.InductionLoop"), + IC2_MACHINES_MACHINE_OVERLOAD(209, IndustrialCraft2.ID, "machines.MachineOverload"), + IC2_MACHINES_INTERRUPT_ONE(210, IndustrialCraft2.ID, "machines.InterruptOne"), + IC2_MACHINES_KA_CHING(211, IndustrialCraft2.ID, "machines.KaChing"), + IC2_MACHINES_MAGNETIZER_LOOP(212, IndustrialCraft2.ID, "machines.MagnetizerLoop"), + + GT_MACHINES_FUSION_LOOP(230, GregTech.ID, "machines.FusionLoop"), + GT_MACHINES_DISTILLERY_LOOP(231, GregTech.ID, "machines.DistilleryLoop"), + GT_MACHINES_PLASMAFORGE_LOOP(232, GregTech.ID, "machines.PlasmaForgeLoop"), + + GUI_BUTTON_DOWN(-1, GregTech.ID, "gui.buttonDown"), + GUI_BUTTON_UP(-1, GregTech.ID, "gui.buttonUp"), + + /* + * Other Minecraft Sounds that were missing + */ + AMBIENT_CAVE_CAVE(-1, "ambient.cave.cave"), + AMBIENT_WEATHER_RAIN(-1, "ambient.weather.rain"), + AMBIENT_WEATHER_THUNDER(-1, "ambient.weather.thunder"), + DAMAGE_FALLBIG(-1, "damage.fallbig"), + DAMAGE_FALLSMALL(-1, "damage.fallsmall"), + DAMAGE_HIT(-1, "damage.hit"), + DAMAGE_HURTFLESH(-1, "damage.hurtflesh"), + DIG_CLOTH(-1, "dig.cloth"), + DIG_GRASS(-1, "dig.grass"), + DIG_GRAVEL(-1, "dig.gravel"), + DIG_SAND(-1, "dig.sand"), + DIG_SNOW(-1, "dig.snow"), + DIG_STONE(-1, "dig.stone"), + DIG_WOOD(-1, "dig.wood"), + FIRE_FIRE(-1, "fire.fire"), + FIREWORKS_BLAST(-1, "fireworks.blast"), + FIREWORKS_BLAST_FAR(-1, "fireworks.blast_far"), + FIREWORKS_LARGEBLAST(-1, "fireworks.largeBlast"), + FIREWORKS_LARGEBLAST_FAR(-1, "fireworks.largeBlast_far"), + FIREWORKS_LAUNCH(-1, "fireworks.launch"), + FIREWORKS_TWINKLE(-1, "fireworks.twinkle"), + FIREWORKS_TWINKLE_FAR(-1, "fireworks.twinkle_far"), + GAME_NEUTRAL_SWIM(-1, "game.neutral.swim"), + GAME_TNT_PRIMED(-1, "game.tnt.primed"), + LIQUID_LAVA(-1, "liquid.lava"), + LIQUID_LAVAPOP(-1, "liquid.lavapop"), + LIQUID_SPLASH(-1, "liquid.splash"), + LIQUID_SWIM(-1, "liquid.swim"), + LIQUID_WATER(-1, "liquid.water"), + MINECART_BASE(-1, "minecart.base"), + MINECART_INSIDE(-1, "minecart.inside"), + MOB_BAT_DEATH(-1, "mob.bat.death"), + MOB_BAT_HURT(-1, "mob.bat.hurt"), + MOB_BAT_IDLE(-1, "mob.bat.idle"), + MOB_BAT_LOOP(-1, "mob.bat.loop"), + MOB_BAT_TAKEOFF(-1, "mob.bat.takeoff"), + MOB_BLAZE_BREATHE(-1, "mob.blaze.breathe"), + MOB_BLAZE_DEATH(-1, "mob.blaze.death"), + MOB_BLAZE_HIT(-1, "mob.blaze.hit"), + MOB_CAT_HISS(-1, "mob.cat.hiss"), + MOB_CAT_HITT(-1, "mob.cat.hitt"), + MOB_CAT_MEOW(-1, "mob.cat.meow"), + MOB_CAT_PURR(-1, "mob.cat.purr"), + MOB_CAT_PURREOW(-1, "mob.cat.purreow"), + MOB_CHICKEN(-1, "mob.chicken"), + MOB_CHICKEN_HURT(-1, "mob.chicken.hurt"), + MOB_CHICKEN_PLOP(-1, "mob.chicken.plop"), + MOB_CHICKEN_SAY(-1, "mob.chicken.say"), + MOB_CHICKEN_STEP(-1, "mob.chicken.step"), + MOB_COW(-1, "mob.cow"), + MOB_COW_HURT(-1, "mob.cow.hurt"), + MOB_COW_SAY(-1, "mob.cow.say"), + MOB_COW_STEP(-1, "mob.cow.step"), + MOB_CREEPER(-1, "mob.creeper"), + MOB_CREEPER_DEATH(-1, "mob.creeper.death"), + MOB_CREEPER_SAY(-1, "mob.creeper.say"), + MOB_ENDERDRAGON_END(-1, "mob.enderdragon.end"), + MOB_ENDERDRAGON_GROWL(-1, "mob.enderdragon.growl"), + MOB_ENDERDRAGON_HIT(-1, "mob.enderdragon.hit"), + MOB_ENDERDRAGON_WINGS(-1, "mob.enderdragon.wings"), + MOB_ENDERMEN_DEATH(-1, "mob.endermen.death"), + MOB_ENDERMEN_HIT(-1, "mob.endermen.hit"), + MOB_ENDERMEN_IDLE(-1, "mob.endermen.idle"), + MOB_ENDERMEN_PORTAL(-1, "mob.endermen.portal"), + MOB_ENDERMEN_SCREAM(-1, "mob.endermen.scream"), + MOB_ENDERMEN_STARE(-1, "mob.endermen.stare"), + MOB_GHAST_AFFECTIONATE_SCREAM(-1, "mob.ghast.affectionate_scream"), + MOB_GHAST_CHARGE(-1, "mob.ghast.charge"), + MOB_GHAST_DEATH(-1, "mob.ghast.death"), + MOB_GHAST_FIREBALL(-1, "mob.ghast.fireball"), + MOB_GHAST_MOAN(-1, "mob.ghast.moan"), + MOB_GHAST_SCREAM(-1, "mob.ghast.scream"), + MOB_HORSE_ANGRY(-1, "mob.horse.angry"), + MOB_HORSE_ARMOR(-1, "mob.horse.armor"), + MOB_HORSE_BREATHE(-1, "mob.horse.breathe"), + MOB_HORSE_DEATH(-1, "mob.horse.death"), + MOB_HORSE_DONKEY_ANGRY(-1, "mob.horse.donkey.angry"), + MOB_HORSE_DONKEY_DEATH(-1, "mob.horse.donkey.death"), + MOB_HORSE_DONKEY_HIT(-1, "mob.horse.donkey.hit"), + MOB_HORSE_DONKEY_IDLE(-1, "mob.horse.donkey.idle"), + MOB_HORSE_GALLOP(-1, "mob.horse.gallop"), + MOB_HORSE_HIT(-1, "mob.horse.hit"), + MOB_HORSE_IDLE(-1, "mob.horse.idle"), + MOB_HORSE_JUMP(-1, "mob.horse.jump"), + MOB_HORSE_LAND(-1, "mob.horse.land"), + MOB_HORSE_LEATHER(-1, "mob.horse.leather"), + MOB_HORSE_SKELETON_DEATH(-1, "mob.horse.skeleton.death"), + MOB_HORSE_SKELETON_HIT(-1, "mob.horse.skeleton.hit"), + MOB_HORSE_SKELETON_IDLE(-1, "mob.horse.skeleton.idle"), + MOB_HORSE_SOFT(-1, "mob.horse.soft"), + MOB_HORSE_WOOD(-1, "mob.horse.wood"), + MOB_HORSE_ZOMBIE_DEATH(-1, "mob.horse.zombie.death"), + MOB_HORSE_ZOMBIE_HIT(-1, "mob.horse.zombie.hit"), + MOB_HORSE_ZOMBIE_IDLE(-1, "mob.horse.zombie.idle"), + MOB_IRONGOLEM_DEATH(-1, "mob.irongolem.death"), + MOB_IRONGOLEM_HIT(-1, "mob.irongolem.hit"), + MOB_IRONGOLEM_THROW(-1, "mob.irongolem.throw"), + MOB_IRONGOLEM_WALK(-1, "mob.irongolem.walk"), + MOB_MAGMACUBE_BIG(-1, "mob.magmacube.big"), + MOB_MAGMACUBE_JUMP(-1, "mob.magmacube.jump"), + MOB_MAGMACUBE_SMALL(-1, "mob.magmacube.small"), + MOB_PIG(-1, "mob.pig"), + MOB_PIG_DEATH(-1, "mob.pig.death"), + MOB_PIG_SAY(-1, "mob.pig.say"), + MOB_PIG_STEP(-1, "mob.pig.step"), + MOB_SHEEP(-1, "mob.sheep"), + MOB_SHEEP_SAY(-1, "mob.sheep.say"), + MOB_SHEEP_SHEAR(-1, "mob.sheep.shear"), + MOB_SHEEP_STEP(-1, "mob.sheep.step"), + MOB_SILVERFISH_HIT(-1, "mob.silverfish.hit"), + MOB_SILVERFISH_KILL(-1, "mob.silverfish.kill"), + MOB_SILVERFISH_SAY(-1, "mob.silverfish.say"), + MOB_SILVERFISH_STEP(-1, "mob.silverfish.step"), + MOB_SKELETON(-1, "mob.skeleton"), + MOB_SKELETON_DEATH(-1, "mob.skeleton.death"), + MOB_SKELETON_HURT(-1, "mob.skeleton.hurt"), + MOB_SKELETON_SAY(-1, "mob.skeleton.say"), + MOB_SKELETON_STEP(-1, "mob.skeleton.step"), + MOB_SLIME(-1, "mob.slime"), + MOB_SLIME_ATTACK(-1, "mob.slime.attack"), + MOB_SLIME_BIG(-1, "mob.slime.big"), + MOB_SLIME_SMALL(-1, "mob.slime.small"), + MOB_SPIDER(-1, "mob.spider"), + MOB_SPIDER_DEATH(-1, "mob.spider.death"), + MOB_SPIDER_SAY(-1, "mob.spider.say"), + MOB_SPIDER_STEP(-1, "mob.spider.step"), + MOB_VILLAGER_DEATH(-1, "mob.villager.death"), + MOB_VILLAGER_HAGGLE(-1, "mob.villager.haggle"), + MOB_VILLAGER_HIT(-1, "mob.villager.hit"), + MOB_VILLAGER_IDLE(-1, "mob.villager.idle"), + MOB_VILLAGER_NO(-1, "mob.villager.no"), + MOB_VILLAGER_YES(-1, "mob.villager.yes"), + MOB_WITHER_DEATH(-1, "mob.wither.death"), + MOB_WITHER_HURT(-1, "mob.wither.hurt"), + MOB_WITHER_IDLE(-1, "mob.wither.idle"), + MOB_WITHER_SHOOT(-1, "mob.wither.shoot"), + MOB_WITHER_SPAWN(-1, "mob.wither.spawn"), + MOB_WOLF_BARK(-1, "mob.wolf.bark"), + MOB_WOLF_DEATH(-1, "mob.wolf.death"), + MOB_WOLF_GROWL(-1, "mob.wolf.growl"), + MOB_WOLF_HOWL(-1, "mob.wolf.howl"), + MOB_WOLF_HURT(-1, "mob.wolf.hurt"), + MOB_WOLF_PANTING(-1, "mob.wolf.panting"), + MOB_WOLF_SHAKE(-1, "mob.wolf.shake"), + MOB_WOLF_STEP(-1, "mob.wolf.step"), + MOB_WOLF_WHINE(-1, "mob.wolf.whine"), + MOB_ZOMBIE(-1, "mob.zombie"), + MOB_ZOMBIE_DEATH(-1, "mob.zombie.death"), + MOB_ZOMBIE_HURT(-1, "mob.zombie.hurt"), + MOB_ZOMBIE_INFECT(-1, "mob.zombie.infect"), + MOB_ZOMBIE_METAL(-1, "mob.zombie.metal"), + MOB_ZOMBIE_REMEDY(-1, "mob.zombie.remedy"), + MOB_ZOMBIE_SAY(-1, "mob.zombie.say"), + MOB_ZOMBIE_STEP(-1, "mob.zombie.step"), + MOB_ZOMBIE_UNFECT(-1, "mob.zombie.unfect"), + MOB_ZOMBIE_WOOD(-1, "mob.zombie.wood"), + MOB_ZOMBIE_WOODBREAK(-1, "mob.zombie.woodbreak"), + MOB_ZOMBIEPIG_ZPIG(-1, "mob.zombiepig.zpig"), + MOB_ZOMBIEPIG_ZPIGANGRY(-1, "mob.zombiepig.zpigangry"), + MOB_ZOMBIEPIG_ZPIGDEATH(-1, "mob.zombiepig.zpigdeath"), + MOB_ZOMBIEPIG_ZPIGHURT(-1, "mob.zombiepig.zpighurt"), + MUSIC_GAME_CALM(-1, "music.game.calm"), + MUSIC_GAME_CREATIVE_CREATIVE(-1, "music.game.creative.creative"), + MUSIC_GAME_END_BOSS(-1, "music.game.end.boss"), + MUSIC_GAME_END_CREDITS(-1, "music.game.end.credits"), + MUSIC_GAME_END_END(-1, "music.game.end.end"), + MUSIC_GAME_HAL(-1, "music.game.hal"), + MUSIC_GAME_NETHER_NETHER(-1, "music.game.nether.nether"), + MUSIC_GAME_NUANCE(-1, "music.game.nuance"), + MUSIC_GAME_PIANO(-1, "music.game.piano"), + MUSIC_MENU_MENU(-1, "music.menu.menu"), + NOTE_BASS(-1, "note.bass"), + NOTE_BASSATTACK(-1, "note.bassattack"), + NOTE_BD(-1, "note.bd"), + NOTE_HARP(-1, "note.harp"), + NOTE_HAT(-1, "note.hat"), + NOTE_PLING(-1, "note.pling"), + NOTE_SNARE(-1, "note.snare"), + PORTAL_PORTAL(-1, "portal.portal"), + PORTAL_TRAVEL(-1, "portal.travel"), + PORTAL_TRIGGER(-1, "portal.trigger"), + RANDOM_ANVIL_LAND(-1, "random.anvil_land"), + RANDOM_BOW(-1, "random.bow"), + RANDOM_BOWHIT(-1, "random.bowhit"), + RANDOM_BREATH(-1, "random.breath"), + RANDOM_BURP(-1, "random.burp"), + RANDOM_CHESTCLOSED(-1, "random.chestclosed"), + RANDOM_CHESTOPEN(-1, "random.chestopen"), + RANDOM_CLASSIC_HURT(-1, "random.classic_hurt"), + RANDOM_DOOR_CLOSE(-1, "random.door_close"), + RANDOM_DOOR_OPEN(-1, "random.door_open"), + RANDOM_DRINK(-1, "random.drink"), + RANDOM_DRR(-1, "random.drr"), + RANDOM_EAT(-1, "random.eat"), + RANDOM_FUSE(-1, "random.fuse"), + RANDOM_GLASS(-1, "random.glass"), + RANDOM_HURT(-1, "random.hurt"), + RANDOM_LEVELUP(-1, "random.levelup"), + RANDOM_ORB(-1, "random.orb"), + RANDOM_POP(-1, "random.pop"), + RANDOM_SPLASH(-1, "random.splash"), + RANDOM_SUCCESSFUL_HIT(-1, "random.successful_hit"), + RANDOM_WOOD_CLICK(-1, "random.wood_click"), + RECORDS_11(-1, "records.11"), + RECORDS_13(-1, "records.13"), + RECORDS_BLOCKS(-1, "records.blocks"), + RECORDS_CAT(-1, "records.cat"), + RECORDS_CHIRP(-1, "records.chirp"), + RECORDS_FAR(-1, "records.far"), + RECORDS_MALL(-1, "records.mall"), + RECORDS_MELLOHI(-1, "records.mellohi"), + RECORDS_STAL(-1, "records.stal"), + RECORDS_STRAD(-1, "records.strad"), + RECORDS_WAIT(-1, "records.wait"), + RECORDS_WARD(-1, "records.ward"), + STEP_CLOTH(-1, "step.cloth"), + STEP_GRASS(-1, "step.grass"), + STEP_GRAVEL(-1, "step.gravel"), + STEP_LADDER(-1, "step.ladder"), + STEP_SAND(-1, "step.sand"), + STEP_SNOW(-1, "step.snow"), + STEP_STONE(-1, "step.stone"), + STEP_WOOD(-1, "step.wood"), + TILE_PISTON_IN(-1, "tile.piston.in"), + TILE_PISTON_OUT(-1, "tile.piston.out"), + + NONE(-1, ""); + + /** + * Internal mapping by {@code int} id + */ + private static final Map<Integer, SoundResource> ID_SOUND_MAP = new ConcurrentHashMap<>(); + /** + * Internal mapping by {@code String} ResourceLocation + */ + private static final Map<String, SoundResource> RESOURCE_STR_SOUND_MAP = new ConcurrentHashMap<>(); + + static { + EnumSet.allOf(SoundResource.class) + .forEach(sound -> { if (sound.id >= 0) ID_SOUND_MAP.put(sound.id, sound); }); + EnumSet.allOf(SoundResource.class) + .forEach(sound -> RESOURCE_STR_SOUND_MAP.put(sound.resourceLocation.toString(), sound)); + } + + /** + * This Sound's identifier + */ + public final int id; + + /** + * The {@link ResourceLocation} of this {@link SoundResource} + */ + public final ResourceLocation resourceLocation; + + SoundResource(final int id, final ResourceLocation resourceLocation) { + this.id = id; + this.resourceLocation = resourceLocation; + } + + SoundResource(final int id, final String resourcePath) { + this(id, new ResourceLocation(resourcePath)); + } + + SoundResource(final int id, final String resourceDomain, final String resourcePath) { + this(id, new ResourceLocation(resourceDomain.toLowerCase(Locale.ENGLISH), resourcePath)); + } + + /** + * @param id The Sounds identifier + * @return The {@link SoundResource} + */ + public static SoundResource get(int id) { + return ID_SOUND_MAP.get(id); + } + + /** + * @param resourceStr The {@link ResourceLocation}'s String of the {@link SoundResource} + * @return The {@link SoundResource} + */ + public static SoundResource get(String resourceStr) { + return RESOURCE_STR_SOUND_MAP.get(resourceStr); + } + + /** + * Provides a backward-compatible Sounds {@code Map<Integer, String>} sound list + * + * @return The map for the deprecated {@link gregtech.api.GregTech_API#sSoundList} + * @deprecated This method is planned for removal. + * <p> + * Use this {@link SoundResource} enum instead. + * </p> + */ + @Deprecated + public static Map<Integer, String> asSoundList() { + return Maps.transformValues(ID_SOUND_MAP, SoundResource::toString); + } + + /** + * @inheritDoc + */ + @Override + public String toString() { + return this.resourceLocation.toString(); + } +} diff --git a/src/main/java/gregtech/api/enums/SteamVariant.java b/src/main/java/gregtech/api/enums/SteamVariant.java new file mode 100644 index 0000000000..941b0d7231 --- /dev/null +++ b/src/main/java/gregtech/api/enums/SteamVariant.java @@ -0,0 +1,16 @@ +package gregtech.api.enums; + +import java.util.Locale; + +public enum SteamVariant { + + BRONZE, + STEEL, + PRIMITIVE, + NONE; + + @Override + public String toString() { + return super.toString().toLowerCase(Locale.ENGLISH); + } +} diff --git a/src/main/java/gregtech/api/enums/SubTag.java b/src/main/java/gregtech/api/enums/SubTag.java new file mode 100644 index 0000000000..e62e5437f8 --- /dev/null +++ b/src/main/java/gregtech/api/enums/SubTag.java @@ -0,0 +1,274 @@ +package gregtech.api.enums; + +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; + +import gregtech.api.interfaces.ICondition; +import gregtech.api.interfaces.ISubTagContainer; + +/** + * Just a simple Class to be able to add special Tags for Materials. + * <p/> + * The Tags should be added in preload and before I do my own preload to the Materials. In order to make yourself a new + * SubTag, just create one new instance of SubTag using getNewSubTag and use that one instance on all Materials you want + * to add those Tags to. + * <p/> + * You should look at this File whenever you update, maybe there are some new Tags you could use. + * <p/> + * ------------------------------------------------------------------------------------------------- + * <p/> + * Some SubTags are used for other things than Materials too. It is useful when I need an easy way to declare Stuff in + * Items. + */ +public final class SubTag implements ICondition<ISubTagContainer> { + + public static final HashMap<String, SubTag> sSubTags = new HashMap<>(); + private static long sSubtagID = 0; + public final long mSubtagID; + public final String mName; + /** + * Add this to your Material if you want to have its Ore Calcite heated in a Blast Furnace for more output. Already + * listed are: Iron, Pyrite, PigIron, DeepIron, ShadowIron, WroughtIron and MeteoricIron. + */ + public static final SubTag BLASTFURNACE_CALCITE_DOUBLE = getNewSubTag("BLASTFURNACE_CALCITE_DOUBLE"), + BLASTFURNACE_CALCITE_TRIPLE = getNewSubTag("BLASTFURNACE_CALCITE_TRIPLE"); + + /** + * Add this to a material with Direct Smelting to prevent the automatic generation of a Bricked/Bronze Blast Furnace + * recipe. Already listed are: Chalcopyrite, Tetrahedrite + */ + public static final SubTag DONT_ADD_DEFAULT_BBF_RECIPE = getNewSubTag("DONT_ADD_DEFAULT_BBF_RECIPE"); + + /** + * Materials which are outputting less in an Induction Smelter. Already listed are: Pyrite, Tetrahedrite, + * Sphalerite, Cinnabar + */ + public static final SubTag INDUCTIONSMELTING_LOW_OUTPUT = getNewSubTag("INDUCTIONSMELTING_LOW_OUTPUT"); + /** + * Add this to your Material if you want to have its Ore Sodium Persulfate washed. Already listed are: Zinc, Nickel, + * Copper, Cobalt, Cobaltite and Tetrahedrite. + */ + public static final SubTag WASHING_SODIUMPERSULFATE = getNewSubTag("WASHING_SODIUMPERSULFATE"); + /** + * Add this to your Material if you want to have its Ore Mercury washed. Already listed are: Gold, Osmium, Mithril, + * Platinum, Midasium, Cooperite and AstralSilver. + */ + public static final SubTag WASHING_MERCURY = getNewSubTag("WASHING_MERCURY"); + /** + * Add this to your Material if you want to have its Ore Mercury washed with 99% output chance. Already listed are: + * Silver + */ + public static final SubTag WASHING_MERCURY_99_PERCENT = getNewSubTag("WASHING_MERCURY_99_PERCENT"); + /** + * Add this to your Material if you want to have its Ore electromagnetically separated to give Gold. + */ + public static final SubTag ELECTROMAGNETIC_SEPERATION_GOLD = getNewSubTag("ELECTROMAGNETIC_SEPERATION_GOLD"); + /** + * Add this to your Material if you want to have its Ore electromagnetically separated to give Iron. + */ + public static final SubTag ELECTROMAGNETIC_SEPERATION_IRON = getNewSubTag("ELECTROMAGNETIC_SEPERATION_IRON"); + /** + * Add this to your Material if you want to have its Ore electromagnetically separated to give Neodymium. + */ + public static final SubTag ELECTROMAGNETIC_SEPERATION_NEODYMIUM = getNewSubTag( + "ELECTROMAGNETIC_SEPERATION_NEODYMIUM"); + /** + * Add this to your Material if you want to have its Ore giving Cinnabar Crystals on Pulverization. Already listed + * are: Redstone + */ + public static final SubTag PULVERIZING_CINNABAR = getNewSubTag("PULVERIZING_CINNABAR"); + /** + * This Material cannot be worked by any other means, than smashing or smelting. This is used for coated Materials. + */ + public static final SubTag NO_WORKING = getNewSubTag("NO_WORKING"); + /** + * This Material cannot be used for regular Metal working techniques. Already + * listed are: Rubber, Plastic, Paper, Wood, Stone + */ + public static final SubTag NO_SMASHING = getNewSubTag("NO_SMASHING"); + /** + * This Material will have no associated recipes in any format. + */ + public static final SubTag NO_RECIPES = getNewSubTag("NO_RECIPES"); + /** + * This Material cannot be unificated + */ + public static final SubTag NO_UNIFICATION = getNewSubTag("NO_UNIFICATION"); + /** + * This Material cannot be used in any Recycler. Already listed are: Stone, Glass, Water + */ + public static final SubTag NO_RECYCLING = getNewSubTag("NO_RECYCLING"); + /** + * This Material cannot be used in any Furnace alike Structure. Already listed are: Paper, Wood, Gunpowder, Stone + */ + public static final SubTag NO_SMELTING = getNewSubTag("NO_SMELTING"); + /** + * This Material can be molten into a Fluid + */ + public static final SubTag SMELTING_TO_FLUID = getNewSubTag("SMELTING_TO_FLUID"); + /** + * This Ore should be molten directly into a Gem of this Material, if the Ingot is missing. Already listed are: + * Cinnabar + */ + public static final SubTag SMELTING_TO_GEM = getNewSubTag("SMELTING_TO_GEM"); + /** + * If this Material is some kind of Wood + */ + public static final SubTag WOOD = getNewSubTag("WOOD"); + /** + * If this Material is some kind of Food (or edible at all) + */ + public static final SubTag FOOD = getNewSubTag("FOOD"); + /** + * If this Material is some kind of Stone + */ + public static final SubTag STONE = getNewSubTag("STONE"); + /** + * If this Material is some kind of Pearl + */ + public static final SubTag PEARL = getNewSubTag("PEARL"); + /** + * If this Material is some kind of Quartz + */ + public static final SubTag QUARTZ = getNewSubTag("QUARTZ"); + /** + * If this Material is Crystallisable + */ + public static final SubTag CRYSTALLISABLE = getNewSubTag("CRYSTALLISABLE"); + /** + * If this Material is some kind of Crystal + */ + public static final SubTag CRYSTAL = getNewSubTag("CRYSTAL"); + /** + * If this Material is some kind of Magical + */ + public static final SubTag MAGICAL = getNewSubTag("MAGICAL"); + /** + * If this Material is some kind of Metal + */ + public static final SubTag METAL = getNewSubTag("METAL"); + /** + * If this Material is some kind of Paper + */ + public static final SubTag PAPER = getNewSubTag("PAPER"); + /** + * If this Material is having a constantly burning Aura + */ + public static final SubTag BURNING = getNewSubTag("BURNING"); + /** + * If this Material is some kind of flammable + */ + public static final SubTag FLAMMABLE = getNewSubTag("FLAMMABLE"); + /** + * If this Material is not burnable at all + */ + public static final SubTag UNBURNABLE = getNewSubTag("UNBURNABLE"); + /** + * If this Material is some kind of explosive + */ + public static final SubTag EXPLOSIVE = getNewSubTag("EXPLOSIVE"); + /** + * If this Material is bouncy + */ + public static final SubTag BOUNCY = getNewSubTag("BOUNCY"); + /** + * If this Material is invisible + */ + public static final SubTag INVISIBLE = getNewSubTag("INVISIBLE"); + /** + * If this Material is transparent + */ + public static final SubTag TRANSPARENT = getNewSubTag("TRANSPARENT"); + /** + * If this Material has a Color + */ + public static final SubTag HAS_COLOR = getNewSubTag("HAS_COLOR"); + /** + * If this Material is stretchable + */ + public static final SubTag STRETCHY = getNewSubTag("STRETCHY"); + /** + * If this Material is soft (and can be made into a Soft Mallet even if it's not wooden or bouncy) + */ + public static final SubTag SOFT = getNewSubTag("SOFT"); + /** + * If this Material is grindable with a simple Mortar + */ + public static final SubTag MORTAR_GRINDABLE = getNewSubTag("MORTAR_GRINDABLE"); + /** + * If this Material is usable for Soldering + */ + public static final SubTag SOLDERING_MATERIAL = getNewSubTag("SOLDERING_MATERIAL"); + /** + * If this Material is has extra Costs for Soldering, requires the Tag "SOLDERING_MATERIAL" too + */ + public static final SubTag SOLDERING_MATERIAL_BAD = getNewSubTag("SOLDERING_MATERIAL_BAD"); + /** + * If this Material is has a discount for Soldering, requires the Tag "SOLDERING_MATERIAL" too + */ + public static final SubTag SOLDERING_MATERIAL_GOOD = getNewSubTag("SOLDERING_MATERIAL_GOOD"); + /** + * Energy Tag for Electricity Primary = Voltage Secondary = Amperage + */ + public static final SubTag ENERGY_ELECTRICITY = getNewSubTag("ENERGY_ELECTRICITY"); + /** + * Energy Tag for Rotating Power Primary = Speed Secondary = Power + */ + public static final SubTag ENERGY_ROTATIONAL = getNewSubTag("ENERGY_ROTATIONAL"); + /** + * Energy Tag for Steam Power Primary = Steam per Tick Secondary = unused (always 1) + */ + public static final SubTag ENERGY_STEAM = getNewSubTag("ENERGY_STEAM"); + /** + * Energy Tag for Air Pressure Power Primary = Pressure Secondary = unused (always 1) + */ + public static final SubTag ENERGY_AIR = getNewSubTag("ENERGY_AIR"); + /** + * Energy Tag for Heat Primary = Temperature Secondary = unused (always 1) + */ + public static final SubTag ENERGY_HEAT = getNewSubTag("ENERGY_HEAT"); + /** + * Energy Tag for RedstoneFlux Primary = unused (always 1) Secondary = RF + */ + public static final SubTag ENERGY_REDSTONE_FLUX = getNewSubTag("ENERGY_REDSTONE_FLUX"); + /** + * Projectile Tag for Arrows + */ + public static final SubTag PROJECTILE_ARROW = getNewSubTag("PROJECTILE_ARROW"); + + public final Collection<ISubTagContainer> mRelevantTaggedItems = new HashSet<>(1); + + private SubTag(String aName) { + mSubtagID = sSubtagID++; + mName = aName; + sSubTags.put(aName, this); + } + + public static SubTag getNewSubTag(String aName) { + for (SubTag tSubTag : sSubTags.values()) if (tSubTag.mName.equals(aName)) return tSubTag; + return new SubTag(aName); + } + + @Override + public String toString() { + return mName; + } + + public SubTag addContainerToList(ISubTagContainer... aContainers) { + if (aContainers != null) for (ISubTagContainer aContainer : aContainers) + if (aContainer != null && !mRelevantTaggedItems.contains(aContainer)) mRelevantTaggedItems.add(aContainer); + return this; + } + + public SubTag addTo(ISubTagContainer... aContainers) { + if (aContainers != null) + for (ISubTagContainer aContainer : aContainers) if (aContainer != null) aContainer.add(this); + return this; + } + + @Override + public boolean isTrue(ISubTagContainer aObject) { + return aObject.contains(this); + } +} diff --git a/src/main/java/gregtech/api/enums/TAE.java b/src/main/java/gregtech/api/enums/TAE.java new file mode 100644 index 0000000000..246b2006ea --- /dev/null +++ b/src/main/java/gregtech/api/enums/TAE.java @@ -0,0 +1,145 @@ +package gregtech.api.enums; + +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.HashSet; + +import gregtech.api.interfaces.ITexture; +import gtPlusPlus.api.objects.Logger; +import gtPlusPlus.api.objects.data.AutoMap; +import gtPlusPlus.core.block.ModBlocks; +import gtPlusPlus.core.lib.CORE; +import gtPlusPlus.core.util.reflect.ReflectionUtils; +import gtPlusPlus.xmod.gregtech.api.objects.GTPP_CopiedBlockTexture; + +public class TAE { + + // TAE stands for Texture Array Expansion. + + public static int gtPPLastUsedIndex = 64; + public static int secondaryIndex = 0; + + public static HashMap<Integer, GTPP_CopiedBlockTexture> mTAE = new HashMap<>(); + private static final HashSet<Integer> mFreeSlots = new HashSet<>(64); + + static { + for (int i = 64; i < 128; i++) { + mFreeSlots.add(i); + } + Logger.INFO("Initialising TAE."); + } + + /** + * + * @param aPage - The Texture page (0-3) + * @param aID - The ID on the specified page (0-15) + * @param GTPP_CopiedBlockTexture - The Texture to register + * @return - Did it register correctly? + */ + public static boolean registerTexture(int aPage, int aID, GTPP_CopiedBlockTexture GTPP_CopiedBlockTexture) { + int aRealID = aID + (aPage * 16); + return registerTexture(64 + aRealID, GTPP_CopiedBlockTexture); + } + + public static boolean registerTexture(int aID, GTPP_CopiedBlockTexture GTPP_CopiedBlockTexture) { + if (mFreeSlots.contains(aID)) { + mFreeSlots.remove(aID); + mTAE.put(aID, GTPP_CopiedBlockTexture); + return true; + } else { + CORE.crash("Tried to register texture with ID " + aID + " to TAE, but it is already in use."); + return false; // Dead Code + } + } + + public static void finalizeTAE() { + String aFreeSpaces = ""; + String aPageAndSlotFree = ""; + AutoMap<Integer> aTemp = new AutoMap<>(mFreeSlots); + for (int i = 0; i < mFreeSlots.size(); i++) { + int j = aTemp.get(i); + aFreeSpaces += j; + aPageAndSlotFree += getPageFromIndex(j); + if (i != (mFreeSlots.size() - 1)) { + aFreeSpaces += ", "; + aPageAndSlotFree += ", "; + } + } + Logger.INFO("Free Indexes within TAE: " + aFreeSpaces); + Logger.INFO("Free Page slots within TAE: " + aPageAndSlotFree); + Logger.INFO("Filling them with ERROR textures."); + for (int aFreeSlot : aTemp.values()) { + registerTexture(aFreeSlot, new GTPP_CopiedBlockTexture(ModBlocks.blockCasingsTieredGTPP, 1, 15)); + } + Logger.INFO("Finalising TAE."); + for (int aKeyTae : mTAE.keySet()) { + Textures.BlockIcons.setCasingTextureForId(aKeyTae, mTAE.get(aKeyTae)); + } + Logger.INFO("Finalised TAE."); + } + + private static boolean registerTextures(GTPP_CopiedBlockTexture GTPP_CopiedBlockTexture) { + try { + // Handle page 2. + Logger.INFO("[TAE} Registering Texture, Last used casing ID is " + gtPPLastUsedIndex + "."); + if (gtPPLastUsedIndex >= 128) { + Field x = ReflectionUtils.getField(Textures.BlockIcons.class, "casingTexturePages"); + if (x != null) { + ITexture[][] h = (ITexture[][]) x.get(null); + if (h != null) { + h[64][secondaryIndex++] = GTPP_CopiedBlockTexture; + x.set(null, h); + Logger + .INFO("[TAE} Registered Texture with ID " + (secondaryIndex - 1) + " in secondary index."); + return true; + } + } + } + + // set to page 1. + else { + Textures.BlockIcons.setCasingTextureForId(gtPPLastUsedIndex, GTPP_CopiedBlockTexture); + Logger.INFO("[TAE} Registered Texture with ID " + (gtPPLastUsedIndex) + " in main index."); + gtPPLastUsedIndex++; + return true; + } + } catch (Throwable t) { + t.printStackTrace(); + } + Logger.INFO("[TAE} Failed to register texture, Last used casing ID is " + gtPPLastUsedIndex + "."); + return false; + } + + public static ITexture getTexture(int index) { + if (gtPPLastUsedIndex >= 128) { + return Textures.BlockIcons.getCasingTextureForId(((64 * 128) + index)); + } + return Textures.BlockIcons.getCasingTextureForId((64 + index)); + } + + public static int GTPP_INDEX(int ID) { + + if (ID >= 64) { + if (gtPPLastUsedIndex >= 128) { + return (128 + ID); + } + } + return (64 + ID); + } + + public static int getIndexFromPage(int page, int blockMeta) { + int id = 64; + id += (page == 0 ? 0 : page == 1 ? 16 : page == 2 ? 32 : page == 3 ? 48 : page == 4 ? 64 : 0); + id += blockMeta; + return id; + } + + public static String getPageFromIndex(int aIndex) { + int aPage = 0; + int aSlot = 0; + int aAdjustedIndex = aIndex > 64 ? (aIndex - 64) : aIndex; + aPage = aAdjustedIndex / 16; + aSlot = aAdjustedIndex - (16 * aPage); + return "[" + aIndex + " | " + aPage + ", " + aSlot + "]"; + } +} diff --git a/src/main/java/gregtech/api/enums/TC_Aspects.java b/src/main/java/gregtech/api/enums/TC_Aspects.java new file mode 100644 index 0000000000..24d19b0b73 --- /dev/null +++ b/src/main/java/gregtech/api/enums/TC_Aspects.java @@ -0,0 +1,112 @@ +package gregtech.api.enums; + +import java.util.List; + +public enum TC_Aspects { + + AER(1), + ALIENIS(20), + AQUA(3), + ARBOR(1), + AURAM(16), + BESTIA(6), + COGNITIO(2), + CORPUS(2), + ELECTRUM(24), + EXANIMIS(32), + FABRICO(2), + FAMES(2), + GELUM(1), + GRANUM(4), + HERBA(2), + HUMANUS(8), + IGNIS(4), + INSTRUMENTUM(4), + ITER(6), + LIMUS(3), + LUCRUM(32), + LUX(4), + MACHINA(16), + MAGNETO(24), + MESSIS(3), + METALLUM(8), + METO(2), + MORTUUS(16), + MOTUS(4), + NEBRISUM(48), + ORDO(8), + PANNUS(6), + PERDITIO(2), + PERFODIO(4), + PERMUTATIO(12), + POTENTIA(16), + PRAECANTATIO(16), + RADIO(48), + SANO(24), + SENSUS(4), + SPIRITUS(24), + STRONTIO(64), + TELUM(6), + TERRA(1), + TEMPESTAS(64), + TENEBRAE(24), + TUTAMEN(12), + VACUOS(6), + VENENUM(16), + VICTUS(4), + VINCULUM(16), + VITIUM(48), + VITREUS(3), + VOLATUS(12); + + /** + * The Thaumcraft Aspect Object of the Mod itself. + */ + public Object mAspect; + + public final int mValue; + + TC_Aspects(int aValue) { + mValue = aValue; + } + + public static class TC_AspectStack { + + public TC_Aspects mAspect; + public long mAmount; + + public TC_AspectStack(TC_Aspects aAspect, long aAmount) { + mAspect = aAspect; + mAmount = aAmount; + } + + public TC_AspectStack copy() { + return new TC_AspectStack(mAspect, mAmount); + } + + public TC_AspectStack copy(long aAmount) { + return new TC_AspectStack(mAspect, aAmount); + } + + public List<TC_AspectStack> addToAspectList(List<TC_AspectStack> aList) { + if (mAmount == 0) return aList; + for (TC_AspectStack tAspect : aList) if (tAspect.mAspect == mAspect) { + tAspect.mAmount += mAmount; + return aList; + } + aList.add(copy()); + return aList; + } + + public boolean removeFromAspectList(List<TC_AspectStack> aList) { + for (TC_AspectStack tAspect : aList) if (tAspect.mAspect == mAspect) { + if (tAspect.mAmount >= mAmount) { + tAspect.mAmount -= mAmount; + if (tAspect.mAmount == 0) aList.remove(tAspect); + return true; + } + } + return false; + } + } +} diff --git a/src/main/java/gregtech/api/enums/TextureSet.java b/src/main/java/gregtech/api/enums/TextureSet.java new file mode 100644 index 0000000000..9e9b182c39 --- /dev/null +++ b/src/main/java/gregtech/api/enums/TextureSet.java @@ -0,0 +1,132 @@ +package gregtech.api.enums; + +import gregtech.api.interfaces.IIconContainer; + +public class TextureSet { + + private static final String aTextMatIconDir = "materialicons/"; + private static final String aTextVoidDir = "/void"; + + private static final TextureType[] IS_BLOCK_TEXTURE = new TextureType[] { TextureType.ITEM, TextureType.ITEM, + TextureType.ITEM, TextureType.ITEM, TextureType.ITEM, TextureType.ITEM, TextureType.ITEM, TextureType.ITEM, + TextureType.ITEM, TextureType.ITEM, TextureType.ITEM, TextureType.ITEM, TextureType.ITEM, TextureType.ITEM, + TextureType.ITEM, TextureType.ITEM, TextureType.ITEM, TextureType.ITEM, TextureType.ITEM, TextureType.ITEM, + TextureType.ITEM, TextureType.ITEM, TextureType.ITEM, TextureType.ITEM, TextureType.ITEM, TextureType.ITEM, + TextureType.ITEM, TextureType.ITEM, TextureType.ITEM, TextureType.ITEM, TextureType.ITEM, TextureType.ITEM, + TextureType.ITEM, TextureType.ITEM, TextureType.ITEM, TextureType.ITEM, TextureType.ITEM, TextureType.ITEM, + TextureType.ITEM, TextureType.ITEM, TextureType.ITEM, TextureType.ITEM, TextureType.ITEM, TextureType.ITEM, + TextureType.ITEM, TextureType.ITEM, TextureType.ITEM, TextureType.ITEM, TextureType.ITEM, TextureType.ITEM, + TextureType.ITEM, TextureType.ITEM, TextureType.ITEM, TextureType.ITEM, TextureType.ITEM, TextureType.ITEM, + TextureType.ITEM, TextureType.ITEM, TextureType.ITEM, TextureType.ITEM, TextureType.ITEM, TextureType.ITEM, + TextureType.ITEM, TextureType.ITEM, TextureType.ITEM, TextureType.BLOCK, TextureType.BLOCK, TextureType.BLOCK, + TextureType.BLOCK, TextureType.BLOCK, TextureType.BLOCK, TextureType.BLOCK, TextureType.BLOCK, + TextureType.BLOCK, TextureType.BLOCK, TextureType.BLOCK, TextureType.BLOCK, TextureType.BLOCK, + TextureType.BLOCK, TextureType.BLOCK, TextureType.BLOCK, TextureType.BLOCK, TextureType.BLOCK, + TextureType.BLOCK, TextureType.BLOCK, TextureType.BLOCK, TextureType.BLOCK, TextureType.BLOCK, + TextureType.BLOCK, TextureType.BLOCK, TextureType.BLOCK, TextureType.BLOCK, TextureType.BLOCK, + TextureType.BLOCK, TextureType.BLOCK, TextureType.BLOCK, TextureType.ITEM, TextureType.ITEM, TextureType.ITEM, + TextureType.ITEM, TextureType.ITEM, TextureType.ITEM, TextureType.ITEM, TextureType.ITEM, TextureType.ITEM, + TextureType.ITEM, TextureType.ITEM, TextureType.ITEM, TextureType.ITEM, TextureType.ITEM, TextureType.ITEM, + TextureType.ITEM, TextureType.ITEM, TextureType.ITEM, TextureType.ITEM, TextureType.ITEM, TextureType.ITEM, + TextureType.ITEM, TextureType.ITEM, TextureType.ITEM, TextureType.ITEM, TextureType.ITEM, TextureType.ITEM, + TextureType.ITEM, TextureType.ITEM, TextureType.ITEM, TextureType.ITEM, TextureType.ITEM, }; + private static final String[] SUFFIXES = new String[] { "/dustTiny", "/dustSmall", "/dust", "/dustImpure", + "/dustPure", "/crushed", "/crushedPurified", "/crushedCentrifuged", "/gem", "/nugget", "/casingSmall", "/ingot", + "/ingotHot", "/ingotDouble", "/ingotTriple", "/ingotQuadruple", "/ingotQuintuple", "/plate", "/plateDouble", + "/plateTriple", "/plateQuadruple", "/plateQuintuple", "/plateDense", "/stick", "/lens", "/round", "/bolt", + "/screw", "/ring", "/foil", "/cell", "/cellPlasma", "/toolHeadSword", "/toolHeadPickaxe", "/toolHeadShovel", + "/toolHeadAxe", "/toolHeadHoe", "/toolHeadHammer", "/toolHeadFile", "/toolHeadSaw", "/toolHeadDrill", + "/toolHeadChainsaw", "/toolHeadWrench", "/toolHeadUniversalSpade", "/toolHeadSense", "/toolHeadPlow", + "/toolHeadArrow", "/toolHeadScrewdriver", "/toolHeadBuzzSaw", "/toolHeadSoldering", "/nanites", "/wireFine", + "/gearGtSmall", "/rotor", "/stickLong", "/springSmall", "/spring", "/arrowGtWood", "/arrowGtPlastic", + "/gemChipped", "/gemFlawed", "/gemFlawless", "/gemExquisite", "/gearGt", "/oreRaw", aTextVoidDir, aTextVoidDir, + "/oreSmall", "/ore", "/wire", "/foil", "/block1", "/block2", "/block3", "/block4", "/block5", "/block6", + "/pipeSide", "/pipeTiny", "/pipeSmall", "/pipeMedium", "/pipeLarge", "/pipeHuge", "/frameGt", "/pipeQuadruple", + "/pipeNonuple", aTextVoidDir, aTextVoidDir, aTextVoidDir, aTextVoidDir, aTextVoidDir, aTextVoidDir, + aTextVoidDir, aTextVoidDir, aTextVoidDir, aTextVoidDir, "/crateGtDust", "/crateGtIngot", "/crateGtGem", + "/crateGtPlate", "/turbineBlade", aTextVoidDir, aTextVoidDir, aTextVoidDir, aTextVoidDir, aTextVoidDir, + aTextVoidDir, aTextVoidDir, aTextVoidDir, aTextVoidDir, aTextVoidDir, aTextVoidDir, aTextVoidDir, aTextVoidDir, + aTextVoidDir, aTextVoidDir, aTextVoidDir, aTextVoidDir, aTextVoidDir, aTextVoidDir, aTextVoidDir, aTextVoidDir, + aTextVoidDir, aTextVoidDir, aTextVoidDir, aTextVoidDir, "/handleMallet", "/toolHeadMallet", }; + + public boolean is_custom = false; + + public static final TextureSet SET_NONE = new TextureSet("NONE"), SET_DULL = new TextureSet("DULL"), + SET_RUBY = new TextureSet("RUBY"), SET_OPAL = new TextureSet("OPAL"), SET_LEAF = new TextureSet("LEAF"), + SET_WOOD = new TextureSet("WOOD"), SET_SAND = new TextureSet("SAND"), SET_FINE = new TextureSet("FINE"), + SET_FIERY = new TextureSet("FIERY"), SET_FLUID = new TextureSet("FLUID"), SET_ROUGH = new TextureSet("ROUGH"), + SET_PAPER = new TextureSet("PAPER"), SET_GLASS = new TextureSet("GLASS"), SET_FLINT = new TextureSet("FLINT"), + SET_LAPIS = new TextureSet("LAPIS"), SET_SHINY = new TextureSet("SHINY"), SET_SHARDS = new TextureSet("SHARDS"), + SET_POWDER = new TextureSet("POWDER"), SET_QUARTZ = new TextureSet("QUARTZ"), + SET_EMERALD = new TextureSet("EMERALD"), SET_DIAMOND = new TextureSet("DIAMOND"), + SET_LIGNITE = new TextureSet("LIGNITE"), SET_MAGNETIC = new TextureSet("MAGNETIC"), + SET_METALLIC = new TextureSet("METALLIC"), SET_NETHERSTAR = new TextureSet("NETHERSTAR"), + SET_GEM_VERTICAL = new TextureSet("GEM_VERTICAL"), SET_GEM_HORIZONTAL = new TextureSet("GEM_HORIZONTAL"); + + /** + * For the Indices of OrePrefixes you need to look into the OrePrefix Enum. + */ + public static final short INDEX_wire = 69, INDEX_foil = 70, INDEX_block1 = 71, INDEX_block2 = 72, INDEX_block3 = 73, + INDEX_block4 = 74, INDEX_block5 = 75, INDEX_block6 = 76; + + public final IIconContainer[] mTextures = new IIconContainer[128]; + public final String mSetName; + + public TextureSet(String aSetName) { + mSetName = aSetName; + for (int i = 0; i < 128; i++) { + if (IS_BLOCK_TEXTURE[i] == TextureType.BLOCK) { + mTextures[i] = new Textures.BlockIcons.CustomIcon(aTextMatIconDir + aSetName + SUFFIXES[i]); + } else { + // Check nanites folder for nanites texture to avoid copy pasting large file multiple times. + // Exemption for CUSTOM textures so they can be overriden as normal by placing nanite image in + // their respective folder. + if (SUFFIXES[i].equals("/nanites") && (!aSetName.contains("CUSTOM"))) { + mTextures[i] = new Textures.ItemIcons.CustomIcon(aTextMatIconDir + "NANITES" + SUFFIXES[i]); + } else { + mTextures[i] = new Textures.ItemIcons.CustomIcon(aTextMatIconDir + aSetName + SUFFIXES[i]); + } + } + } + } + + public TextureSet(String aSetName, boolean isCustom) { + this("CUSTOM/" + aSetName); + this.is_custom = isCustom; + } + + /** + * Construct a TextureSet that will delegate some of its textures to the origin TextureSet. + * <p> + * This assumes you want to construct a custom texture set. + */ + private TextureSet(String aSetName, TextureSet origin, boolean overrideBlock, boolean overrideItem) { + mSetName = "CUSTOM/" + aSetName; + this.is_custom = true; + + for (int i = 0; i < 128; i++) { + if (IS_BLOCK_TEXTURE[i] == TextureType.BLOCK) { + if (overrideBlock) { + mTextures[i] = new Textures.BlockIcons.CustomIcon(aTextMatIconDir + mSetName + SUFFIXES[i]); + } else { + mTextures[i] = origin.mTextures[i]; + } + } else { + if (overrideItem) { + mTextures[i] = new Textures.ItemIcons.CustomIcon(aTextMatIconDir + aSetName + SUFFIXES[i]); + } else { + mTextures[i] = origin.mTextures[i]; + } + } + } + } + + public TextureSet withBlockTextures(String aNewSetName) { + return new TextureSet(aNewSetName, this, true, false); + } + + private enum TextureType { + BLOCK, + ITEM, + } +} diff --git a/src/main/java/gregtech/api/enums/Textures.java b/src/main/java/gregtech/api/enums/Textures.java new file mode 100644 index 0000000000..4faaddce38 --- /dev/null +++ b/src/main/java/gregtech/api/enums/Textures.java @@ -0,0 +1,1895 @@ +package gregtech.api.enums; + +import static gregtech.api.enums.Mods.GregTech; + +import java.util.HashMap; +import java.util.Map; + +import net.minecraft.client.renderer.texture.TextureMap; +import net.minecraft.util.IIcon; +import net.minecraft.util.ResourceLocation; + +import gregtech.api.GregTech_API; +import gregtech.api.interfaces.IIconContainer; +import gregtech.api.interfaces.ITexture; +import gregtech.api.render.TextureFactory; +import gregtech.api.util.GT_Utility; + +public class Textures { + + public enum BlockIcons implements IIconContainer, Runnable { + + // ADDED + MACHINE_UEV_SIDE, + MACHINE_UIV_SIDE, + + MACHINE_UMV_SIDE, + MACHINE_UXV_SIDE, + + MACHINE_MAXV_SIDE, + + MACHINE_UEV_TOP, + MACHINE_UIV_TOP, + + MACHINE_UMV_TOP, + MACHINE_UXV_TOP, + + MACHINE_MAXV_TOP, + + MACHINE_UEV_BOTTOM, + MACHINE_UIV_BOTTOM, + + MACHINE_UMV_BOTTOM, + MACHINE_UXV_BOTTOM, + + MACHINE_MAXV_BOTTOM, + + OVERLAY_SCHEST, + OVERLAY_SCHEST_GLOW, + OVERLAY_STANK, + OVERLAY_STANK_GLOW, + + OVERLAY_PIPELINE_FLUID_BACK, + OVERLAY_PIPELINE_FLUID_FRONT, + OVERLAY_PIPELINE_FLUID_SIDE_LEFT_RIGHT, + OVERLAY_PIPELINE_FLUID_SIDE_LEFT_RIGHT_GLOW, + OVERLAY_PIPELINE_FLUID_SIDE_UP_DOWN, + OVERLAY_PIPELINE_FLUID_SIDE_UP_DOWN_GLOW, + + OVERLAY_PIPELINE_ITEM_BACK, + OVERLAY_PIPELINE_ITEM_FRONT, + OVERLAY_PIPELINE_ITEM_SIDE_LEFT_RIGHT, + OVERLAY_PIPELINE_ITEM_SIDE_LEFT_RIGHT_GLOW, + OVERLAY_PIPELINE_ITEM_SIDE_UP_DOWN, + OVERLAY_PIPELINE_ITEM_SIDE_UP_DOWN_GLOW, + + LONG_DISTANCE_PIPE_FLUID, + LONG_DISTANCE_PIPE_ITEM, + + HIDDEN_FACE, + + MACHINE_CASING_TANK_1, + MACHINE_CASING_TANK_2, + MACHINE_CASING_TANK_3, + MACHINE_CASING_TANK_4, + + MACHINE_CASING_TANK_5, + MACHINE_CASING_TANK_6, + MACHINE_CASING_TANK_7, + MACHINE_CASING_TANK_8, + + MACHINE_CASING_TANK_9, + MACHINE_CASING_TANK_10, + MACHINE_CASING_TANK_11, + MACHINE_CASING_TANK_12, + + MACHINE_CASING_TANK_13, + MACHINE_CASING_TANK_14, + MACHINE_CASING_TANK_0, + + BLOCK_STEELEAF, + BLOCK_ICHORIUM, + BLOCK_FIRESTONE, + BLOCK_SHADOW, + + OVERLAY_ENERGY_IN_POWER, + OVERLAY_ENERGY_OUT_POWER, + OVERLAY_AUTOMAINTENANCE, + OVERLAY_AUTOMAINTENANCE_GLOW, + OVERLAY_AUTOMAINTENANCE_IDLE, + OVERLAY_AUTOMAINTENANCE_IDLE_GLOW, + + // + VOID // The Empty Texture + , + RENDERING_ERROR, + PIPE_RESTRICTOR, + INSULATION_FULL, + INSULATION_TINY, + INSULATION_SMALL, + INSULATION_MEDIUM, + INSULATION_MEDIUM_PLUS, + INSULATION_LARGE, + INSULATION_HUGE, + CFOAM_FRESH, + + CFOAM_HARDENED, + SOLARPANEL, + SOLARPANEL_8V, + SOLARPANEL_LV, + SOLARPANEL_MV, + SOLARPANEL_HV, + SOLARPANEL_EV, + SOLARPANEL_IV, + SOLARPANEL_LuV, + SOLARPANEL_ZPM, + + SOLARPANEL_UV, + SOLARPANEL_UHV, + SOLARPANEL_UEV, + SOLARPANEL_UIV, + VENT_NORMAL, + VENT_ADVANCED, + COVER_WOOD_PLATE, + ARROW_UP, + ARROW_UP_GLOW, + ARROW_DOWN, + ARROW_DOWN_GLOW, + ARROW_LEFT, + ARROW_LEFT_GLOW, + ARROW_RIGHT, + ARROW_RIGHT_GLOW, + AUTOMATION_FILTER, + AUTOMATION_FILTER_GLOW, + AUTOMATION_TYPEFILTER, + AUTOMATION_TYPEFILTER_GLOW, + AUTOMATION_RECIPEFILTER, + AUTOMATION_RECIPEFILTER_GLOW, + + AUTOMATION_CHESTBUFFER, + AUTOMATION_CHESTBUFFER_GLOW, + AUTOMATION_SUPERBUFFER, + AUTOMATION_SUPERBUFFER_GLOW, + AUTOMATION_REGULATOR, + AUTOMATION_REGULATOR_GLOW, + AUTOMATION_ITEMDISTRIBUTOR, + AUTOMATION_ITEMDISTRIBUTOR_GLOW, + CONCRETE_LIGHT_STONE, + CONCRETE_LIGHT_COBBLE, + CONCRETE_LIGHT_COBBLE_MOSSY, + + CONCRETE_LIGHT_BRICKS, + CONCRETE_LIGHT_BRICKS_CRACKED, + CONCRETE_LIGHT_BRICKS_MOSSY, + CONCRETE_LIGHT_BRICKS_CHISELED, + CONCRETE_LIGHT_SMOOTH, + CONCRETE_DARK_STONE, + + CONCRETE_DARK_COBBLE, + CONCRETE_DARK_COBBLE_MOSSY, + CONCRETE_DARK_BRICKS, + CONCRETE_DARK_BRICKS_CRACKED, + CONCRETE_DARK_BRICKS_MOSSY, + CONCRETE_DARK_BRICKS_CHISELED, + + CONCRETE_DARK_SMOOTH, + GRANITE_BLACK_STONE, + GRANITE_BLACK_COBBLE, + GRANITE_BLACK_COBBLE_MOSSY, + GRANITE_BLACK_BRICKS, + GRANITE_BLACK_BRICKS_CRACKED, + GRANITE_BLACK_BRICKS_MOSSY, + + GRANITE_BLACK_BRICKS_CHISELED, + GRANITE_BLACK_SMOOTH, + GRANITE_RED_STONE, + GRANITE_RED_COBBLE, + GRANITE_RED_COBBLE_MOSSY, + GRANITE_RED_BRICKS, + GRANITE_RED_BRICKS_CRACKED, + + GRANITE_RED_BRICKS_MOSSY, + GRANITE_RED_BRICKS_CHISELED, + GRANITE_RED_SMOOTH, + MACHINE_BRONZEBRICKS_TOP, + MACHINE_BRONZEBRICKS_SIDE, + MACHINE_BRONZEBRICKS_BOTTOM, + + MACHINE_STEELBRICKS_TOP, + MACHINE_STEELBRICKS_SIDE, + MACHINE_STEELBRICKS_BOTTOM, + MACHINE_BRONZE_TOP, + MACHINE_BRONZE_SIDE, + MACHINE_BRONZE_BOTTOM, + MACHINE_STEEL_TOP, + + MACHINE_STEEL_SIDE, + MACHINE_STEEL_BOTTOM, + MACHINE_8V_TOP, + MACHINE_8V_SIDE, + MACHINE_8V_BOTTOM, + MACHINE_LV_TOP, + MACHINE_LV_SIDE, + MACHINE_LV_BOTTOM, + MACHINE_MV_TOP, + + MACHINE_MV_SIDE, + MACHINE_MV_BOTTOM, + MACHINE_HV_TOP, + MACHINE_HV_SIDE, + MACHINE_HV_BOTTOM, + MACHINE_EV_TOP, + MACHINE_EV_SIDE, + MACHINE_EV_BOTTOM, + MACHINE_IV_TOP, + + MACHINE_IV_SIDE, + MACHINE_IV_BOTTOM, + MACHINE_LuV_TOP, + MACHINE_LuV_SIDE, + MACHINE_LuV_BOTTOM, + MACHINE_ZPM_TOP, + MACHINE_ZPM_SIDE, + MACHINE_ZPM_BOTTOM, + MACHINE_UV_TOP, + + MACHINE_UV_SIDE, + MACHINE_UV_BOTTOM, + MACHINE_MAX_TOP, + MACHINE_MAX_SIDE, + MACHINE_MAX_BOTTOM, + MACHINE_BRONZEPLATEDBRICKS, + MACHINE_HEATPROOFCASING, + MACHINE_DIM_TRANS_CASING, + MACHINE_DIM_INJECTOR, + MACHINE_DIM_BRIDGE, + MACHINE_COIL_SUPERCONDUCTOR, + + MACHINE_BRONZEBLASTFURNACE, + MACHINE_BRONZEBLASTFURNACE_ACTIVE, + MACHINE_BRONZEBLASTFURNACE_ACTIVE_GLOW, + MACHINE_CASING_ROBUST_TUNGSTENSTEEL, + MACHINE_CASING_CLEAN_STAINLESSSTEEL, + MACHINE_CASING_STABLE_TITANIUM, + MACHINE_CASING_MINING_OSMIRIDIUM, + MACHINE_CASING_MINING_NEUTRONIUM, + MACHINE_CASING_MINING_BLACKPLUTONIUM, + MACHINE_CASING_RHODIUM_PALLADIUM, + MACHINE_CASING_IRIDIUM, + MACHINE_CASING_MAGICAL, + MACHINE_CASING_RADIANT_NAQUADAH_ALLOY, + + MACHINE_CASING_FIREBOX_TITANIUM, + MACHINE_CASING_FUSION_COIL, + MACHINE_CASING_FUSION, + MACHINE_CASING_FUSION_GLASS, + MACHINE_CASING_FUSION_GLASS_YELLOW, + MACHINE_CASING_FUSION_GLASS_YELLOW_GLOW, + MACHINE_CASING_FUSION_2, + + MACHINE_CASING_MAGIC, + MACHINE_CASING_MAGIC_GLOW, + MACHINE_CASING_MAGIC_ACTIVE, + MACHINE_CASING_MAGIC_ACTIVE_GLOW, + MACHINE_CASING_MAGIC_FRONT, + MACHINE_CASING_MAGIC_FRONT_GLOW, + MACHINE_CASING_MAGIC_FRONT_ACTIVE, + MACHINE_CASING_MAGIC_FRONT_ACTIVE_GLOW, + MACHINE_CASING_DRAGONEGG, + MACHINE_CASING_DRAGONEGG_GLOW, + MACHINE_CASING_SOLID_STEEL, + + MACHINE_CASING_FROST_PROOF, + MACHINE_CASING_PUMP, + MACHINE_CASING_MOTOR, + MACHINE_CASING_PIPE_BRONZE, + MACHINE_CASING_PIPE_STEEL, + MACHINE_CASING_PIPE_TITANIUM, + MACHINE_CASING_PIPE_TUNGSTENSTEEL, + MACHINE_CASING_PIPE_POLYTETRAFLUOROETHYLENE, + MACHINE_CASING_PIPE_POLYBENZIMIDAZOLE, + + MACHINE_CASING_GEARBOX_BRONZE, + MACHINE_CASING_GEARBOX_STEEL, + MACHINE_CASING_GEARBOX_TITANIUM, + MACHINE_CASING_GEARBOX_TUNGSTENSTEEL, + MACHINE_CASING_DATA_DRIVE, + MACHINE_CASING_CONTAINMENT_FIELD, + + MACHINE_CASING_ASSEMBLER, + MACHINE_CASING_PROCESSOR, + MACHINE_CASING_STRIPES_A, + MACHINE_CASING_STRIPES_B, + MACHINE_CASING_RADIOACTIVEHAZARD, + MACHINE_CASING_BIOHAZARD, + MACHINE_CASING_EXPLOSIONHAZARD, + + MACHINE_CASING_FIREHAZARD, + MACHINE_CASING_ACIDHAZARD, + MACHINE_CASING_MAGICHAZARD, + MACHINE_CASING_FROSTHAZARD, + MACHINE_CASING_NOISEHAZARD, + MACHINE_CASING_GRATE, + MACHINE_CASING_VENT, + MACHINE_CASING_VENT_T2, + + MACHINE_CASING_RADIATIONPROOF, + MACHINE_CASING_ADVANCEDRADIATIONPROOF, + MACHINE_CASING_FIREBOX_BRONZE, + MACHINE_CASING_FIREBOX_STEEL, + MACHINE_CASING_FIREBOX_TUNGSTENSTEEL, + MACHINE_CASING_ENGINE_INTAKE, + MACHINE_CASING_EXTREME_ENGINE_INTAKE, // changed color in a terrible way + MACHINE_CASING_CHEMICALLY_INERT, + MACHINE_COIL_CUPRONICKEL, + + MACHINE_CASING_DENSEBRICKS, + MACHINE_CASING_BRICKEDBLASTFURNACE_ACTIVE, + MACHINE_CASING_BRICKEDBLASTFURNACE_ACTIVE_GLOW, + MACHINE_CASING_BRICKEDBLASTFURNACE_INACTIVE, + + MACHINE_COIL_KANTHAL, + MACHINE_COIL_NICHROME, + MACHINE_COIL_TUNGSTENSTEEL, + MACHINE_COIL_HSSG, + MACHINE_COIL_NAQUADAH, + MACHINE_COIL_NAQUADAHALLOY, + MACHINE_COIL_ELECTRUMFLUX, + MACHINE_COIL_AWAKENEDDRACONIUM, + MACHINE_COIL_HSSS, + MACHINE_COIL_TRINIUM, + MACHINE_COIL_INFINITY, + MACHINE_COIL_HYPOGEN, + MACHINE_COIL_ETERNAL, + BOILER_SOLAR, + BOILER_FRONT, + BOILER_FRONT_GLOW, + + BOILER_FRONT_ACTIVE, + BOILER_FRONT_ACTIVE_GLOW, + BOILER_LAVA_FRONT, + BOILER_LAVA_FRONT_GLOW, + BOILER_LAVA_FRONT_ACTIVE, + BOILER_LAVA_FRONT_ACTIVE_GLOW, + + NAQUADAH_REACTOR_SOLID_BACK, + NAQUADAH_REACTOR_SOLID_BACK_GLOW, + NAQUADAH_REACTOR_SOLID_FRONT, + NAQUADAH_REACTOR_SOLID_FRONT_GLOW, + NAQUADAH_REACTOR_SOLID_SIDE, + NAQUADAH_REACTOR_SOLID_SIDE_GLOW, + NAQUADAH_REACTOR_SOLID_BOTTOM, + NAQUADAH_REACTOR_SOLID_BOTTOM_GLOW, + NAQUADAH_REACTOR_SOLID_TOP, + NAQUADAH_REACTOR_SOLID_TOP_GLOW, + NAQUADAH_REACTOR_SOLID_BACK_ACTIVE, + NAQUADAH_REACTOR_SOLID_BACK_ACTIVE_GLOW, + NAQUADAH_REACTOR_SOLID_FRONT_ACTIVE, + NAQUADAH_REACTOR_SOLID_FRONT_ACTIVE_GLOW, + NAQUADAH_REACTOR_SOLID_SIDE_ACTIVE, + NAQUADAH_REACTOR_SOLID_SIDE_ACTIVE_GLOW, + NAQUADAH_REACTOR_SOLID_BOTTOM_ACTIVE, + NAQUADAH_REACTOR_SOLID_BOTTOM_ACTIVE_GLOW, + NAQUADAH_REACTOR_SOLID_TOP_ACTIVE, + NAQUADAH_REACTOR_SOLID_TOP_ACTIVE_GLOW, + + NAQUADAH_REACTOR_FLUID_BACK, + NAQUADAH_REACTOR_FLUID_BACK_GLOW, + NAQUADAH_REACTOR_FLUID_FRONT, + NAQUADAH_REACTOR_FLUID_FRONT_GLOW, + NAQUADAH_REACTOR_FLUID_SIDE, + NAQUADAH_REACTOR_FLUID_SIDE_GLOW, + NAQUADAH_REACTOR_FLUID_BOTTOM, + NAQUADAH_REACTOR_FLUID_BOTTOM_GLOW, + NAQUADAH_REACTOR_FLUID_TOP, + NAQUADAH_REACTOR_FLUID_TOP_GLOW, + NAQUADAH_REACTOR_FLUID_BACK_ACTIVE, + NAQUADAH_REACTOR_FLUID_BACK_ACTIVE_GLOW, + NAQUADAH_REACTOR_FLUID_FRONT_ACTIVE, + NAQUADAH_REACTOR_FLUID_FRONT_ACTIVE_GLOW, + NAQUADAH_REACTOR_FLUID_SIDE_ACTIVE, + NAQUADAH_REACTOR_FLUID_SIDE_ACTIVE_GLOW, + NAQUADAH_REACTOR_FLUID_BOTTOM_ACTIVE, + NAQUADAH_REACTOR_FLUID_BOTTOM_ACTIVE_GLOW, + NAQUADAH_REACTOR_FLUID_TOP_ACTIVE, + NAQUADAH_REACTOR_FLUID_TOP_ACTIVE_GLOW, + + DIESEL_GENERATOR_BACK, + DIESEL_GENERATOR_BACK_GLOW, + DIESEL_GENERATOR_FRONT, + DIESEL_GENERATOR_FRONT_GLOW, + DIESEL_GENERATOR_SIDE, + DIESEL_GENERATOR_SIDE_GLOW, + DIESEL_GENERATOR_BOTTOM, + DIESEL_GENERATOR_BOTTOM_GLOW, + DIESEL_GENERATOR_TOP, + DIESEL_GENERATOR_TOP_GLOW, + DIESEL_GENERATOR_BACK_ACTIVE, + DIESEL_GENERATOR_BACK_ACTIVE_GLOW, + DIESEL_GENERATOR_FRONT_ACTIVE, + DIESEL_GENERATOR_FRONT_ACTIVE_GLOW, + DIESEL_GENERATOR_SIDE_ACTIVE, + DIESEL_GENERATOR_SIDE_ACTIVE_GLOW, + DIESEL_GENERATOR_BOTTOM_ACTIVE, + DIESEL_GENERATOR_BOTTOM_ACTIVE_GLOW, + DIESEL_GENERATOR_TOP_ACTIVE, + DIESEL_GENERATOR_TOP_ACTIVE_GLOW, + + GAS_TURBINE_BACK, + GAS_TURBINE_BACK_GLOW, + GAS_TURBINE_FRONT, + GAS_TURBINE_FRONT_GLOW, + GAS_TURBINE_SIDE, + GAS_TURBINE_SIDE_GLOW, + GAS_TURBINE_BOTTOM, + GAS_TURBINE_BOTTOM_GLOW, + GAS_TURBINE_TOP, + GAS_TURBINE_TOP_GLOW, + GAS_TURBINE_BACK_ACTIVE, + GAS_TURBINE_BACK_ACTIVE_GLOW, + GAS_TURBINE_FRONT_ACTIVE, + GAS_TURBINE_FRONT_ACTIVE_GLOW, + GAS_TURBINE_SIDE_ACTIVE, + GAS_TURBINE_SIDE_ACTIVE_GLOW, + GAS_TURBINE_BOTTOM_ACTIVE, + GAS_TURBINE_BOTTOM_ACTIVE_GLOW, + GAS_TURBINE_TOP_ACTIVE, + GAS_TURBINE_TOP_ACTIVE_GLOW, + + STEAM_TURBINE_BACK, + STEAM_TURBINE_BACK_GLOW, + STEAM_TURBINE_FRONT, + STEAM_TURBINE_FRONT_GLOW, + STEAM_TURBINE_SIDE, + STEAM_TURBINE_SIDE_GLOW, + STEAM_TURBINE_BOTTOM, + STEAM_TURBINE_BOTTOM_GLOW, + STEAM_TURBINE_TOP, + STEAM_TURBINE_TOP_GLOW, + STEAM_TURBINE_BACK_ACTIVE, + STEAM_TURBINE_BACK_ACTIVE_GLOW, + STEAM_TURBINE_FRONT_ACTIVE, + STEAM_TURBINE_FRONT_ACTIVE_GLOW, + STEAM_TURBINE_SIDE_ACTIVE, + STEAM_TURBINE_SIDE_ACTIVE_GLOW, + STEAM_TURBINE_BOTTOM_ACTIVE, + STEAM_TURBINE_BOTTOM_ACTIVE_GLOW, + STEAM_TURBINE_TOP_ACTIVE, + STEAM_TURBINE_TOP_ACTIVE_GLOW, + + BLOCK_BRONZEPREIN, + BLOCK_STEELPREIN, + BLOCK_TITANIUMPREIN, + BLOCK_NAQUADAHPREIN, + BLOCK_NEUTRONIUMPREIN, + BLOCK_DEEP_DARK_RAW, + BLOCK_IRREIN, + BLOCK_PLASCRETE, + BLOCK_TSREIN, + + OVERLAY_LOCKER, + OVERLAY_LOCKER_000, + OVERLAY_LOCKER_001, + OVERLAY_LOCKER_002, + OVERLAY_LOCKER_003, + OVERLAY_LOCKER_004, + OVERLAY_LOCKER_005, + OVERLAY_LOCKER_006, + OVERLAY_LOCKER_007, + OVERLAY_LOCKER_008, + OVERLAY_LOCKER_009, + OVERLAY_LOCKER_010, + OVERLAY_LOCKER_011, + OVERLAY_LOCKER_012, + OVERLAY_LOCKER_013, + + OVERLAY_LENS, + OVERLAY_PIPE, + OVERLAY_PIPE_IN, + OVERLAY_PIPE_OUT, + OVERLAY_INPUT_HATCH_2x2, + FLUID_OUT_SIGN, + FLUID_IN_SIGN, + ITEM_IN_SIGN, + ITEM_OUT_SIGN, + OVERLAY_MUFFLER, + + OVERLAY_CONTROLLER, + OVERLAY_ACTIVITYDETECTOR, + OVERLAY_ACTIVITYDETECTOR_GLOW, + OVERLAY_ENERGYDETECTOR, + OVERLAY_FLUIDDETECTOR, + OVERLAY_ITEMDETECTOR, + + OVERLAY_REDSTONE_TRANSMITTER, + OVERLAY_REDSTONE_RECEIVER, + OVERLAY_MAINTENANCE_DETECTOR, + + OVERLAY_ADVANCED_REDSTONE_TRANSMITTER, + OVERLAY_ADVANCED_REDSTONE_RECEIVER, + OVERLAY_WIRELESS_ITEM_DETECTOR, + OVERLAY_WIRELESS_FLUID_DETECTOR, + OVERLAY_WIRELESS_MAINTENANCE_DETECTOR, + OVERLAY_WIRELESS_ACTIVITYDETECTOR, + OVERLAY_METRICS_TRANSMITTER, + + OVERLAY_FLUID_STORAGE_MONITOR0, + OVERLAY_FLUID_STORAGE_MONITOR1, + OVERLAY_FLUID_STORAGE_MONITOR2, + OVERLAY_FLUID_STORAGE_MONITOR3, + OVERLAY_FLUID_STORAGE_MONITOR4, + OVERLAY_FLUID_STORAGE_MONITOR5, + OVERLAY_FLUID_STORAGE_MONITOR6, + OVERLAY_FLUID_STORAGE_MONITOR7, + OVERLAY_FLUID_STORAGE_MONITOR8, + OVERLAY_FLUID_STORAGE_MONITOR9, + OVERLAY_FLUID_STORAGE_MONITOR10, + OVERLAY_FLUID_STORAGE_MONITOR11, + OVERLAY_FLUID_STORAGE_MONITOR12, + OVERLAY_FLUID_STORAGE_MONITOR13, + OVERLAY_FLUID_STORAGE_MONITOR14, + + OVERLAY_DTPF_OFF, + OVERLAY_DTPF_ON, + OVERLAY_FUSION1, + OVERLAY_FUSION1_GLOW, + OVERLAY_FUSION2, + OVERLAY_FUSION2_GLOW, + OVERLAY_FUSION3, + OVERLAY_FUSION3_GLOW, + OVERLAY_SCREEN, + OVERLAY_SCREEN_GLOW, + OVERLAY_QTANK, + OVERLAY_QTANK_GLOW, + OVERLAY_QCHEST, + OVERLAY_QCHEST_GLOW, + OVERLAY_SHUTTER, + + OVERLAY_CLOSET, + OVERLAY_DUCTTAPE, + OVERLAY_MAINTENANCE, + OVERLAY_DATA_ACCESS, + OVERLAY_CONVEYOR, + OVERLAY_PUMP, + OVERLAY_VALVE, + OVERLAY_ARM, + OVERLAY_DRAIN, + OVERLAY_CRAFTING, + OVERLAY_ENERGY_IN, + OVERLAY_ENERGY_OUT, + + OVERLAY_ENERGY_IN_MULTI, + OVERLAY_ENERGY_OUT_MULTI, + OVERLAY_FRONT_LARGE_BOILER, + OVERLAY_FRONT_LARGE_BOILER_GLOW, + OVERLAY_FRONT_LARGE_BOILER_ACTIVE, + OVERLAY_FRONT_LARGE_BOILER_ACTIVE_GLOW, + OVERLAY_FRONT_VACUUM_FREEZER, + OVERLAY_FRONT_VACUUM_FREEZER_GLOW, + OVERLAY_FRONT_VACUUM_FREEZER_ACTIVE, + OVERLAY_FRONT_VACUUM_FREEZER_ACTIVE_GLOW, + OVERLAY_ENERGY_ON_WIRELESS, + OVERLAY_ENERGY_OFF_WIRELESS, + + OVERLAY_FRONT_MULTI_SMELTER, + OVERLAY_FRONT_MULTI_SMELTER_GLOW, + OVERLAY_FRONT_MULTI_SMELTER_ACTIVE, + OVERLAY_FRONT_MULTI_SMELTER_ACTIVE_GLOW, + OVERLAY_FRONT_ELECTRIC_BLAST_FURNACE, + OVERLAY_FRONT_ELECTRIC_BLAST_FURNACE_GLOW, + OVERLAY_FRONT_ELECTRIC_BLAST_FURNACE_ACTIVE, + OVERLAY_FRONT_ELECTRIC_BLAST_FURNACE_ACTIVE_GLOW, + OVERLAY_FRONT_IMPLOSION_COMPRESSOR, + OVERLAY_FRONT_IMPLOSION_COMPRESSOR_GLOW, + OVERLAY_FRONT_IMPLOSION_COMPRESSOR_ACTIVE, + OVERLAY_FRONT_IMPLOSION_COMPRESSOR_ACTIVE_GLOW, + OVERLAY_FRONT_RESEARCH_COMPLETER, + OVERLAY_FRONT_RESEARCH_COMPLETER_GLOW, + OVERLAY_FRONT_RESEARCH_COMPLETER_ACTIVE, + OVERLAY_FRONT_RESEARCH_COMPLETER_ACTIVE_GLOW, + + OVERLAY_TOP_POTIONBREWER, + OVERLAY_TOP_POTIONBREWER_GLOW, + OVERLAY_TOP_REPLICATOR, + OVERLAY_TOP_REPLICATOR_GLOW, + OVERLAY_TOP_MASSFAB, + OVERLAY_TOP_MASSFAB_GLOW, + OVERLAY_TOP_STEAM_HAMMER, + OVERLAY_TOP_STEAM_HAMMER_GLOW, + OVERLAY_TOP_STEAM_FURNACE, + OVERLAY_TOP_STEAM_FURNACE_GLOW, + OVERLAY_TOP_STEAM_ALLOY_SMELTER, + OVERLAY_TOP_STEAM_ALLOY_SMELTER_GLOW, + + OVERLAY_TOP_STEAM_MACERATOR, + OVERLAY_TOP_STEAM_MACERATOR_GLOW, + OVERLAY_TOP_STEAM_COMPRESSOR, + OVERLAY_TOP_STEAM_COMPRESSOR_GLOW, + OVERLAY_TOP_STEAM_EXTRACTOR, + OVERLAY_TOP_STEAM_EXTRACTOR_GLOW, + OVERLAY_TOP_DISASSEMBLER, + OVERLAY_TOP_DISASSEMBLER_GLOW, + OVERLAY_TOP_BOXINATOR, + OVERLAY_TOP_BOXINATOR_GLOW, + OVERLAY_TOP_ROCK_BREAKER, + OVERLAY_TOP_ROCK_BREAKER_GLOW, + OVERLAY_TOP_SCANNER, + OVERLAY_TOP_SCANNER_GLOW, + OVERLAY_TOP_INDUSTRIAL_APIARY, + OVERLAY_TOP_INDUSTRIAL_APIARY_GLOW, + + OVERLAY_FRONT_POTIONBREWER, + OVERLAY_FRONT_POTIONBREWER_GLOW, + OVERLAY_FRONT_REPLICATOR, + OVERLAY_FRONT_REPLICATOR_GLOW, + OVERLAY_FRONT_MASSFAB, + OVERLAY_FRONT_MASSFAB_GLOW, + OVERLAY_FRONT_STEAM_HAMMER, + OVERLAY_FRONT_STEAM_HAMMER_GLOW, + OVERLAY_FRONT_STEAM_HAMMER_ACTIVE, + OVERLAY_FRONT_STEAM_HAMMER_ACTIVE_GLOW, + OVERLAY_FRONT_STEAM_FURNACE, + OVERLAY_FRONT_STEAM_FURNACE_GLOW, + OVERLAY_FRONT_STEAM_ALLOY_SMELTER, + OVERLAY_FRONT_STEAM_ALLOY_SMELTER_GLOW, + + OVERLAY_FRONT_STEAM_MACERATOR, + OVERLAY_FRONT_STEAM_MACERATOR_GLOW, + OVERLAY_FRONT_STEAM_MACERATOR_ACTIVE, + OVERLAY_FRONT_STEAM_MACERATOR_ACTIVE_GLOW, + OVERLAY_FRONT_STEAM_COMPRESSOR, + OVERLAY_FRONT_STEAM_COMPRESSOR_GLOW, + OVERLAY_FRONT_STEAM_EXTRACTOR, + OVERLAY_FRONT_STEAM_EXTRACTOR_GLOW, + OVERLAY_FRONT_DISASSEMBLER, + OVERLAY_FRONT_DISASSEMBLER_GLOW, + OVERLAY_FRONT_DISASSEMBLER_ACTIVE, + OVERLAY_FRONT_DISASSEMBLER_ACTIVE_GLOW, + OVERLAY_FRONT_BOXINATOR, + OVERLAY_FRONT_BOXINATOR_GLOW, + OVERLAY_FRONT_ROCK_BREAKER, + OVERLAY_FRONT_ROCK_BREAKER_GLOW, + OVERLAY_FRONT_SCANNER, + OVERLAY_FRONT_SCANNER_GLOW, + OVERLAY_FRONT_INDUSTRIAL_APIARY, + OVERLAY_FRONT_INDUSTRIAL_APIARY_GLOW, + + OVERLAY_BOTTOM_POTIONBREWER, + OVERLAY_BOTTOM_POTIONBREWER_GLOW, + OVERLAY_BOTTOM_REPLICATOR, + OVERLAY_BOTTOM_REPLICATOR_GLOW, + OVERLAY_BOTTOM_MASSFAB, + OVERLAY_BOTTOM_MASSFAB_GLOW, + OVERLAY_BOTTOM_STEAM_HAMMER, + OVERLAY_BOTTOM_STEAM_HAMMER_GLOW, + OVERLAY_BOTTOM_STEAM_FURNACE, + OVERLAY_BOTTOM_STEAM_FURNACE_GLOW, + + OVERLAY_BOTTOM_STEAM_ALLOY_SMELTER, + OVERLAY_BOTTOM_STEAM_ALLOY_SMELTER_GLOW, + OVERLAY_BOTTOM_STEAM_MACERATOR, + OVERLAY_BOTTOM_STEAM_MACERATOR_GLOW, + OVERLAY_BOTTOM_STEAM_COMPRESSOR, + OVERLAY_BOTTOM_STEAM_COMPRESSOR_GLOW, + OVERLAY_BOTTOM_STEAM_EXTRACTOR, + OVERLAY_BOTTOM_STEAM_EXTRACTOR_GLOW, + OVERLAY_BOTTOM_DISASSEMBLER, + OVERLAY_BOTTOM_DISASSEMBLER_GLOW, + OVERLAY_BOTTOM_BOXINATOR, + OVERLAY_BOTTOM_BOXINATOR_GLOW, + OVERLAY_BOTTOM_ROCK_BREAKER, + OVERLAY_BOTTOM_ROCK_BREAKER_GLOW, + OVERLAY_BOTTOM_SCANNER, + OVERLAY_BOTTOM_SCANNER_GLOW, + OVERLAY_BOTTOM_INDUSTRIAL_APIARY, + OVERLAY_BOTTOM_INDUSTRIAL_APIARY_GLOW, + + OVERLAY_SIDE_POTIONBREWER, + OVERLAY_SIDE_POTIONBREWER_GLOW, + OVERLAY_SIDE_REPLICATOR, + OVERLAY_SIDE_REPLICATOR_GLOW, + OVERLAY_SIDE_MASSFAB, + OVERLAY_SIDE_MASSFAB_GLOW, + OVERLAY_SIDE_STEAM_HAMMER, + OVERLAY_SIDE_STEAM_HAMMER_GLOW, + OVERLAY_SIDE_STEAM_FURNACE, + OVERLAY_SIDE_STEAM_FURNACE_GLOW, + OVERLAY_SIDE_STEAM_ALLOY_SMELTER, + OVERLAY_SIDE_STEAM_ALLOY_SMELTER_GLOW, + OVERLAY_SIDE_STEAM_MACERATOR, + OVERLAY_SIDE_STEAM_MACERATOR_GLOW, + OVERLAY_SIDE_STEAM_COMPRESSOR, + OVERLAY_SIDE_STEAM_COMPRESSOR_GLOW, + OVERLAY_SIDE_STEAM_EXTRACTOR, + OVERLAY_SIDE_STEAM_EXTRACTOR_GLOW, + OVERLAY_SIDE_DISASSEMBLER, + OVERLAY_SIDE_DISASSEMBLER_GLOW, + OVERLAY_SIDE_BOXINATOR, + OVERLAY_SIDE_BOXINATOR_GLOW, + OVERLAY_SIDE_ROCK_BREAKER, + OVERLAY_SIDE_ROCK_BREAKER_GLOW, + OVERLAY_SIDE_SCANNER, + OVERLAY_SIDE_SCANNER_GLOW, + OVERLAY_SIDE_INDUSTRIAL_APIARY, + OVERLAY_SIDE_INDUSTRIAL_APIARY_GLOW, + + OVERLAY_TOP_POTIONBREWER_ACTIVE, + OVERLAY_TOP_POTIONBREWER_ACTIVE_GLOW, + OVERLAY_TOP_REPLICATOR_ACTIVE, + OVERLAY_TOP_REPLICATOR_ACTIVE_GLOW, + OVERLAY_TOP_MASSFAB_ACTIVE, + OVERLAY_TOP_MASSFAB_ACTIVE_GLOW, + + OVERLAY_TOP_STEAM_HAMMER_ACTIVE, + OVERLAY_TOP_STEAM_HAMMER_ACTIVE_GLOW, + OVERLAY_TOP_STEAM_FURNACE_ACTIVE, + OVERLAY_TOP_STEAM_FURNACE_ACTIVE_GLOW, + OVERLAY_TOP_STEAM_ALLOY_SMELTER_ACTIVE, + OVERLAY_TOP_STEAM_ALLOY_SMELTER_ACTIVE_GLOW, + OVERLAY_TOP_STEAM_MACERATOR_ACTIVE, + OVERLAY_TOP_STEAM_MACERATOR_ACTIVE_GLOW, + OVERLAY_TOP_STEAM_COMPRESSOR_ACTIVE, + OVERLAY_TOP_STEAM_COMPRESSOR_ACTIVE_GLOW, + + OVERLAY_TOP_STEAM_EXTRACTOR_ACTIVE, + OVERLAY_TOP_STEAM_EXTRACTOR_ACTIVE_GLOW, + OVERLAY_TOP_DISASSEMBLER_ACTIVE, + OVERLAY_TOP_DISASSEMBLER_ACTIVE_GLOW, + OVERLAY_TOP_BOXINATOR_ACTIVE, + OVERLAY_TOP_BOXINATOR_ACTIVE_GLOW, + OVERLAY_TOP_ROCK_BREAKER_ACTIVE, + OVERLAY_TOP_ROCK_BREAKER_ACTIVE_GLOW, + OVERLAY_TOP_SCANNER_ACTIVE, + OVERLAY_TOP_SCANNER_ACTIVE_GLOW, + OVERLAY_TOP_INDUSTRIAL_APIARY_ACTIVE, + OVERLAY_TOP_INDUSTRIAL_APIARY_ACTIVE_GLOW, + + OVERLAY_FRONT_POTIONBREWER_ACTIVE, + OVERLAY_FRONT_POTIONBREWER_ACTIVE_GLOW, + OVERLAY_FRONT_REPLICATOR_ACTIVE, + OVERLAY_FRONT_REPLICATOR_ACTIVE_GLOW, + OVERLAY_FRONT_MASSFAB_ACTIVE, + OVERLAY_FRONT_MASSFAB_ACTIVE_GLOW, + OVERLAY_FRONT_STEAM_FURNACE_ACTIVE, + OVERLAY_FRONT_STEAM_FURNACE_ACTIVE_GLOW, + OVERLAY_FRONT_STEAM_ALLOY_SMELTER_ACTIVE, + OVERLAY_FRONT_STEAM_ALLOY_SMELTER_ACTIVE_GLOW, + + OVERLAY_FRONT_STEAM_COMPRESSOR_ACTIVE, + OVERLAY_FRONT_STEAM_COMPRESSOR_ACTIVE_GLOW, + OVERLAY_FRONT_STEAM_EXTRACTOR_ACTIVE, + OVERLAY_FRONT_STEAM_EXTRACTOR_ACTIVE_GLOW, + OVERLAY_FRONT_BOXINATOR_ACTIVE, + OVERLAY_FRONT_BOXINATOR_ACTIVE_GLOW, + OVERLAY_FRONT_ROCK_BREAKER_ACTIVE, + OVERLAY_FRONT_ROCK_BREAKER_ACTIVE_GLOW, + OVERLAY_FRONT_SCANNER_ACTIVE, + OVERLAY_FRONT_SCANNER_ACTIVE_GLOW, + OVERLAY_FRONT_INDUSTRIAL_APIARY_ACTIVE, + OVERLAY_FRONT_INDUSTRIAL_APIARY_ACTIVE_GLOW, + + OVERLAY_BOTTOM_POTIONBREWER_ACTIVE, + OVERLAY_BOTTOM_POTIONBREWER_ACTIVE_GLOW, + OVERLAY_BOTTOM_REPLICATOR_ACTIVE, + OVERLAY_BOTTOM_REPLICATOR_ACTIVE_GLOW, + OVERLAY_BOTTOM_MASSFAB_ACTIVE, + OVERLAY_BOTTOM_MASSFAB_ACTIVE_GLOW, + OVERLAY_BOTTOM_STEAM_HAMMER_ACTIVE, + OVERLAY_BOTTOM_STEAM_HAMMER_ACTIVE_GLOW, + OVERLAY_BOTTOM_STEAM_FURNACE_ACTIVE, + OVERLAY_BOTTOM_STEAM_FURNACE_ACTIVE_GLOW, + OVERLAY_BOTTOM_STEAM_ALLOY_SMELTER_ACTIVE, + OVERLAY_BOTTOM_STEAM_ALLOY_SMELTER_ACTIVE_GLOW, + OVERLAY_BOTTOM_STEAM_MACERATOR_ACTIVE, + OVERLAY_BOTTOM_STEAM_MACERATOR_ACTIVE_GLOW, + OVERLAY_BOTTOM_STEAM_COMPRESSOR_ACTIVE, + OVERLAY_BOTTOM_STEAM_COMPRESSOR_ACTIVE_GLOW, + + OVERLAY_BOTTOM_STEAM_EXTRACTOR_ACTIVE, + OVERLAY_BOTTOM_STEAM_EXTRACTOR_ACTIVE_GLOW, + OVERLAY_BOTTOM_DISASSEMBLER_ACTIVE, + OVERLAY_BOTTOM_DISASSEMBLER_ACTIVE_GLOW, + OVERLAY_BOTTOM_BOXINATOR_ACTIVE, + OVERLAY_BOTTOM_BOXINATOR_ACTIVE_GLOW, + OVERLAY_BOTTOM_ROCK_BREAKER_ACTIVE, + OVERLAY_BOTTOM_ROCK_BREAKER_ACTIVE_GLOW, + OVERLAY_BOTTOM_SCANNER_ACTIVE, + OVERLAY_BOTTOM_SCANNER_ACTIVE_GLOW, + OVERLAY_BOTTOM_INDUSTRIAL_APIARY_ACTIVE, + OVERLAY_BOTTOM_INDUSTRIAL_APIARY_ACTIVE_GLOW, + + OVERLAY_SIDE_POTIONBREWER_ACTIVE, + OVERLAY_SIDE_POTIONBREWER_ACTIVE_GLOW, + OVERLAY_SIDE_REPLICATOR_ACTIVE, + OVERLAY_SIDE_REPLICATOR_ACTIVE_GLOW, + OVERLAY_SIDE_MASSFAB_ACTIVE, + OVERLAY_SIDE_MASSFAB_ACTIVE_GLOW, + OVERLAY_SIDE_STEAM_HAMMER_ACTIVE, + OVERLAY_SIDE_STEAM_HAMMER_ACTIVE_GLOW, + OVERLAY_SIDE_STEAM_FURNACE_ACTIVE, + OVERLAY_SIDE_STEAM_FURNACE_ACTIVE_GLOW, + OVERLAY_SIDE_STEAM_ALLOY_SMELTER_ACTIVE, + OVERLAY_SIDE_STEAM_ALLOY_SMELTER_ACTIVE_GLOW, + + OVERLAY_SIDE_STEAM_MACERATOR_ACTIVE, + OVERLAY_SIDE_STEAM_MACERATOR_ACTIVE_GLOW, + OVERLAY_SIDE_STEAM_COMPRESSOR_ACTIVE, + OVERLAY_SIDE_STEAM_COMPRESSOR_ACTIVE_GLOW, + OVERLAY_SIDE_STEAM_EXTRACTOR_ACTIVE, + OVERLAY_SIDE_STEAM_EXTRACTOR_ACTIVE_GLOW, + OVERLAY_SIDE_DISASSEMBLER_ACTIVE, + OVERLAY_SIDE_DISASSEMBLER_ACTIVE_GLOW, + OVERLAY_SIDE_BOXINATOR_ACTIVE, + OVERLAY_SIDE_BOXINATOR_ACTIVE_GLOW, + OVERLAY_SIDE_ROCK_BREAKER_ACTIVE, + OVERLAY_SIDE_ROCK_BREAKER_ACTIVE_GLOW, + OVERLAY_SIDE_SCANNER_ACTIVE, + OVERLAY_SIDE_SCANNER_ACTIVE_GLOW, + OVERLAY_SIDE_INDUSTRIAL_APIARY_ACTIVE, + OVERLAY_SIDE_INDUSTRIAL_APIARY_ACTIVE_GLOW, + + OVERLAY_ADV_PUMP, + OVERLAY_TELEPORTER, + OVERLAY_TELEPORTER_GLOW, + OVERLAY_TELEPORTER_ACTIVE, + OVERLAY_TELEPORTER_ACTIVE_GLOW, + OVERLAY_TELEPORTER_SIDES, + OVERLAY_TELEPORTER_SIDES_GLOW, + FUSIONI_1, + FUSIONI_2, + FUSIONI_3, + FUSIONI_4, + FUSIONI_5, + + FUSIONI_6, + FUSIONI_7, + FUSIONI_8, + FUSIONI_9, + FUSIONI_10, + FUSIONI_11, + FUSIONI_12, + FUSIONII_1, + FUSIONII_2, + FUSIONII_3, + FUSIONII_4, + FUSIONII_5, + FUSIONII_6, + FUSIONII_7, + FUSIONII_8, + FUSIONII_9, + + FUSIONII_10, + FUSIONII_11, + FUSIONII_12, + LARGETURBINE_ST1, + LARGETURBINE_ST2, + LARGETURBINE_ST3, + LARGETURBINE_ST4, + LARGETURBINE_ST5, + LARGETURBINE_ST6, + LARGETURBINE_ST7, + LARGETURBINE_ST8, + LARGETURBINE_ST9, + LARGETURBINE_ST_ACTIVE1, + LARGETURBINE_ST_ACTIVE2, + LARGETURBINE_ST_ACTIVE3, + LARGETURBINE_ST_ACTIVE4, + LARGETURBINE_ST_ACTIVE5, + LARGETURBINE_ST_ACTIVE6, + LARGETURBINE_ST_ACTIVE7, + LARGETURBINE_ST_ACTIVE8, + LARGETURBINE_ST_ACTIVE9, + + LARGETURBINE_SS1, + LARGETURBINE_SS2, + LARGETURBINE_SS3, + LARGETURBINE_SS4, + LARGETURBINE_SS5, + LARGETURBINE_SS6, + LARGETURBINE_SS7, + LARGETURBINE_SS8, + LARGETURBINE_SS9, + LARGETURBINE_SS_ACTIVE1, + LARGETURBINE_SS_ACTIVE2, + LARGETURBINE_SS_ACTIVE3, + LARGETURBINE_SS_ACTIVE4, + LARGETURBINE_SS_ACTIVE5, + LARGETURBINE_SS_ACTIVE6, + LARGETURBINE_SS_ACTIVE7, + LARGETURBINE_SS_ACTIVE8, + LARGETURBINE_SS_ACTIVE9, + + LARGETURBINE_TI1, + LARGETURBINE_TI2, + LARGETURBINE_TI3, + LARGETURBINE_TI4, + LARGETURBINE_TI5, + LARGETURBINE_TI6, + LARGETURBINE_TI7, + LARGETURBINE_TI8, + LARGETURBINE_TI9, + LARGETURBINE_TI_ACTIVE1, + LARGETURBINE_TI_ACTIVE2, + LARGETURBINE_TI_ACTIVE3, + LARGETURBINE_TI_ACTIVE4, + LARGETURBINE_TI_ACTIVE5, + LARGETURBINE_TI_ACTIVE6, + LARGETURBINE_TI_ACTIVE7, + LARGETURBINE_TI_ACTIVE8, + LARGETURBINE_TI_ACTIVE9, + + LARGETURBINE_TU1, + LARGETURBINE_TU2, + LARGETURBINE_TU3, + LARGETURBINE_TU4, + LARGETURBINE_TU5, + LARGETURBINE_TU6, + LARGETURBINE_TU7, + LARGETURBINE_TU8, + LARGETURBINE_TU9, + LARGETURBINE_TU_ACTIVE1, + LARGETURBINE_TU_ACTIVE2, + LARGETURBINE_TU_ACTIVE3, + LARGETURBINE_TU_ACTIVE4, + LARGETURBINE_TU_ACTIVE5, + LARGETURBINE_TU_ACTIVE6, + LARGETURBINE_TU_ACTIVE7, + LARGETURBINE_TU_ACTIVE8, + LARGETURBINE_TU_ACTIVE9, + + MACHINE_CASING_TURBINE, + MACHINE_CASING_ADVANCEDGAS, + BLOCK_ADAMANTIUM, + BLOCK_ALUMINIUM, + BLOCK_AMERICIUM, + + BLOCK_ANNEALEDCOPPER, + BLOCK_ANTIMONY, + BLOCK_ARSENIC, + BLOCK_ASTRALSILVER, + BLOCK_BATTERYALLOY, + BLOCK_BERYLLIUM, + BLOCK_BISMUTH, + BLOCK_BISMUTHBRONZE, + BLOCK_BLACKBRONZE, + BLOCK_BLACKSTEEL, + + BLOCK_BLUEALLOY, + BLOCK_BLUESTEEL, + BLOCK_BRASS, + BLOCK_BRONZE, + BLOCK_CAESIUM, + BLOCK_CERIUM, + BLOCK_CHROME, + BLOCK_CHROMIUMDIOXIDE, + BLOCK_COBALT, + BLOCK_COBALTBRASS, + BLOCK_COPPER, + + BLOCK_CUPRONICKEL, + BLOCK_DAMASCUSSTEEL, + BLOCK_DARKIRON, + BLOCK_DEEPIRON, + BLOCK_DESH, + BLOCK_DURANIUM, + BLOCK_DYSPROSIUM, + BLOCK_ELECTRUM, + BLOCK_ELECTRUMFLUX, + BLOCK_ENDERIUM, + + BLOCK_ERBIUM, + BLOCK_EUROPIUM, + BLOCK_FIERYSTEEL, + BLOCK_GADOLINIUM, + BLOCK_GALLIUM, + BLOCK_HOLMIUM, + BLOCK_HSLA, + BLOCK_INDIUM, + BLOCK_INFUSEDGOLD, + BLOCK_INVAR, + BLOCK_IRIDIUM, + + BLOCK_IRONMAGNETIC, + BLOCK_IRONWOOD, + BLOCK_KANTHAL, + BLOCK_KNIGHTMETAL, + BLOCK_LANTHANUM, + BLOCK_LEAD, + BLOCK_LUTETIUM, + BLOCK_MAGNALIUM, + BLOCK_MAGNESIUM, + BLOCK_MANGANESE, + BLOCK_METEORICIRON, + + BLOCK_METEORICSTEEL, + BLOCK_MIDASIUM, + BLOCK_TRINIUM, + BLOCK_MITHRIL, + BLOCK_MOLYBDENUM, + BLOCK_NAQUADAH, + BLOCK_NAQUADAHALLOY, + BLOCK_NAQUADAHENRICHED, + BLOCK_NAQUADRIA, + BLOCK_NEODYMIUM, + BLOCK_NEODYMIUMMAGNETIC, + + BLOCK_NEUTRONIUM, + BLOCK_NICHROME, + BLOCK_NICKEL, + BLOCK_NIOBIUM, + BLOCK_NIOBIUMNITRIDE, + BLOCK_NIOBIUMTITANIUM, + BLOCK_OSMIRIDIUM, + BLOCK_OSMIUM, + BLOCK_PALLADIUM, + BLOCK_PIGIRON, + BLOCK_PLATINUM, + + BLOCK_PLUTONIUM, + BLOCK_PLUTONIUM241, + BLOCK_PRASEODYMIUM, + BLOCK_PROMETHIUM, + BLOCK_REDALLOY, + BLOCK_REDSTEEL, + BLOCK_ROSEGOLD, + BLOCK_RUBIDIUM, + BLOCK_SAMARIUM, + BLOCK_SCANDIUM, + BLOCK_SHADOWIRON, + + BLOCK_SHADOWSTEEL, + BLOCK_SILICON, + BLOCK_SILVER, + BLOCK_SOLDERINGALLOY, + BLOCK_STAINLESSSTEEL, + BLOCK_STEEL, + BLOCK_STEELMAGNETIC, + BLOCK_STERLINGSILVER, + BLOCK_SUNNARIUM, + BLOCK_TANTALUM, + + BLOCK_TELLURIUM, + BLOCK_TERBIUM, + BLOCK_THAUMIUM, + BLOCK_THORIUM, + BLOCK_THULIUM, + BLOCK_TIN, + BLOCK_TINALLOY, + BLOCK_TITANIUM, + BLOCK_TRITANIUM, + BLOCK_TUNGSTEN, + BLOCK_TUNGSTENSTEEL, + BLOCK_ULTIMET, + BLOCK_SPACETIME, + + BLOCK_URANIUM, + BLOCK_URANIUM235, + BLOCK_VANADIUM, + BLOCK_VANADIUMGALLIUM, + BLOCK_WROUGHTIRON, + BLOCK_YTTRBIUM, + BLOCK_YTTRIUM, + BLOCK_YTTRIUMBARIUMCUPRATE, + BLOCK_ZINC, + BLOCK_TUNGSTENCARBIDE, + + BLOCK_VANADIUMSTEEL, + BLOCK_HSSG, + BLOCK_HSSE, + BLOCK_HSSS, + BLOCK_AERCRYSTAL, + BLOCK_AMBER, + BLOCK_AMETHYST, + BLOCK_AQUACRYSTAL, + BLOCK_BLUETOPAZ, + BLOCK_CERTUSQUARTZ, + BLOCK_DILITHIUM, + + BLOCK_ENDEREYE, + BLOCK_ENDERPEARL, + BLOCK_FOOLSRUBY, + BLOCK_FORCE, + BLOCK_FORCICIUM, + BLOCK_FORCILLIUM, + BLOCK_GREENSAPPHIRE, + BLOCK_IGNISCRYSTAL, + BLOCK_JASPER, + BLOCK_LAZURITE, + + BLOCK_LIGNITE, + BLOCK_MONAZITE, + BLOCK_NITER, + BLOCK_OLIVINE, + BLOCK_OPAL, + BLOCK_ORDOCRYSTAL, + BLOCK_PERDITIOCRYSTAL, + BLOCK_PHOSPHORUS, + BLOCK_QUARTZITE, + BLOCK_REDGARNET, + BLOCK_RUBY, + + BLOCK_SAPPHIRE, + BLOCK_SODALITE, + BLOCK_TANZANITE, + BLOCK_TERRACRYSTAL, + BLOCK_TOPAZ, + BLOCK_VINTEUM, + BLOCK_YELLOWGARNET, + BLOCK_NETHERSTAR, + BLOCK_CHARCOAL, + BLOCK_BLAZE, + BLOCK_CRYOLITE, + MARBLE_STONE, + MARBLE_COBBLE, + BLOCK_NICKELALUMINIUM, + BLOCK_SILICONSG, + BLOCK_TRANSCENDENTMETAL, + BLOCK_UNIVERSIUM, + BLOCK_ETERNITY, + BLOCK_MAGMATTER, + + BLOCK_ORIHARUKON, + + BLOCK_WHITEDWARFMATTER, + + BLOCK_BLACKDWARFMATTER, + + MARBLE_COBBLE_MOSSY, + MARBLE_BRICKS, + MARBLE_BRICKS_CRACKED, + MARBLE_BRICKS_MOSSY, + MARBLE_BRICKS_CHISELED, + MARBLE_SMOOTH, + BASALT_STONE, + BASALT_COBBLE, + BASALT_COBBLE_MOSSY, + BASALT_BRICKS, + + BASALT_BRICKS_CRACKED, + BASALT_BRICKS_MOSSY, + BASALT_BRICKS_CHISELED, + BASALT_SMOOTH, + OVERLAY_FRONT_HEAT_EXCHANGER_ACTIVE, + OVERLAY_FRONT_HEAT_EXCHANGER_ACTIVE_GLOW, + OVERLAY_FRONT_HEAT_EXCHANGER, + OVERLAY_FRONT_HEAT_EXCHANGER_GLOW, + OVERLAY_FRONT_PROCESSING_ARRAY_ACTIVE, + OVERLAY_FRONT_PROCESSING_ARRAY_ACTIVE_GLOW, + + OVERLAY_FRONT_PROCESSING_ARRAY, + OVERLAY_FRONT_PROCESSING_ARRAY_GLOW, + OVERLAY_FRONT_OIL_DRILL_ACTIVE, + OVERLAY_FRONT_OIL_DRILL_ACTIVE_GLOW, + OVERLAY_FRONT_OIL_DRILL, + OVERLAY_FRONT_OIL_DRILL_GLOW, + OVERLAY_FRONT_DIESEL_ENGINE_ACTIVE, + OVERLAY_FRONT_DIESEL_ENGINE_ACTIVE_GLOW, + OVERLAY_FRONT_DIESEL_ENGINE, + OVERLAY_FRONT_DIESEL_ENGINE_GLOW, + OVERLAY_FRONT_EXTREME_DIESEL_ENGINE_ACTIVE, + OVERLAY_FRONT_EXTREME_DIESEL_ENGINE_ACTIVE_GLOW, + OVERLAY_FRONT_EXTREME_DIESEL_ENGINE, + OVERLAY_FRONT_EXTREME_DIESEL_ENGINE_GLOW, + OVERLAY_FRONT_PYROLYSE_OVEN_ACTIVE, + OVERLAY_FRONT_PYROLYSE_OVEN_ACTIVE_GLOW, + + OVERLAY_FRONT_PYROLYSE_OVEN, + OVERLAY_FRONT_PYROLYSE_OVEN_GLOW, + OVERLAY_FRONT_OIL_CRACKER_ACTIVE, + OVERLAY_FRONT_OIL_CRACKER_ACTIVE_GLOW, + OVERLAY_FRONT_OIL_CRACKER, + OVERLAY_FRONT_OIL_CRACKER_GLOW, + OVERLAY_FRONT_DISTILLATION_TOWER_ACTIVE, + OVERLAY_FRONT_DISTILLATION_TOWER_ACTIVE_GLOW, + OVERLAY_FRONT_DISTILLATION_TOWER, + OVERLAY_FRONT_DISTILLATION_TOWER_GLOW, + + OVERLAY_FRONT_ASSEMBLY_LINE_ACTIVE, + OVERLAY_FRONT_ASSEMBLY_LINE_ACTIVE_GLOW, + OVERLAY_FRONT_ASSEMBLY_LINE, + OVERLAY_FRONT_ASSEMBLY_LINE_GLOW, + OVERLAY_FRONT_ORE_DRILL_ACTIVE, + OVERLAY_FRONT_ORE_DRILL_ACTIVE_GLOW, + OVERLAY_FRONT_ORE_DRILL, + OVERLAY_FRONT_ORE_DRILL_GLOW, + OVERLAY_TOP_CLEANROOM_ACTIVE, + OVERLAY_TOP_CLEANROOM_ACTIVE_GLOW, + OVERLAY_TOP_CLEANROOM, + OVERLAY_TOP_CLEANROOM_GLOW, + + OVERLAY_FRONT_LARGE_CHEMICAL_REACTOR, + OVERLAY_FRONT_LARGE_CHEMICAL_REACTOR_GLOW, + OVERLAY_FRONT_LARGE_CHEMICAL_REACTOR_ACTIVE, + OVERLAY_FRONT_LARGE_CHEMICAL_REACTOR_ACTIVE_GLOW, + + PIPE_RESTRICTOR_UP, + PIPE_RESTRICTOR_DOWN, + PIPE_RESTRICTOR_LEFT, + PIPE_RESTRICTOR_RIGHT, + PIPE_RESTRICTOR_NU, + PIPE_RESTRICTOR_ND, + PIPE_RESTRICTOR_NL, + PIPE_RESTRICTOR_NR, + + PIPE_RESTRICTOR_UD, + PIPE_RESTRICTOR_UL, + PIPE_RESTRICTOR_UR, + PIPE_RESTRICTOR_DL, + PIPE_RESTRICTOR_DR, + PIPE_RESTRICTOR_LR, + + LARGETURBINE_ST_EMPTY1, + LARGETURBINE_ST_EMPTY2, + LARGETURBINE_ST_EMPTY3, + LARGETURBINE_ST_EMPTY4, + LARGETURBINE_ST_EMPTY5, + LARGETURBINE_ST_EMPTY6, + LARGETURBINE_ST_EMPTY7, + LARGETURBINE_ST_EMPTY8, + LARGETURBINE_ST_EMPTY9, + + LARGETURBINE_SS_EMPTY1, + LARGETURBINE_SS_EMPTY2, + LARGETURBINE_SS_EMPTY3, + LARGETURBINE_SS_EMPTY4, + LARGETURBINE_SS_EMPTY5, + LARGETURBINE_SS_EMPTY6, + LARGETURBINE_SS_EMPTY7, + LARGETURBINE_SS_EMPTY8, + LARGETURBINE_SS_EMPTY9, + + LARGETURBINE_TI_EMPTY1, + LARGETURBINE_TI_EMPTY2, + LARGETURBINE_TI_EMPTY3, + LARGETURBINE_TI_EMPTY4, + LARGETURBINE_TI_EMPTY5, + LARGETURBINE_TI_EMPTY6, + LARGETURBINE_TI_EMPTY7, + LARGETURBINE_TI_EMPTY8, + LARGETURBINE_TI_EMPTY9, + + LARGETURBINE_TU_EMPTY1, + LARGETURBINE_TU_EMPTY2, + LARGETURBINE_TU_EMPTY3, + LARGETURBINE_TU_EMPTY4, + LARGETURBINE_TU_EMPTY5, + LARGETURBINE_TU_EMPTY6, + LARGETURBINE_TU_EMPTY7, + LARGETURBINE_TU_EMPTY8, + LARGETURBINE_TU_EMPTY9, + + LARGETURBINE_ADVGAS1, + LARGETURBINE_ADVGAS2, + LARGETURBINE_ADVGAS3, + LARGETURBINE_ADVGAS4, + LARGETURBINE_ADVGAS5, + LARGETURBINE_ADVGAS6, + LARGETURBINE_ADVGAS7, + LARGETURBINE_ADVGAS8, + LARGETURBINE_ADVGAS9, + + LARGETURBINE_ADVGAS_ACTIVE1, + LARGETURBINE_ADVGAS_ACTIVE2, + LARGETURBINE_ADVGAS_ACTIVE3, + LARGETURBINE_ADVGAS_ACTIVE4, + LARGETURBINE_ADVGAS_ACTIVE5, + LARGETURBINE_ADVGAS_ACTIVE6, + LARGETURBINE_ADVGAS_ACTIVE7, + LARGETURBINE_ADVGAS_ACTIVE8, + LARGETURBINE_ADVGAS_ACTIVE9, + + LARGETURBINE_ADVGAS_EMPTY1, + LARGETURBINE_ADVGAS_EMPTY2, + LARGETURBINE_ADVGAS_EMPTY3, + LARGETURBINE_ADVGAS_EMPTY4, + LARGETURBINE_ADVGAS_EMPTY5, + LARGETURBINE_ADVGAS_EMPTY6, + LARGETURBINE_ADVGAS_EMPTY7, + LARGETURBINE_ADVGAS_EMPTY8, + LARGETURBINE_ADVGAS_EMPTY9, + + OVERLAY_ME_HATCH, + OVERLAY_ME_HATCH_ACTIVE, + OVERLAY_ME_INPUT_HATCH, + OVERLAY_ME_INPUT_HATCH_ACTIVE, + OVERLAY_ME_INPUT_FLUID_HATCH, + OVERLAY_ME_INPUT_FLUID_HATCH_ACTIVE, + OVERLAY_ME_CRAFTING_INPUT_BUFFER, + OVERLAY_ME_CRAFTING_INPUT_BUS, + OVERLAY_ME_CRAFTING_INPUT_SLAVE, + OVERLAY_ME_CRAFTING_HATCH, + OVERLAY_ME_CRAFTING_HATCH_ACTIVE, + OVERLAY_ME_FLUID_HATCH, + OVERLAY_ME_FLUID_HATCH_ACTIVE, + + STRUCTURE_MARK, + + MV_TOP_CYCLOTRON_SOLENOID, + MV_SIDE_CYCLOTRON_SOLENOID, + EV_TOP_CYCLOTRON_SOLENOID, + EV_SIDE_CYCLOTRON_SOLENOID, + IV_TOP_CYCLOTRON_SOLENOID, + IV_SIDE_CYCLOTRON_SOLENOID, + HV_TOP_CYCLOTRON_SOLENOID, + HV_SIDE_CYCLOTRON_SOLENOID, + LuV_TOP_CYCLOTRON_SOLENOID, + LuV_SIDE_CYCLOTRON_SOLENOID, + UMV_TOP_CYCLOTRON_SOLENOID, + UIV_TOP_CYCLOTRON_SOLENOID, + UEV_TOP_CYCLOTRON_SOLENOID, + UHV_TOP_CYCLOTRON_SOLENOID, + UV_TOP_CYCLOTRON_SOLENOID, + UV_SIDE_CYCLOTRON_SOLENOID, + UHV_SIDE_CYCLOTRON_SOLENOID, + UEV_SIDE_CYCLOTRON_SOLENOID, + UIV_SIDE_CYCLOTRON_SOLENOID, + UMV_SIDE_CYCLOTRON_SOLENOID, + ZPM_TOP_CYCLOTRON_SOLENOID, + ZPM_SIDE_CYCLOTRON_SOLENOID, + MACHINE_CASING_PCB_TIER_1, + MACHINE_CASING_PCB_TIER_2, + MACHINE_CASING_PCB_TIER_3, + INFINITY_COOLED_CASING, + + LARGETURBINE_NEW1, + LARGETURBINE_NEW2, + LARGETURBINE_NEW3, + LARGETURBINE_NEW4, + LARGETURBINE_NEW5, + LARGETURBINE_NEW6, + LARGETURBINE_NEW7, + LARGETURBINE_NEW8, + LARGETURBINE_NEW9, + LARGETURBINE_NEW_ACTIVE1, + LARGETURBINE_NEW_ACTIVE2, + LARGETURBINE_NEW_ACTIVE3, + LARGETURBINE_NEW_ACTIVE4, + LARGETURBINE_NEW_ACTIVE5, + LARGETURBINE_NEW_ACTIVE6, + LARGETURBINE_NEW_ACTIVE7, + LARGETURBINE_NEW_ACTIVE8, + LARGETURBINE_NEW_ACTIVE9, + LARGETURBINE_NEW_EMPTY1, + LARGETURBINE_NEW_EMPTY2, + LARGETURBINE_NEW_EMPTY3, + LARGETURBINE_NEW_EMPTY4, + LARGETURBINE_NEW_EMPTY5, + LARGETURBINE_NEW_EMPTY6, + LARGETURBINE_NEW_EMPTY7, + LARGETURBINE_NEW_EMPTY8, + LARGETURBINE_NEW_EMPTY9,; + + /** + * Icon for Fresh CFoam + */ + public static final ITexture[] FRESHFOAM = { TextureFactory.of(CFOAM_FRESH) }; + /** + * Icons for Hardened CFoam 0 = No Color 1 - 16 = Colors + */ + public static final ITexture[][] HARDENEDFOAMS = { + new ITexture[] { TextureFactory.of(CFOAM_HARDENED, Dyes.CONSTRUCTION_FOAM.mRGBa) }, + new ITexture[] { TextureFactory.of(CFOAM_HARDENED, Dyes.VALUES[0].mRGBa) }, + new ITexture[] { TextureFactory.of(CFOAM_HARDENED, Dyes.VALUES[1].mRGBa) }, + new ITexture[] { TextureFactory.of(CFOAM_HARDENED, Dyes.VALUES[2].mRGBa) }, + new ITexture[] { TextureFactory.of(CFOAM_HARDENED, Dyes.VALUES[3].mRGBa) }, + new ITexture[] { TextureFactory.of(CFOAM_HARDENED, Dyes.VALUES[4].mRGBa) }, + new ITexture[] { TextureFactory.of(CFOAM_HARDENED, Dyes.VALUES[5].mRGBa) }, + new ITexture[] { TextureFactory.of(CFOAM_HARDENED, Dyes.VALUES[6].mRGBa) }, + new ITexture[] { TextureFactory.of(CFOAM_HARDENED, Dyes.VALUES[7].mRGBa) }, + new ITexture[] { TextureFactory.of(CFOAM_HARDENED, Dyes.VALUES[8].mRGBa) }, + new ITexture[] { TextureFactory.of(CFOAM_HARDENED, Dyes.VALUES[9].mRGBa) }, + new ITexture[] { TextureFactory.of(CFOAM_HARDENED, Dyes.VALUES[10].mRGBa) }, + new ITexture[] { TextureFactory.of(CFOAM_HARDENED, Dyes.VALUES[11].mRGBa) }, + new ITexture[] { TextureFactory.of(CFOAM_HARDENED, Dyes.VALUES[12].mRGBa) }, + new ITexture[] { TextureFactory.of(CFOAM_HARDENED, Dyes.VALUES[13].mRGBa) }, + new ITexture[] { TextureFactory.of(CFOAM_HARDENED, Dyes.VALUES[14].mRGBa) }, + new ITexture[] { TextureFactory.of(CFOAM_HARDENED, Dyes.VALUES[15].mRGBa) } }; + /** + * Machine Casings by Tier 0 = 8V, 1 = LV, 2 = MV, 3 = HV, 4 = EV, 5 = IV, 6 = IV, 7 = IV, 8 = IV, 9 = IV + */ + public static final IIconContainer[] MACHINECASINGS_SIDE = { MACHINE_8V_SIDE, MACHINE_LV_SIDE, MACHINE_MV_SIDE, + MACHINE_HV_SIDE, MACHINE_EV_SIDE, MACHINE_IV_SIDE, MACHINE_LuV_SIDE, MACHINE_ZPM_SIDE, MACHINE_UV_SIDE, + MACHINE_MAX_SIDE, MACHINE_UEV_SIDE, MACHINE_UIV_SIDE, MACHINE_UMV_SIDE, MACHINE_UXV_SIDE, + MACHINE_MAXV_SIDE, }, + MACHINECASINGS_TOP = { MACHINE_8V_TOP, MACHINE_LV_TOP, MACHINE_MV_TOP, MACHINE_HV_TOP, MACHINE_EV_TOP, + MACHINE_IV_TOP, MACHINE_LuV_TOP, MACHINE_ZPM_TOP, MACHINE_UV_TOP, MACHINE_MAX_TOP, MACHINE_UEV_TOP, + MACHINE_UIV_TOP, MACHINE_UMV_TOP, MACHINE_UXV_TOP, MACHINE_MAXV_TOP, }, + MACHINECASINGS_BOTTOM = { MACHINE_8V_BOTTOM, MACHINE_LV_BOTTOM, MACHINE_MV_BOTTOM, MACHINE_HV_BOTTOM, + MACHINE_EV_BOTTOM, MACHINE_IV_BOTTOM, MACHINE_LuV_BOTTOM, MACHINE_ZPM_BOTTOM, MACHINE_UV_BOTTOM, + MACHINE_MAX_BOTTOM, MACHINE_UEV_BOTTOM, MACHINE_UIV_BOTTOM, MACHINE_UMV_BOTTOM, MACHINE_UXV_BOTTOM, + MACHINE_MAXV_BOTTOM, }, + GRANITES = { GRANITE_BLACK_STONE, GRANITE_BLACK_COBBLE, GRANITE_BLACK_COBBLE_MOSSY, GRANITE_BLACK_BRICKS, + GRANITE_BLACK_BRICKS_CRACKED, GRANITE_BLACK_BRICKS_MOSSY, GRANITE_BLACK_BRICKS_CHISELED, + GRANITE_BLACK_SMOOTH, GRANITE_RED_STONE, GRANITE_RED_COBBLE, GRANITE_RED_COBBLE_MOSSY, + GRANITE_RED_BRICKS, GRANITE_RED_BRICKS_CRACKED, GRANITE_RED_BRICKS_MOSSY, GRANITE_RED_BRICKS_CHISELED, + GRANITE_RED_SMOOTH, }, + CONCRETES = { CONCRETE_DARK_STONE, CONCRETE_DARK_COBBLE, CONCRETE_DARK_COBBLE_MOSSY, CONCRETE_DARK_BRICKS, + CONCRETE_DARK_BRICKS_CRACKED, CONCRETE_DARK_BRICKS_MOSSY, CONCRETE_DARK_BRICKS_CHISELED, + CONCRETE_DARK_SMOOTH, CONCRETE_LIGHT_STONE, CONCRETE_LIGHT_COBBLE, CONCRETE_LIGHT_COBBLE_MOSSY, + CONCRETE_LIGHT_BRICKS, CONCRETE_LIGHT_BRICKS_CRACKED, CONCRETE_LIGHT_BRICKS_MOSSY, + CONCRETE_LIGHT_BRICKS_CHISELED, CONCRETE_LIGHT_SMOOTH, }, + STONES = { MARBLE_STONE, MARBLE_COBBLE, MARBLE_COBBLE_MOSSY, MARBLE_BRICKS, MARBLE_BRICKS_CRACKED, + MARBLE_BRICKS_MOSSY, MARBLE_BRICKS_CHISELED, MARBLE_SMOOTH, BASALT_STONE, BASALT_COBBLE, + BASALT_COBBLE_MOSSY, BASALT_BRICKS, BASALT_BRICKS_CRACKED, BASALT_BRICKS_MOSSY, BASALT_BRICKS_CHISELED, + BASALT_SMOOTH, }, + TURBINE = { LARGETURBINE_ST1, LARGETURBINE_ST2, LARGETURBINE_ST3, LARGETURBINE_ST4, LARGETURBINE_ST5, + LARGETURBINE_ST6, LARGETURBINE_ST7, LARGETURBINE_ST8, LARGETURBINE_ST9 }, + TURBINE_ACTIVE = { LARGETURBINE_ST_ACTIVE1, LARGETURBINE_ST_ACTIVE2, LARGETURBINE_ST_ACTIVE3, + LARGETURBINE_ST_ACTIVE4, LARGETURBINE_ST_ACTIVE5, LARGETURBINE_ST_ACTIVE6, LARGETURBINE_ST_ACTIVE7, + LARGETURBINE_ST_ACTIVE8, LARGETURBINE_ST_ACTIVE9 }, + TURBINE_EMPTY = { LARGETURBINE_ST_EMPTY1, LARGETURBINE_ST_EMPTY2, LARGETURBINE_ST_EMPTY3, + LARGETURBINE_ST_EMPTY4, LARGETURBINE_ST_EMPTY5, LARGETURBINE_ST_EMPTY6, LARGETURBINE_ST_EMPTY7, + LARGETURBINE_ST_EMPTY8, LARGETURBINE_ST_EMPTY9 }, + TURBINE_NEW = { LARGETURBINE_NEW1, LARGETURBINE_NEW2, LARGETURBINE_NEW3, LARGETURBINE_NEW4, + LARGETURBINE_NEW5, LARGETURBINE_NEW6, LARGETURBINE_NEW7, LARGETURBINE_NEW8, LARGETURBINE_NEW9 }, + TURBINE_NEW_ACTIVE = { LARGETURBINE_NEW_ACTIVE1, LARGETURBINE_NEW_ACTIVE2, LARGETURBINE_NEW_ACTIVE3, + LARGETURBINE_NEW_ACTIVE4, LARGETURBINE_NEW_ACTIVE5, LARGETURBINE_NEW_ACTIVE6, LARGETURBINE_NEW_ACTIVE7, + LARGETURBINE_NEW_ACTIVE8, LARGETURBINE_NEW_ACTIVE9 }, + TURBINE_NEW_EMPTY = { LARGETURBINE_NEW_EMPTY1, LARGETURBINE_NEW_EMPTY2, LARGETURBINE_NEW_EMPTY3, + LARGETURBINE_NEW_EMPTY4, LARGETURBINE_NEW_EMPTY5, LARGETURBINE_NEW_EMPTY6, LARGETURBINE_NEW_EMPTY7, + LARGETURBINE_NEW_EMPTY8, LARGETURBINE_NEW_EMPTY9 }, + TURBINE1 = { LARGETURBINE_SS1, LARGETURBINE_SS2, LARGETURBINE_SS3, LARGETURBINE_SS4, LARGETURBINE_SS5, + LARGETURBINE_SS6, LARGETURBINE_SS7, LARGETURBINE_SS8, LARGETURBINE_SS9 }, + TURBINE_ACTIVE1 = { LARGETURBINE_SS_ACTIVE1, LARGETURBINE_SS_ACTIVE2, LARGETURBINE_SS_ACTIVE3, + LARGETURBINE_SS_ACTIVE4, LARGETURBINE_SS_ACTIVE5, LARGETURBINE_SS_ACTIVE6, LARGETURBINE_SS_ACTIVE7, + LARGETURBINE_SS_ACTIVE8, LARGETURBINE_SS_ACTIVE9 }, + TURBINE_EMPTY1 = { LARGETURBINE_SS_EMPTY1, LARGETURBINE_SS_EMPTY2, LARGETURBINE_SS_EMPTY3, + LARGETURBINE_SS_EMPTY4, LARGETURBINE_SS_EMPTY5, LARGETURBINE_SS_EMPTY6, LARGETURBINE_SS_EMPTY7, + LARGETURBINE_SS_EMPTY8, LARGETURBINE_SS_EMPTY9 }, + TURBINE2 = { LARGETURBINE_TI1, LARGETURBINE_TI2, LARGETURBINE_TI3, LARGETURBINE_TI4, LARGETURBINE_TI5, + LARGETURBINE_TI6, LARGETURBINE_TI7, LARGETURBINE_TI8, LARGETURBINE_TI9 }, + TURBINE_ACTIVE2 = { LARGETURBINE_TI_ACTIVE1, LARGETURBINE_TI_ACTIVE2, LARGETURBINE_TI_ACTIVE3, + LARGETURBINE_TI_ACTIVE4, LARGETURBINE_TI_ACTIVE5, LARGETURBINE_TI_ACTIVE6, LARGETURBINE_TI_ACTIVE7, + LARGETURBINE_TI_ACTIVE8, LARGETURBINE_TI_ACTIVE9 }, + TURBINE_EMPTY2 = { LARGETURBINE_TI_EMPTY1, LARGETURBINE_TI_EMPTY2, LARGETURBINE_TI_EMPTY3, + LARGETURBINE_TI_EMPTY4, LARGETURBINE_TI_EMPTY5, LARGETURBINE_TI_EMPTY6, LARGETURBINE_TI_EMPTY7, + LARGETURBINE_TI_EMPTY8, LARGETURBINE_TI_EMPTY9 }, + TURBINE3 = { LARGETURBINE_TU1, LARGETURBINE_TU2, LARGETURBINE_TU3, LARGETURBINE_TU4, LARGETURBINE_TU5, + LARGETURBINE_TU6, LARGETURBINE_TU7, LARGETURBINE_TU8, LARGETURBINE_TU9 }, + TURBINE_ACTIVE3 = { LARGETURBINE_TU_ACTIVE1, LARGETURBINE_TU_ACTIVE2, LARGETURBINE_TU_ACTIVE3, + LARGETURBINE_TU_ACTIVE4, LARGETURBINE_TU_ACTIVE5, LARGETURBINE_TU_ACTIVE6, LARGETURBINE_TU_ACTIVE7, + LARGETURBINE_TU_ACTIVE8, LARGETURBINE_TU_ACTIVE9 }, + TURBINE_EMPTY3 = { LARGETURBINE_TU_EMPTY1, LARGETURBINE_TU_EMPTY2, LARGETURBINE_TU_EMPTY3, + LARGETURBINE_TU_EMPTY4, LARGETURBINE_TU_EMPTY5, LARGETURBINE_TU_EMPTY6, LARGETURBINE_TU_EMPTY7, + LARGETURBINE_TU_EMPTY8, LARGETURBINE_TU_EMPTY9 }, + TURBINEADVGAS = { LARGETURBINE_ADVGAS1, LARGETURBINE_ADVGAS2, LARGETURBINE_ADVGAS3, LARGETURBINE_ADVGAS4, + LARGETURBINE_ADVGAS5, LARGETURBINE_ADVGAS6, LARGETURBINE_ADVGAS7, LARGETURBINE_ADVGAS8, + LARGETURBINE_ADVGAS9 }, + TURBINE_ADVGASACTIVE = { LARGETURBINE_ADVGAS_ACTIVE1, LARGETURBINE_ADVGAS_ACTIVE2, + LARGETURBINE_ADVGAS_ACTIVE3, LARGETURBINE_ADVGAS_ACTIVE4, LARGETURBINE_ADVGAS_ACTIVE5, + LARGETURBINE_ADVGAS_ACTIVE6, LARGETURBINE_ADVGAS_ACTIVE7, LARGETURBINE_ADVGAS_ACTIVE8, + LARGETURBINE_ADVGAS_ACTIVE9 }, + TURBINE_ADVGASEMPTY = { LARGETURBINE_ADVGAS_EMPTY1, LARGETURBINE_ADVGAS_EMPTY2, LARGETURBINE_ADVGAS_EMPTY3, + LARGETURBINE_ADVGAS_EMPTY4, LARGETURBINE_ADVGAS_EMPTY5, LARGETURBINE_ADVGAS_EMPTY6, + LARGETURBINE_ADVGAS_EMPTY7, LARGETURBINE_ADVGAS_EMPTY8, LARGETURBINE_ADVGAS_EMPTY9 }, + CONNECTED_HULLS = { CONCRETE_DARK_STONE, FUSIONI_1, FUSIONI_2, FUSIONI_3, FUSIONI_4, FUSIONI_5, FUSIONI_6, + FUSIONI_7, FUSIONI_8, FUSIONI_9, FUSIONI_10, FUSIONI_11, FUSIONI_12, FUSIONII_1, FUSIONII_2, FUSIONII_3, + FUSIONII_4, FUSIONII_5, FUSIONII_6, FUSIONII_7, FUSIONII_8, FUSIONII_9, FUSIONII_10, FUSIONII_11, + FUSIONII_12, }, + STORAGE_BLOCKS1 = { BLOCK_ADAMANTIUM, BLOCK_ALUMINIUM, BLOCK_AMERICIUM, BLOCK_ANNEALEDCOPPER, + BLOCK_ANTIMONY, BLOCK_ARSENIC, BLOCK_ASTRALSILVER, BLOCK_BATTERYALLOY, BLOCK_BERYLLIUM, BLOCK_BISMUTH, + BLOCK_BISMUTHBRONZE, BLOCK_BLACKBRONZE, BLOCK_BLACKSTEEL, BLOCK_BLUEALLOY, BLOCK_BLUESTEEL, + BLOCK_BRASS }, + STORAGE_BLOCKS2 = { BLOCK_BRONZE, BLOCK_CAESIUM, BLOCK_CERIUM, BLOCK_CHROME, BLOCK_CHROMIUMDIOXIDE, + BLOCK_COBALT, BLOCK_COBALTBRASS, BLOCK_COPPER, BLOCK_CUPRONICKEL, BLOCK_DAMASCUSSTEEL, BLOCK_DARKIRON, + BLOCK_DEEPIRON, BLOCK_DESH, BLOCK_DURANIUM, BLOCK_DYSPROSIUM, BLOCK_ELECTRUM }, + STORAGE_BLOCKS3 = { BLOCK_ELECTRUMFLUX, BLOCK_ENDERIUM, BLOCK_ERBIUM, BLOCK_EUROPIUM, BLOCK_FIERYSTEEL, + BLOCK_GADOLINIUM, BLOCK_GALLIUM, BLOCK_HOLMIUM, BLOCK_HSLA, BLOCK_INDIUM, BLOCK_INFUSEDGOLD, + BLOCK_INVAR, BLOCK_IRIDIUM, BLOCK_IRONMAGNETIC, BLOCK_IRONWOOD, BLOCK_KANTHAL }, + STORAGE_BLOCKS4 = { BLOCK_KNIGHTMETAL, BLOCK_LANTHANUM, BLOCK_LEAD, BLOCK_LUTETIUM, BLOCK_MAGNALIUM, + BLOCK_MAGNESIUM, BLOCK_MANGANESE, BLOCK_METEORICIRON, BLOCK_METEORICSTEEL, BLOCK_TRINIUM, BLOCK_MITHRIL, + BLOCK_MOLYBDENUM, BLOCK_NAQUADAH, BLOCK_NAQUADAHALLOY, BLOCK_NAQUADAHENRICHED, BLOCK_NAQUADRIA }, + STORAGE_BLOCKS5 = { BLOCK_NEODYMIUM, BLOCK_NEODYMIUMMAGNETIC, BLOCK_NEUTRONIUM, BLOCK_NICHROME, + BLOCK_NICKEL, BLOCK_NIOBIUM, BLOCK_NIOBIUMNITRIDE, BLOCK_NIOBIUMTITANIUM, BLOCK_OSMIRIDIUM, + BLOCK_OSMIUM, BLOCK_PALLADIUM, BLOCK_PIGIRON, BLOCK_PLATINUM, BLOCK_PLUTONIUM, BLOCK_PLUTONIUM241, + BLOCK_PRASEODYMIUM }, + STORAGE_BLOCKS6 = { BLOCK_PROMETHIUM, BLOCK_REDALLOY, BLOCK_REDSTEEL, BLOCK_ROSEGOLD, BLOCK_RUBIDIUM, + BLOCK_SAMARIUM, BLOCK_SCANDIUM, BLOCK_SHADOWIRON, BLOCK_SHADOWSTEEL, BLOCK_SILICON, BLOCK_SILVER, + BLOCK_SOLDERINGALLOY, BLOCK_STAINLESSSTEEL, BLOCK_STEEL, BLOCK_STEELMAGNETIC, BLOCK_STERLINGSILVER }, + STORAGE_BLOCKS7 = { BLOCK_SUNNARIUM, BLOCK_TANTALUM, BLOCK_TELLURIUM, BLOCK_TERBIUM, BLOCK_THAUMIUM, + BLOCK_THORIUM, BLOCK_THULIUM, BLOCK_TIN, BLOCK_TINALLOY, BLOCK_TITANIUM, BLOCK_TRITANIUM, + BLOCK_TUNGSTEN, BLOCK_TUNGSTENSTEEL, BLOCK_ULTIMET, BLOCK_URANIUM, BLOCK_URANIUM235 }, + STORAGE_BLOCKS8 = { BLOCK_VANADIUM, BLOCK_VANADIUMGALLIUM, BLOCK_WROUGHTIRON, BLOCK_YTTRBIUM, BLOCK_YTTRIUM, + BLOCK_YTTRIUMBARIUMCUPRATE, BLOCK_ZINC, BLOCK_TUNGSTENCARBIDE, BLOCK_VANADIUMSTEEL, BLOCK_HSSG, + BLOCK_HSSE, BLOCK_HSSS, BLOCK_STEELEAF, BLOCK_ICHORIUM, BLOCK_FIRESTONE, BLOCK_SHADOW }, + STORAGE_BLOCKS9 = { BLOCK_AERCRYSTAL, BLOCK_AMBER, BLOCK_AMETHYST, BLOCK_AQUACRYSTAL, BLOCK_BLUETOPAZ, + BLOCK_CERTUSQUARTZ, BLOCK_DILITHIUM, BLOCK_ENDEREYE, BLOCK_ENDERPEARL, BLOCK_FOOLSRUBY, BLOCK_FORCE, + BLOCK_FORCICIUM, BLOCK_FORCILLIUM, BLOCK_GREENSAPPHIRE, BLOCK_IGNISCRYSTAL, BLOCK_JASPER }, + STORAGE_BLOCKS10 = { BLOCK_LAZURITE, BLOCK_LIGNITE, BLOCK_MONAZITE, BLOCK_NITER, BLOCK_OLIVINE, BLOCK_OPAL, + BLOCK_ORDOCRYSTAL, BLOCK_PERDITIOCRYSTAL, BLOCK_PHOSPHORUS, BLOCK_QUARTZITE, BLOCK_REDGARNET, + BLOCK_RUBY, BLOCK_SAPPHIRE, BLOCK_SODALITE, BLOCK_TANZANITE, BLOCK_TERRACRYSTAL }, + STORAGE_BLOCKS11 = { BLOCK_TOPAZ, BLOCK_VINTEUM, BLOCK_YELLOWGARNET, BLOCK_NETHERSTAR, BLOCK_CHARCOAL, + BLOCK_BLAZE }, + STORAGE_BLOCKS12 = { BLOCK_CRYOLITE, BLOCK_SILICONSG, BLOCK_NICKELALUMINIUM, BLOCK_SPACETIME, + BLOCK_TRANSCENDENTMETAL, BLOCK_ORIHARUKON, BLOCK_WHITEDWARFMATTER, BLOCK_BLACKDWARFMATTER, + BLOCK_UNIVERSIUM, BLOCK_ETERNITY, BLOCK_MAGMATTER }; + + public static final ITexture[] HIDDEN_TEXTURE = { TextureFactory.builder() + .addIcon(HIDDEN_FACE) + .stdOrient() + .build() }; + public static final ITexture[] ERROR_RENDERING = { TextureFactory.of(RENDERING_ERROR) }; + public static final ITexture[] OVERLAYS_ENERGY_IN = { + TextureFactory.of(OVERLAY_ENERGY_IN, new short[] { 180, 180, 180, 0 }), + TextureFactory.of(OVERLAY_ENERGY_IN, new short[] { 220, 220, 220, 0 }), + TextureFactory.of(OVERLAY_ENERGY_IN, new short[] { 255, 100, 0, 0 }), + TextureFactory.of(OVERLAY_ENERGY_IN, new short[] { 255, 255, 30, 0 }), + TextureFactory.of(OVERLAY_ENERGY_IN, new short[] { 128, 128, 128, 0 }), + TextureFactory.of(OVERLAY_ENERGY_IN, new short[] { 240, 240, 245, 0 }), + TextureFactory.of(OVERLAY_ENERGY_IN, new short[] { 220, 220, 245, 0 }), + TextureFactory.of(OVERLAY_ENERGY_IN, new short[] { 200, 200, 245, 0 }), + TextureFactory.of(OVERLAY_ENERGY_IN, new short[] { 180, 180, 245, 0 }), + TextureFactory.of(OVERLAY_ENERGY_IN, new short[] { 160, 160, 245, 0 }), + TextureFactory.of(OVERLAY_ENERGY_IN, new short[] { 140, 140, 245, 0 }), + TextureFactory.of(OVERLAY_ENERGY_IN, new short[] { 120, 120, 245, 0 }), + TextureFactory.of(OVERLAY_ENERGY_IN, new short[] { 100, 100, 245, 0 }), + TextureFactory.of(OVERLAY_ENERGY_IN, new short[] { 80, 80, 245, 0 }), + TextureFactory.of(OVERLAY_ENERGY_IN, new short[] { 60, 60, 245, 0 }), + TextureFactory.of(OVERLAY_ENERGY_IN, new short[] { 40, 40, 245, 0 }), }; + public static ITexture[] OVERLAYS_ENERGY_OUT = { + TextureFactory.of(OVERLAY_ENERGY_OUT, new short[] { 180, 180, 180, 0 }), + TextureFactory.of(OVERLAY_ENERGY_OUT, new short[] { 220, 220, 220, 0 }), + TextureFactory.of(OVERLAY_ENERGY_OUT, new short[] { 255, 100, 0, 0 }), + TextureFactory.of(OVERLAY_ENERGY_OUT, new short[] { 255, 255, 30, 0 }), + TextureFactory.of(OVERLAY_ENERGY_OUT, new short[] { 128, 128, 128, 0 }), + TextureFactory.of(OVERLAY_ENERGY_OUT, new short[] { 240, 240, 245, 0 }), + TextureFactory.of(OVERLAY_ENERGY_OUT, new short[] { 220, 220, 245, 0 }), + TextureFactory.of(OVERLAY_ENERGY_OUT, new short[] { 200, 200, 245, 0 }), + TextureFactory.of(OVERLAY_ENERGY_OUT, new short[] { 180, 180, 245, 0 }), + TextureFactory.of(OVERLAY_ENERGY_OUT, new short[] { 160, 160, 245, 0 }), + TextureFactory.of(OVERLAY_ENERGY_OUT, new short[] { 140, 140, 245, 0 }), + TextureFactory.of(OVERLAY_ENERGY_OUT, new short[] { 120, 120, 245, 0 }), + TextureFactory.of(OVERLAY_ENERGY_OUT, new short[] { 100, 100, 245, 0 }), + TextureFactory.of(OVERLAY_ENERGY_OUT, new short[] { 80, 80, 245, 0 }), + TextureFactory.of(OVERLAY_ENERGY_OUT, new short[] { 60, 60, 245, 0 }), + TextureFactory.of(OVERLAY_ENERGY_OUT, new short[] { 40, 40, 245, 0 }), }; + public static final ITexture[] OVERLAYS_ENERGY_IN_MULTI = { + TextureFactory.of(OVERLAY_ENERGY_IN_MULTI, new short[] { 180, 180, 180, 0 }), + TextureFactory.of(OVERLAY_ENERGY_IN_MULTI, new short[] { 220, 220, 220, 0 }), + TextureFactory.of(OVERLAY_ENERGY_IN_MULTI, new short[] { 255, 100, 0, 0 }), + TextureFactory.of(OVERLAY_ENERGY_IN_MULTI, new short[] { 255, 255, 30, 0 }), + TextureFactory.of(OVERLAY_ENERGY_IN_MULTI, new short[] { 128, 128, 128, 0 }), + TextureFactory.of(OVERLAY_ENERGY_IN_MULTI, new short[] { 240, 240, 245, 0 }), + TextureFactory.of(OVERLAY_ENERGY_IN_MULTI, new short[] { 220, 220, 245, 0 }), + TextureFactory.of(OVERLAY_ENERGY_IN_MULTI, new short[] { 200, 200, 245, 0 }), + TextureFactory.of(OVERLAY_ENERGY_IN_MULTI, new short[] { 180, 180, 245, 0 }), + TextureFactory.of(OVERLAY_ENERGY_IN_MULTI, new short[] { 160, 160, 245, 0 }), + TextureFactory.of(OVERLAY_ENERGY_IN_MULTI, new short[] { 140, 140, 245, 0 }), + TextureFactory.of(OVERLAY_ENERGY_IN_MULTI, new short[] { 120, 120, 245, 0 }), + TextureFactory.of(OVERLAY_ENERGY_IN_MULTI, new short[] { 100, 100, 245, 0 }), + TextureFactory.of(OVERLAY_ENERGY_IN_MULTI, new short[] { 80, 80, 245, 0 }), + TextureFactory.of(OVERLAY_ENERGY_IN_MULTI, new short[] { 60, 60, 245, 0 }), + TextureFactory.of(OVERLAY_ENERGY_IN_MULTI, new short[] { 40, 40, 245, 0 }), }; + + public static final ITexture[] OVERLAYS_ENERGY_IN_MULTI_WIRELESS_ON = { + TextureFactory.of(OVERLAY_ENERGY_ON_WIRELESS, new short[] { 255, 255, 255, 0 }), + TextureFactory.of(OVERLAY_ENERGY_ON_WIRELESS, new short[] { 255, 255, 255, 0 }), + TextureFactory.of(OVERLAY_ENERGY_ON_WIRELESS, new short[] { 255, 255, 255, 0 }), + TextureFactory.of(OVERLAY_ENERGY_ON_WIRELESS, new short[] { 255, 255, 255, 0 }), + TextureFactory.of(OVERLAY_ENERGY_ON_WIRELESS, new short[] { 255, 255, 255, 0 }), + TextureFactory.of(OVERLAY_ENERGY_ON_WIRELESS, new short[] { 255, 255, 255, 0 }), + TextureFactory.of(OVERLAY_ENERGY_ON_WIRELESS, new short[] { 255, 255, 255, 0 }), + TextureFactory.of(OVERLAY_ENERGY_ON_WIRELESS, new short[] { 255, 255, 255, 0 }), + TextureFactory.of(OVERLAY_ENERGY_ON_WIRELESS, new short[] { 255, 255, 255, 0 }), + TextureFactory.of(OVERLAY_ENERGY_ON_WIRELESS, new short[] { 255, 255, 255, 0 }), + TextureFactory.of(OVERLAY_ENERGY_ON_WIRELESS, new short[] { 255, 255, 255, 0 }), + TextureFactory.of(OVERLAY_ENERGY_ON_WIRELESS, new short[] { 255, 255, 255, 0 }), + TextureFactory.of(OVERLAY_ENERGY_ON_WIRELESS, new short[] { 255, 255, 255, 0 }), + TextureFactory.of(OVERLAY_ENERGY_ON_WIRELESS, new short[] { 255, 255, 255, 0 }), + TextureFactory.of(OVERLAY_ENERGY_ON_WIRELESS, new short[] { 255, 255, 255, 0 }), + TextureFactory.of(OVERLAY_ENERGY_ON_WIRELESS, new short[] { 255, 255, 255, 0 }), }; + + public static final ITexture[] OVERLAYS_ENERGY_IN_MULTI_WIRELESS_OFF = { + TextureFactory.of(OVERLAY_ENERGY_OFF_WIRELESS, new short[] { 0, 0, 0, 0 }), + TextureFactory.of(OVERLAY_ENERGY_OFF_WIRELESS, new short[] { 0, 0, 0, 0 }), + TextureFactory.of(OVERLAY_ENERGY_OFF_WIRELESS, new short[] { 0, 0, 0, 0 }), + TextureFactory.of(OVERLAY_ENERGY_OFF_WIRELESS, new short[] { 0, 0, 0, 0 }), + TextureFactory.of(OVERLAY_ENERGY_OFF_WIRELESS, new short[] { 0, 0, 0, 0 }), + TextureFactory.of(OVERLAY_ENERGY_OFF_WIRELESS, new short[] { 0, 0, 0, 0 }), + TextureFactory.of(OVERLAY_ENERGY_OFF_WIRELESS, new short[] { 0, 0, 0, 0 }), + TextureFactory.of(OVERLAY_ENERGY_OFF_WIRELESS, new short[] { 0, 0, 0, 0 }), + TextureFactory.of(OVERLAY_ENERGY_OFF_WIRELESS, new short[] { 0, 0, 0, 0 }), + TextureFactory.of(OVERLAY_ENERGY_OFF_WIRELESS, new short[] { 0, 0, 0, 0 }), + TextureFactory.of(OVERLAY_ENERGY_OFF_WIRELESS, new short[] { 0, 0, 0, 0 }), + TextureFactory.of(OVERLAY_ENERGY_OFF_WIRELESS, new short[] { 0, 0, 0, 0 }), + TextureFactory.of(OVERLAY_ENERGY_OFF_WIRELESS, new short[] { 0, 0, 0, 0 }), + TextureFactory.of(OVERLAY_ENERGY_OFF_WIRELESS, new short[] { 0, 0, 0, 0 }), + TextureFactory.of(OVERLAY_ENERGY_OFF_WIRELESS, new short[] { 0, 0, 0, 0 }), + TextureFactory.of(OVERLAY_ENERGY_OFF_WIRELESS, new short[] { 0, 0, 0, 0 }), }; + + public static final ITexture[] OVERLAYS_ENERGY_OUT_MULTI = { + TextureFactory.of(OVERLAY_ENERGY_OUT_MULTI, new short[] { 180, 180, 180, 0 }), + TextureFactory.of(OVERLAY_ENERGY_OUT_MULTI, new short[] { 220, 220, 220, 0 }), + TextureFactory.of(OVERLAY_ENERGY_OUT_MULTI, new short[] { 255, 100, 0, 0 }), + TextureFactory.of(OVERLAY_ENERGY_OUT_MULTI, new short[] { 255, 255, 30, 0 }), + TextureFactory.of(OVERLAY_ENERGY_OUT_MULTI, new short[] { 128, 128, 128, 0 }), + TextureFactory.of(OVERLAY_ENERGY_OUT_MULTI, new short[] { 240, 240, 245, 0 }), + TextureFactory.of(OVERLAY_ENERGY_OUT_MULTI, new short[] { 220, 220, 245, 0 }), + TextureFactory.of(OVERLAY_ENERGY_OUT_MULTI, new short[] { 200, 200, 245, 0 }), + TextureFactory.of(OVERLAY_ENERGY_OUT_MULTI, new short[] { 180, 180, 245, 0 }), + TextureFactory.of(OVERLAY_ENERGY_OUT_MULTI, new short[] { 160, 160, 245, 0 }), + TextureFactory.of(OVERLAY_ENERGY_OUT_MULTI, new short[] { 140, 140, 245, 0 }), + TextureFactory.of(OVERLAY_ENERGY_OUT_MULTI, new short[] { 120, 120, 245, 0 }), + TextureFactory.of(OVERLAY_ENERGY_OUT_MULTI, new short[] { 100, 100, 245, 0 }), + TextureFactory.of(OVERLAY_ENERGY_OUT_MULTI, new short[] { 80, 80, 245, 0 }), + TextureFactory.of(OVERLAY_ENERGY_OUT_MULTI, new short[] { 60, 60, 245, 0 }), + TextureFactory.of(OVERLAY_ENERGY_OUT_MULTI, new short[] { 40, 40, 245, 0 }), }; + public static final ITexture[] OVERLAYS_ENERGY_IN_POWER = { + TextureFactory.of(OVERLAY_ENERGY_IN_POWER, new short[] { 180, 180, 180, 0 }), + TextureFactory.of(OVERLAY_ENERGY_IN_POWER, new short[] { 220, 220, 220, 0 }), + TextureFactory.of(OVERLAY_ENERGY_IN_POWER, new short[] { 255, 100, 0, 0 }), + TextureFactory.of(OVERLAY_ENERGY_IN_POWER, new short[] { 255, 255, 30, 0 }), + TextureFactory.of(OVERLAY_ENERGY_IN_POWER, new short[] { 128, 128, 128, 0 }), + TextureFactory.of(OVERLAY_ENERGY_IN_POWER, new short[] { 240, 240, 245, 0 }), + TextureFactory.of(OVERLAY_ENERGY_IN_POWER, new short[] { 220, 220, 245, 0 }), + TextureFactory.of(OVERLAY_ENERGY_IN_POWER, new short[] { 200, 200, 245, 0 }), + TextureFactory.of(OVERLAY_ENERGY_IN_POWER, new short[] { 180, 180, 245, 0 }), + TextureFactory.of(OVERLAY_ENERGY_IN_POWER, new short[] { 160, 160, 245, 0 }), + TextureFactory.of(OVERLAY_ENERGY_IN_POWER, new short[] { 140, 140, 245, 0 }), + TextureFactory.of(OVERLAY_ENERGY_IN_POWER, new short[] { 120, 120, 245, 0 }), + TextureFactory.of(OVERLAY_ENERGY_IN_POWER, new short[] { 100, 100, 245, 0 }), + TextureFactory.of(OVERLAY_ENERGY_IN_POWER, new short[] { 80, 80, 245, 0 }), + TextureFactory.of(OVERLAY_ENERGY_IN_POWER, new short[] { 60, 60, 245, 0 }), + TextureFactory.of(OVERLAY_ENERGY_IN_POWER, new short[] { 40, 40, 245, 0 }), }; + public static final ITexture[] OVERLAYS_ENERGY_OUT_POWER = { + TextureFactory.of(OVERLAY_ENERGY_OUT_POWER, new short[] { 180, 180, 180, 0 }), + TextureFactory.of(OVERLAY_ENERGY_OUT_POWER, new short[] { 220, 220, 220, 0 }), + TextureFactory.of(OVERLAY_ENERGY_OUT_POWER, new short[] { 255, 100, 0, 0 }), + TextureFactory.of(OVERLAY_ENERGY_OUT_POWER, new short[] { 255, 255, 30, 0 }), + TextureFactory.of(OVERLAY_ENERGY_OUT_POWER, new short[] { 128, 128, 128, 0 }), + TextureFactory.of(OVERLAY_ENERGY_OUT_POWER, new short[] { 240, 240, 245, 0 }), + TextureFactory.of(OVERLAY_ENERGY_OUT_POWER, new short[] { 220, 220, 245, 0 }), + TextureFactory.of(OVERLAY_ENERGY_OUT_POWER, new short[] { 200, 200, 245, 0 }), + TextureFactory.of(OVERLAY_ENERGY_OUT_POWER, new short[] { 180, 180, 245, 0 }), + TextureFactory.of(OVERLAY_ENERGY_OUT_POWER, new short[] { 160, 160, 245, 0 }), + TextureFactory.of(OVERLAY_ENERGY_OUT_POWER, new short[] { 140, 140, 245, 0 }), + TextureFactory.of(OVERLAY_ENERGY_OUT_POWER, new short[] { 120, 120, 245, 0 }), + TextureFactory.of(OVERLAY_ENERGY_OUT_POWER, new short[] { 100, 100, 245, 0 }), + TextureFactory.of(OVERLAY_ENERGY_OUT_POWER, new short[] { 80, 80, 245, 0 }), + TextureFactory.of(OVERLAY_ENERGY_OUT_POWER, new short[] { 60, 60, 245, 0 }), + TextureFactory.of(OVERLAY_ENERGY_OUT_POWER, new short[] { 40, 40, 245, 0 }), }; + public static final ITexture[] LOCKERS = { TextureFactory.of(OVERLAY_LOCKER_000), + TextureFactory.of(OVERLAY_LOCKER_001), TextureFactory.of(OVERLAY_LOCKER_002), + TextureFactory.of(OVERLAY_LOCKER_003), TextureFactory.of(OVERLAY_LOCKER_004), + TextureFactory.of(OVERLAY_LOCKER_005), TextureFactory.of(OVERLAY_LOCKER_006), + TextureFactory.of(OVERLAY_LOCKER_007), TextureFactory.of(OVERLAY_LOCKER_008), + TextureFactory.of(OVERLAY_LOCKER_009), TextureFactory.of(OVERLAY_LOCKER_010), + TextureFactory.of(OVERLAY_LOCKER_011), TextureFactory.of(OVERLAY_LOCKER_012), + TextureFactory.of(OVERLAY_LOCKER_013), }; + /** + * USE casingTexturePages[page] instead of CASING_BLOCKS since it is casingTexturePages[0] + */ + @Deprecated + public static final ITexture[] CASING_BLOCKS = new ITexture[128]; // original variable still limited to 128 + + public static ITexture[][] MACHINE_CASINGS = new ITexture[15][17]; + /** + * by Default pages are null page 0: 0-63 GT casing 1-4, 64-127 GT++ page 1: 0-15 GT casing 5, 22-26 GS dyson + * swarm, 48-57 GT casing 8, 63 EMT, 80-95 GT reinforced blocks, 96 casing 2 meta 6, 97 error casing page 8: + * 0-111 TecTech, 112-127 GT casing 6 page 12: 0-127 GlodBlock page 42: 0-126 glee8e, 127 KekzTech LSC base + */ + public static ITexture[][] casingTexturePages = new ITexture[128][]; // page holder so we don't make an short + // long array + + public static final int ERROR_TEXTURE_INDEX = (1 << 7) + 97; + private static final Map<ITexture, Integer> reverseMap = new HashMap<>(); + + static { + for (byte i = 0; i < MACHINE_CASINGS.length; i++) + for (byte j = 0; j < MACHINE_CASINGS[i].length; j++) MACHINE_CASINGS[i][j] = TextureFactory.of( + MACHINECASINGS_BOTTOM[i], + MACHINECASINGS_TOP[i], + MACHINECASINGS_SIDE[i], + Dyes.getModulation(j - 1, Dyes.MACHINE_METAL.mRGBa)); + casingTexturePages[0] = new ITexture[128]; + // adds some known pages, modders also can do it... + GT_Utility.addTexturePage((byte) 1); + GT_Utility.addTexturePage((byte) 8); + setCasingTextureForId(ERROR_TEXTURE_INDEX, ERROR_RENDERING[0]); + } + + IIcon mIcon; + + BlockIcons() { + GregTech_API.sGTBlockIconload.add(this); + } + + public static ITexture getCasingTextureForId(int id) { + final ITexture[] page = casingTexturePages[(id >> 7) & 0x7f]; + if (page == null) return null; + return page[id & 0x7f]; + } + + public static void setCasingTextureForId(int id, ITexture iTexture) { + casingTexturePages[(byte) ((id >> 7) & 0x7f)][(byte) (id & 0x7f)] = iTexture; + reverseMap.put(iTexture, id); + } + + public static void setCasingTexture(byte page, byte index, ITexture iTexture) { + casingTexturePages[page][index] = iTexture; + reverseMap.put(iTexture, (page << 7) + index); + } + + public static int getTextureIndex(ITexture texture) { + Integer id = reverseMap.get(texture); + return id == null ? ERROR_TEXTURE_INDEX : id; + } + + @Override + public IIcon getIcon() { + return mIcon; + } + + @Override + public IIcon getOverlayIcon() { + return null; + } + + @Override + public ResourceLocation getTextureFile() { + return TextureMap.locationBlocksTexture; + } + + @Override + public void run() { + mIcon = GregTech_API.sBlockIcons.registerIcon(GregTech.getResourcePath("iconsets", this.toString())); + } + + public static class CustomIcon implements IIconContainer, Runnable { + + protected IIcon mIcon; + protected String mIconName; + + public CustomIcon(String aIconName) { + mIconName = !aIconName.contains(":") ? GregTech.getResourcePath(aIconName) : aIconName; + GregTech_API.sGTBlockIconload.add(this); + } + + @Override + public void run() { + mIcon = GregTech_API.sBlockIcons.registerIcon(mIconName); + } + + @Override + public IIcon getIcon() { + return mIcon; + } + + @Override + public IIcon getOverlayIcon() { + return null; + } + + @Override + public ResourceLocation getTextureFile() { + return TextureMap.locationBlocksTexture; + } + } + } + + public enum ItemIcons implements IIconContainer, Runnable { + + VOID, // The Empty Texture + RENDERING_ERROR, + WRENCH, + MORTAR, + CROWBAR, + JACKHAMMER, + WIRE_CUTTER, + KNIFE, + BUTCHERYKNIFE, + SICKLE, + SCOOP, + GRAFTER, + PLUNGER, + ROLLING_PIN, + HANDLE_SWORD, + HANDLE_FILE, + HANDLE_SAW, + HANDLE_SCREWDRIVER, + HANDLE_BUZZSAW, + HANDLE_ELECTRIC_SCREWDRIVER, + HANDLE_SOLDERING, + POWER_UNIT_LV, + POWER_UNIT_MV, + POWER_UNIT_HV, + DURABILITY_BAR_0, + DURABILITY_BAR_1, + DURABILITY_BAR_2, + DURABILITY_BAR_3, + DURABILITY_BAR_4, + DURABILITY_BAR_5, + DURABILITY_BAR_6, + DURABILITY_BAR_7, + DURABILITY_BAR_8, + ENERGY_BAR_0, + ENERGY_BAR_1, + ENERGY_BAR_2, + ENERGY_BAR_3, + ENERGY_BAR_4, + ENERGY_BAR_5, + ENERGY_BAR_6, + ENERGY_BAR_7, + ENERGY_BAR_8, + TURBINE, + TURBINE_SMALL, + TURBINE_LARGE, + TURBINE_HUGE, + POCKET_MULTITOOL_CLOSED, + POCKET_MULTITOOL_BRANCHCUTTER, + POCKET_MULTITOOL_FILE, + POCKET_MULTITOOL_KNIFE, + POCKET_MULTITOOL_SAW, + POCKET_MULTITOOL_SCREWDRIVER, + POCKET_MULTITOOL_WIRECUTTER, + HALO, + HALO_FUZZY; + + public static final IIconContainer[] DURABILITY_BAR = { DURABILITY_BAR_0, DURABILITY_BAR_1, DURABILITY_BAR_2, + DURABILITY_BAR_3, DURABILITY_BAR_4, DURABILITY_BAR_5, DURABILITY_BAR_6, DURABILITY_BAR_7, + DURABILITY_BAR_8, }, + ENERGY_BAR = { ENERGY_BAR_0, ENERGY_BAR_1, ENERGY_BAR_2, ENERGY_BAR_3, ENERGY_BAR_4, ENERGY_BAR_5, + ENERGY_BAR_6, ENERGY_BAR_7, ENERGY_BAR_8, }; + + public static final ITexture[] ERROR_RENDERING = { TextureFactory.of(RENDERING_ERROR) }; + + IIcon mIcon, mOverlay; + + ItemIcons() { + GregTech_API.sGTItemIconload.add(this); + } + + @Override + public IIcon getIcon() { + return mIcon; + } + + @Override + public IIcon getOverlayIcon() { + return mOverlay; + } + + @Override + public ResourceLocation getTextureFile() { + return TextureMap.locationItemsTexture; + } + + @Override + public void run() { + mIcon = GregTech_API.sItemIcons.registerIcon(GregTech.getResourcePath("iconsets", this.toString())); + mOverlay = GregTech_API.sItemIcons.registerIcon(GregTech.getResourcePath("iconsets", this + "_OVERLAY")); + } + + public static class CustomIcon implements IIconContainer, Runnable { + + protected IIcon mIcon, mOverlay; + protected String mIconName; + + public CustomIcon(String aIconName) { + mIconName = aIconName; + GregTech_API.sGTItemIconload.add(this); + } + + @Override + public IIcon getIcon() { + return mIcon; + } + + @Override + public IIcon getOverlayIcon() { + return mOverlay; + } + + @Override + public ResourceLocation getTextureFile() { + return TextureMap.locationItemsTexture; + } + + @Override + public void run() { + mIcon = GregTech_API.sItemIcons.registerIcon(GregTech.getResourcePath(mIconName)); + mOverlay = GregTech_API.sItemIcons.registerIcon(GregTech.getResourcePath(mIconName + "_OVERLAY")); + } + } + } +} diff --git a/src/main/java/gregtech/api/enums/TickTime.java b/src/main/java/gregtech/api/enums/TickTime.java new file mode 100644 index 0000000000..28c68e172f --- /dev/null +++ b/src/main/java/gregtech/api/enums/TickTime.java @@ -0,0 +1,10 @@ +package gregtech.api.enums; + +public class TickTime { + + public static final int TICK = 1; + public static final int SECOND = TICK * 20; + public static final int MINUTE = SECOND * 60; + public static final int HOUR = MINUTE * 60; + public static final int DAY = HOUR * 24; +} diff --git a/src/main/java/gregtech/api/enums/Tier.java b/src/main/java/gregtech/api/enums/Tier.java new file mode 100644 index 0000000000..84e8344334 --- /dev/null +++ b/src/main/java/gregtech/api/enums/Tier.java @@ -0,0 +1,500 @@ +package gregtech.api.enums; + +import static gregtech.api.enums.GT_Values.V; + +/** + * Experimental Class for later + */ +public class Tier { + + public static final Tier[] ELECTRIC = new Tier[] { + new Tier( + SubTag.ENERGY_ELECTRICITY, + 0, + V[0], + 1, + 1, + 1, + Materials.WroughtIron, + ItemList.Hull_ULV, + OrePrefixes.cableGt01.get(Materials.Lead), + OrePrefixes.cableGt04.get(Materials.Lead), + OrePrefixes.circuit.get(Materials.Primitive), + OrePrefixes.circuit.get(Materials.Basic)), + new Tier( + SubTag.ENERGY_ELECTRICITY, + 1, + V[1], + 1, + 1, + 1, + Materials.Steel, + ItemList.Hull_LV, + OrePrefixes.cableGt01.get(Materials.Tin), + OrePrefixes.cableGt04.get(Materials.Tin), + OrePrefixes.circuit.get(Materials.Basic), + OrePrefixes.circuit.get(Materials.Good)), + new Tier( + SubTag.ENERGY_ELECTRICITY, + 2, + V[2], + 1, + 1, + 1, + Materials.Aluminium, + ItemList.Hull_MV, + OrePrefixes.cableGt01.get(Materials.AnyCopper), + OrePrefixes.cableGt04.get(Materials.AnyCopper), + OrePrefixes.circuit.get(Materials.Good), + OrePrefixes.circuit.get(Materials.Advanced)), + new Tier( + SubTag.ENERGY_ELECTRICITY, + 3, + V[3], + 1, + 1, + 1, + Materials.StainlessSteel, + ItemList.Hull_HV, + OrePrefixes.cableGt01.get(Materials.Gold), + OrePrefixes.cableGt04.get(Materials.Gold), + OrePrefixes.circuit.get(Materials.Advanced), + OrePrefixes.circuit.get(Materials.Data)), + new Tier( + SubTag.ENERGY_ELECTRICITY, + 4, + V[4], + 1, + 1, + 1, + Materials.Titanium, + ItemList.Hull_EV, + OrePrefixes.cableGt01.get(Materials.Aluminium), + OrePrefixes.cableGt04.get(Materials.Aluminium), + OrePrefixes.circuit.get(Materials.Data), + OrePrefixes.circuit.get(Materials.Elite)), + new Tier( + SubTag.ENERGY_ELECTRICITY, + 5, + V[5], + 1, + 1, + 1, + Materials.TungstenSteel, + ItemList.Hull_IV, + OrePrefixes.cableGt01.get(Materials.Platinum), + OrePrefixes.cableGt04.get(Materials.Platinum), + OrePrefixes.circuit.get(Materials.Elite), + OrePrefixes.circuit.get(Materials.Master)), + new Tier( + SubTag.ENERGY_ELECTRICITY, + 6, + V[6], + 1, + 1, + 1, + Materials.Chrome, + ItemList.Hull_LuV, + OrePrefixes.cableGt01.get(Materials.NiobiumTitanium), + OrePrefixes.cableGt04.get(Materials.NiobiumTitanium), + OrePrefixes.circuit.get(Materials.Master), + OrePrefixes.circuit.get(Materials.Ultimate)), + new Tier( + SubTag.ENERGY_ELECTRICITY, + 7, + V[7], + 1, + 1, + 1, + Materials.Iridium, + ItemList.Hull_ZPM, + OrePrefixes.cableGt01.get(Materials.Naquadah), + OrePrefixes.cableGt04.get(Materials.Naquadah), + OrePrefixes.circuit.get(Materials.Ultimate), + OrePrefixes.circuit.get(Materials.SuperconductorUHV)), + new Tier( + SubTag.ENERGY_ELECTRICITY, + 8, + V[8], + 1, + 1, + 1, + Materials.Osmium, + ItemList.Hull_UV, + OrePrefixes.cableGt04.get(Materials.NaquadahAlloy), + OrePrefixes.wireGt01.get(Materials.SuperconductorUHV), + OrePrefixes.circuit.get(Materials.SuperconductorUHV), + OrePrefixes.circuit.get(Materials.Infinite)), + new Tier( + SubTag.ENERGY_ELECTRICITY, + 9, + V[9], + 1, + 1, + 1, + Materials.Neutronium, + ItemList.Hull_MAX, + OrePrefixes.wireGt01.get(Materials.SuperconductorUHV), + OrePrefixes.wireGt04.get(Materials.SuperconductorUHV), + OrePrefixes.circuit.get(Materials.Infinite), + OrePrefixes.circuit.get(Materials.Infinite)), + new Tier( + SubTag.ENERGY_ELECTRICITY, + 10, + V[10], + 1, + 1, + 1, + Materials.Neutronium, + ItemList.Hull_MAX, + OrePrefixes.wireGt01.get(Materials.SuperconductorUHV), + OrePrefixes.wireGt04.get(Materials.SuperconductorUHV), + OrePrefixes.circuit.get(Materials.Infinite), + OrePrefixes.circuit.get(Materials.Infinite)), + new Tier( + SubTag.ENERGY_ELECTRICITY, + 11, + V[11], + 1, + 1, + 1, + Materials.Neutronium, + ItemList.Hull_MAX, + OrePrefixes.wireGt01.get(Materials.SuperconductorUHV), + OrePrefixes.wireGt04.get(Materials.SuperconductorUHV), + OrePrefixes.circuit.get(Materials.Infinite), + OrePrefixes.circuit.get(Materials.Infinite)), + new Tier( + SubTag.ENERGY_ELECTRICITY, + 12, + V[12], + 1, + 1, + 1, + Materials.Neutronium, + ItemList.Hull_MAX, + OrePrefixes.wireGt01.get(Materials.SuperconductorUHV), + OrePrefixes.wireGt04.get(Materials.SuperconductorUHV), + OrePrefixes.circuit.get(Materials.Infinite), + OrePrefixes.circuit.get(Materials.Infinite)), + new Tier( + SubTag.ENERGY_ELECTRICITY, + 13, + V[13], + 1, + 1, + 1, + Materials.Neutronium, + ItemList.Hull_MAX, + OrePrefixes.wireGt01.get(Materials.SuperconductorUHV), + OrePrefixes.wireGt04.get(Materials.SuperconductorUHV), + OrePrefixes.circuit.get(Materials.Infinite), + OrePrefixes.circuit.get(Materials.Infinite)), + new Tier( + SubTag.ENERGY_ELECTRICITY, + 14, + V[14], + 1, + 1, + 1, + Materials.Neutronium, + ItemList.Hull_MAX, + OrePrefixes.wireGt01.get(Materials.SuperconductorUHV), + OrePrefixes.wireGt04.get(Materials.SuperconductorUHV), + OrePrefixes.circuit.get(Materials.Infinite), + OrePrefixes.circuit.get(Materials.Infinite)), + new Tier( + SubTag.ENERGY_ELECTRICITY, + 15, + V[15], + 1, + 1, + 1, + Materials.Neutronium, + ItemList.Hull_MAX, + OrePrefixes.wireGt01.get(Materials.SuperconductorUHV), + OrePrefixes.wireGt04.get(Materials.SuperconductorUHV), + OrePrefixes.circuit.get(Materials.Infinite), + OrePrefixes.circuit.get(Materials.Infinite)), + + // READ GT_VALUES CLASS BEFORE YOU START ADDING STUFF TO TIERS 8+ - and probably dont do it in + // GT but in GTNH core mod - that way we shouldnt need to set the tier class + }, ROTATIONAL = new Tier[] { + new Tier( + SubTag.ENERGY_ROTATIONAL, + 1, + 32, + 1, + 1, + 1, + Materials.Wood, + OrePrefixes.frameGt.get(Materials.Wood), + OrePrefixes.stick.get(Materials.Wood), + OrePrefixes.ingot.get(Materials.Wood), + OrePrefixes.gearGt.get(Materials.Wood), + OrePrefixes.gearGt.get(Materials.Stone)), + new Tier( + SubTag.ENERGY_ROTATIONAL, + 1, + 32, + 1, + 2, + 2, + Materials.WoodSealed, + OrePrefixes.frameGt.get(Materials.WoodSealed), + OrePrefixes.stick.get(Materials.WoodSealed), + OrePrefixes.ingot.get(Materials.WoodSealed), + OrePrefixes.gearGt.get(Materials.WoodSealed), + OrePrefixes.gearGt.get(Materials.Stone)), + new Tier( + SubTag.ENERGY_ROTATIONAL, + 2, + 128, + 1, + 1, + 1, + Materials.Stone, + OrePrefixes.frameGt.get(Materials.Stone), + OrePrefixes.stick.get(Materials.Stone), + OrePrefixes.ingot.get(Materials.Stone), + OrePrefixes.gearGt.get(Materials.Stone), + OrePrefixes.gearGt.get(Materials.Bronze)), + new Tier( + SubTag.ENERGY_ROTATIONAL, + 2, + 128, + 1, + 2, + 2, + Materials.IronWood, + OrePrefixes.frameGt.get(Materials.IronWood), + OrePrefixes.stick.get(Materials.IronWood), + OrePrefixes.ingot.get(Materials.IronWood), + OrePrefixes.gearGt.get(Materials.IronWood), + OrePrefixes.gearGt.get(Materials.Bronze)), + new Tier( + SubTag.ENERGY_ROTATIONAL, + 3, + 512, + 1, + 1, + 1, + Materials.Bronze, + OrePrefixes.frameGt.get(Materials.Bronze), + OrePrefixes.stick.get(Materials.Bronze), + OrePrefixes.ingot.get(Materials.Bronze), + OrePrefixes.gearGt.get(Materials.Bronze), + OrePrefixes.gearGt.get(Materials.Steel)), + new Tier( + SubTag.ENERGY_ROTATIONAL, + 3, + 512, + 1, + 2, + 2, + Materials.Brass, + OrePrefixes.frameGt.get(Materials.Brass), + OrePrefixes.stick.get(Materials.Brass), + OrePrefixes.ingot.get(Materials.Brass), + OrePrefixes.gearGt.get(Materials.Brass), + OrePrefixes.gearGt.get(Materials.Steel)), + new Tier( + SubTag.ENERGY_ROTATIONAL, + 4, + 2048, + 1, + 1, + 1, + Materials.Steel, + OrePrefixes.frameGt.get(Materials.Steel), + OrePrefixes.stick.get(Materials.Steel), + OrePrefixes.ingot.get(Materials.Steel), + OrePrefixes.gearGt.get(Materials.Steel), + OrePrefixes.gearGt.get(Materials.TungstenSteel)), + new Tier( + SubTag.ENERGY_ROTATIONAL, + 4, + 2048, + 1, + 2, + 2, + Materials.Titanium, + OrePrefixes.frameGt.get(Materials.Titanium), + OrePrefixes.stick.get(Materials.Titanium), + OrePrefixes.ingot.get(Materials.Titanium), + OrePrefixes.gearGt.get(Materials.Titanium), + OrePrefixes.gearGt.get(Materials.TungstenSteel)), + new Tier( + SubTag.ENERGY_ROTATIONAL, + 5, + 8192, + 1, + 1, + 1, + Materials.TungstenSteel, + OrePrefixes.frameGt.get(Materials.TungstenSteel), + OrePrefixes.stick.get(Materials.TungstenSteel), + OrePrefixes.ingot.get(Materials.TungstenSteel), + OrePrefixes.gearGt.get(Materials.TungstenSteel), + OrePrefixes.gearGt.get(Materials.Iridium)), + new Tier( + SubTag.ENERGY_ROTATIONAL, + 6, + 32768, + 1, + 1, + 1, + Materials.Iridium, + OrePrefixes.frameGt.get(Materials.Iridium), + OrePrefixes.stick.get(Materials.Iridium), + OrePrefixes.ingot.get(Materials.Iridium), + OrePrefixes.gearGt.get(Materials.Iridium), + OrePrefixes.gearGt.get(Materials.Neutronium)), + new Tier( + SubTag.ENERGY_ROTATIONAL, + 9, + Integer.MAX_VALUE - 7, + 1, + 1, + 1, + Materials.Neutronium, + OrePrefixes.frameGt.get(Materials.Neutronium), + OrePrefixes.stick.get(Materials.Neutronium), + OrePrefixes.ingot.get(Materials.Neutronium), + OrePrefixes.gearGt.get(Materials.Neutronium), + OrePrefixes.gearGt.get(Materials.Neutronium)), }, + STEAM = new Tier[] { + new Tier( + SubTag.ENERGY_STEAM, + 1, + 32, + 1, + 1, + 1, + Materials.Bronze, + OrePrefixes.frameGt.get(Materials.Bronze), + OrePrefixes.pipeMedium.get(Materials.Bronze), + OrePrefixes.pipeHuge.get(Materials.Bronze), + OrePrefixes.pipeMedium.get(Materials.Bronze), + OrePrefixes.pipeLarge.get(Materials.Bronze)), + new Tier( + SubTag.ENERGY_STEAM, + 2, + 128, + 1, + 1, + 1, + Materials.Steel, + OrePrefixes.frameGt.get(Materials.Steel), + OrePrefixes.pipeMedium.get(Materials.Steel), + OrePrefixes.pipeHuge.get(Materials.Steel), + OrePrefixes.pipeMedium.get(Materials.Steel), + OrePrefixes.pipeLarge.get(Materials.Steel)), + new Tier( + SubTag.ENERGY_STEAM, + 3, + 512, + 1, + 1, + 1, + Materials.Titanium, + OrePrefixes.frameGt.get(Materials.Titanium), + OrePrefixes.pipeMedium.get(Materials.Titanium), + OrePrefixes.pipeHuge.get(Materials.Titanium), + OrePrefixes.pipeMedium.get(Materials.Titanium), + OrePrefixes.pipeLarge.get(Materials.Titanium)), + new Tier( + SubTag.ENERGY_STEAM, + 4, + 2048, + 1, + 1, + 1, + Materials.TungstenSteel, + OrePrefixes.frameGt.get(Materials.TungstenSteel), + OrePrefixes.pipeMedium.get(Materials.TungstenSteel), + OrePrefixes.pipeHuge.get(Materials.TungstenSteel), + OrePrefixes.pipeMedium.get(Materials.TungstenSteel), + OrePrefixes.pipeLarge.get(Materials.TungstenSteel)), + new Tier( + SubTag.ENERGY_STEAM, + 5, + 8192, + 1, + 1, + 1, + Materials.Iridium, + OrePrefixes.frameGt.get(Materials.Iridium), + OrePrefixes.pipeMedium.get(Materials.Iridium), + OrePrefixes.pipeHuge.get(Materials.Iridium), + OrePrefixes.pipeMedium.get(Materials.Iridium), + OrePrefixes.pipeLarge.get(Materials.Iridium)), + new Tier( + SubTag.ENERGY_STEAM, + 9, + Integer.MAX_VALUE - 7, + 1, + 1, + 1, + Materials.Neutronium, + OrePrefixes.frameGt.get(Materials.Neutronium), + OrePrefixes.pipeMedium.get(Materials.Neutronium), + OrePrefixes.pipeHuge.get(Materials.Neutronium), + OrePrefixes.pipeMedium.get(Materials.Neutronium), + OrePrefixes.pipeLarge.get(Materials.Neutronium)), }; + /** + * Used for Crafting Recipes + */ + public final Object mHullObject, mConductingObject, mLargerConductingObject, mManagingObject, mBetterManagingObject; + + private final SubTag mType; + private final byte mRank; + private final long mPrimaryValue, mSecondaryValue, mSpeedMultiplier, mEnergyCostMultiplier; + private final Materials mMaterial; + + public Tier(SubTag aType, int aRank, long aPrimaryValue, long aSecondaryValue, long aSpeedMultiplier, + long aEnergyCostMultiplier, Materials aMaterial, Object aHullObject, Object aConductingObject, + Object aLargerConductingObject, Object aManagingObject, Object aBetterManagingObject) { + mType = aType; + mRank = (byte) aRank; + mPrimaryValue = aPrimaryValue; + mSecondaryValue = aSecondaryValue; + mSpeedMultiplier = aSpeedMultiplier; + mEnergyCostMultiplier = Math.max(mSpeedMultiplier, aEnergyCostMultiplier); + mMaterial = aMaterial; + + mHullObject = aHullObject; + mConductingObject = aConductingObject; + mManagingObject = aManagingObject; + mBetterManagingObject = aBetterManagingObject; + mLargerConductingObject = aLargerConductingObject; + } + + public byte getRank() { + return mRank; + } + + public SubTag getEnergyType() { + return mType; + } + + public long getEnergyPrimary() { + return mPrimaryValue; + } + + public long getEnergySecondary() { + return mSecondaryValue; + } + + public long getSpeedMultiplier() { + return mSpeedMultiplier; + } + + public long getEnergyCostMultiplier() { + return mEnergyCostMultiplier; + } + + public Materials getMaterial() { + return mMaterial; + } +} diff --git a/src/main/java/gregtech/api/enums/TierEU.java b/src/main/java/gregtech/api/enums/TierEU.java new file mode 100644 index 0000000000..26b6393115 --- /dev/null +++ b/src/main/java/gregtech/api/enums/TierEU.java @@ -0,0 +1,40 @@ +package gregtech.api.enums; + +import static gregtech.api.enums.GT_Values.V; + +public class TierEU { + + // Do NOT use these for crafting recipes as they are exactly 1A! Use RECIPE_ULV etc. + public static final long ULV = V[GTVoltageIndex.ULV]; + public static final long LV = V[GTVoltageIndex.LV]; + public static final long MV = V[GTVoltageIndex.MV]; + public static final long HV = V[GTVoltageIndex.HV]; + public static final long EV = V[GTVoltageIndex.EV]; + public static final long IV = V[GTVoltageIndex.IV]; + public static final long LuV = V[GTVoltageIndex.LuV]; + public static final long ZPM = V[GTVoltageIndex.ZPM]; + public static final long UV = V[GTVoltageIndex.UV]; + public static final long UHV = V[GTVoltageIndex.UHV]; + public static final long UEV = V[GTVoltageIndex.UEV]; + public static final long UIV = V[GTVoltageIndex.UIV]; + public static final long UMV = V[GTVoltageIndex.UMV]; + public static final long UXV = V[GTVoltageIndex.UXV]; + public static final long MAX = V[GTVoltageIndex.MAX]; + + // Use me for recipes. + public static final long RECIPE_ULV = GT_Values.VP[GTVoltageIndex.ULV]; + public static final long RECIPE_LV = GT_Values.VP[GTVoltageIndex.LV]; + public static final long RECIPE_MV = GT_Values.VP[GTVoltageIndex.MV]; + public static final long RECIPE_HV = GT_Values.VP[GTVoltageIndex.HV]; + public static final long RECIPE_EV = GT_Values.VP[GTVoltageIndex.EV]; + public static final long RECIPE_IV = GT_Values.VP[GTVoltageIndex.IV]; + public static final long RECIPE_LuV = GT_Values.VP[GTVoltageIndex.LuV]; + public static final long RECIPE_ZPM = GT_Values.VP[GTVoltageIndex.ZPM]; + public static final long RECIPE_UV = GT_Values.VP[GTVoltageIndex.UV]; + public static final long RECIPE_UHV = GT_Values.VP[GTVoltageIndex.UHV]; + public static final long RECIPE_UEV = GT_Values.VP[GTVoltageIndex.UEV]; + public static final long RECIPE_UIV = GT_Values.VP[GTVoltageIndex.UIV]; + public static final long RECIPE_UMV = GT_Values.VP[GTVoltageIndex.UMV]; + public static final long RECIPE_UXV = GT_Values.VP[GTVoltageIndex.UXV]; + public static final long RECIPE_MAX = GT_Values.VP[GTVoltageIndex.MAX]; +} diff --git a/src/main/java/gregtech/api/enums/ToolDictNames.java b/src/main/java/gregtech/api/enums/ToolDictNames.java new file mode 100644 index 0000000000..e4a8ee8444 --- /dev/null +++ b/src/main/java/gregtech/api/enums/ToolDictNames.java @@ -0,0 +1,44 @@ +package gregtech.api.enums; + +public enum ToolDictNames { + + craftingToolSaw, + craftingToolHoe, + craftingToolAxe, + craftingToolFile, + craftingToolPlow, + craftingToolDrill, + craftingToolSword, + craftingToolScoop, + craftingToolKnife, + craftingToolBlade, + craftingToolMortar, + craftingToolShovel, + craftingToolWrench, + craftingToolPlunger, + craftingToolCrowbar, + craftingToolPickaxe, + craftingToolDrawplate, + craftingToolRollingPin, + craftingToolWireCutter, + craftingToolBranchCutter, + craftingToolHardHammer, + craftingToolSoftHammer, + craftingToolJackHammer, + craftingToolMiningDrill, + craftingToolForgeHammer, + craftingToolScrewdriver, + craftingToolSolderingIron, + craftingToolSolderingMetal; + + public static boolean contains(String aName) { + if (!aName.startsWith("craftingTool")) return false; + for (ToolDictNames tool : ToolDictNames.values()) { + if (tool.toString() + .equals(aName)) { + return true; + } + } + return false; + } +} diff --git a/src/main/java/gregtech/api/enums/ToolModes.java b/src/main/java/gregtech/api/enums/ToolModes.java new file mode 100644 index 0000000000..2f849279a4 --- /dev/null +++ b/src/main/java/gregtech/api/enums/ToolModes.java @@ -0,0 +1,18 @@ +package gregtech.api.enums; + +public enum ToolModes { + + REGULAR(0), + WRENCH_LINE(1); + + private final byte modeValue; + + ToolModes(int modeValue) { + this.modeValue = (byte) modeValue; + } + + public byte get() { + return modeValue; + } + +} diff --git a/src/main/java/gregtech/api/enums/VoidingMode.java b/src/main/java/gregtech/api/enums/VoidingMode.java new file mode 100644 index 0000000000..8ae9dda57d --- /dev/null +++ b/src/main/java/gregtech/api/enums/VoidingMode.java @@ -0,0 +1,112 @@ +package gregtech.api.enums; + +import java.util.Collection; +import java.util.EnumSet; +import java.util.Set; + +import javax.annotation.Nonnull; + +import com.gtnewhorizons.modularui.api.drawable.UITexture; + +import gregtech.api.gui.modularui.GT_UITextures; + +public enum VoidingMode { + + /** + * Voids nothing, protects both item and fluid + */ + VOID_NONE(true, true, GT_UITextures.BUTTON_STANDARD, GT_UITextures.OVERLAY_BUTTON_VOID_EXCESS_NONE, "none"), + /** + * Voids item, protects fluid + */ + VOID_ITEM(false, true, GT_UITextures.BUTTON_STANDARD_PRESSED, GT_UITextures.OVERLAY_BUTTON_VOID_EXCESS_ITEM, + "item"), + /** + * Voids fluid, protects item + */ + VOID_FLUID(true, false, GT_UITextures.BUTTON_STANDARD_PRESSED, GT_UITextures.OVERLAY_BUTTON_VOID_EXCESS_FLUID, + "fluid"), + /** + * Voids all, protects nothing + */ + VOID_ALL(false, false, GT_UITextures.BUTTON_STANDARD_PRESSED, GT_UITextures.OVERLAY_BUTTON_VOID_EXCESS_ALL, "all"); + + /** + * Default set of voiding mode you will probably support. + */ + public static final Set<VoidingMode> ALL_OPTIONS = EnumSet.allOf(VoidingMode.class); + /** + * Set of voiding mode you will probably support if your machine has no item output + */ + public static final Set<VoidingMode> FLUID_ONLY_MODES = EnumSet.of(VOID_FLUID, VOID_NONE); + /** + * Set of voiding mode you will probably support if your machine has no fluid output + */ + public static final Set<VoidingMode> ITEM_ONLY_MODES = EnumSet.of(VOID_ITEM, VOID_NONE); + public final boolean protectItem; + public final boolean protectFluid; + public final UITexture buttonTexture; + public final UITexture buttonOverlay; + public final String name; + + VoidingMode(boolean protectItem, boolean protectFluid, UITexture buttonTexture, UITexture buttonOverlay, + String name) { + this.protectItem = protectItem; + this.protectFluid = protectFluid; + this.buttonTexture = buttonTexture; + this.buttonOverlay = buttonOverlay; + this.name = name; + } + + public String getTransKey() { + return "GT5U.gui.button.voiding_mode_" + name; + } + + public VoidingMode next() { + return values()[(ordinal() + 1) % values().length]; + } + + public VoidingMode previous() { + return values()[(ordinal() + values().length - 1) % values().length]; + } + + public VoidingMode nextInCollection(Collection<VoidingMode> allowed) { + if (allowed.isEmpty()) throw new IllegalArgumentException("nothing allowed"); + VoidingMode ret = this; + do { + ret = ret.next(); + } while (!allowed.contains(ret)); + return ret; + } + + public VoidingMode previousInCollection(Collection<VoidingMode> allowed) { + if (allowed.isEmpty()) throw new IllegalArgumentException("nothing allowed"); + VoidingMode ret = this; + do { + ret = ret.previous(); + } while (!allowed.contains(ret)); + return ret; + } + + /** + * Do not use this for loading mode from TEs, to prevent mode being shifted when new mode is added. + */ + @Nonnull + public static VoidingMode fromOrdinal(int ordinal) { + if (ordinal >= 0 && ordinal < values().length) { + return values()[ordinal]; + } + return VOID_NONE; + } + + @Nonnull + public static VoidingMode fromName(String name) { + for (VoidingMode mode : values()) { + if (mode.name.equals(name)) { + return mode; + } + } + return VOID_NONE; + } + +} diff --git a/src/main/java/gregtech/api/events/BlockScanningEvent.java b/src/main/java/gregtech/api/events/BlockScanningEvent.java new file mode 100644 index 0000000000..bc2a33bcea --- /dev/null +++ b/src/main/java/gregtech/api/events/BlockScanningEvent.java @@ -0,0 +1,47 @@ +package gregtech.api.events; + +import java.util.ArrayList; + +import net.minecraft.block.Block; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.world.World; +import net.minecraftforge.common.util.ForgeDirection; +import net.minecraftforge.event.world.WorldEvent; + +import cpw.mods.fml.common.eventhandler.Cancelable; + +@Cancelable +public class BlockScanningEvent extends WorldEvent { + + public final EntityPlayer mPlayer; + public final int mX, mY, mZ, mScanLevel; + public final ArrayList<String> mList; + public final ForgeDirection mSide; + public final float mClickX, mClickY, mClickZ; + public final TileEntity mTileEntity; + public final Block mBlock; + + /** + * used to determine the amount of Energy this Scan is costing. + */ + public int mEUCost = 0; + + public BlockScanningEvent(World aWorld, EntityPlayer aPlayer, int aX, int aY, int aZ, ForgeDirection side, + int aScanLevel, Block aBlock, TileEntity aTileEntity, ArrayList<String> aList, float aClickX, float aClickY, + float aClickZ) { + super(aWorld); + mPlayer = aPlayer; + mScanLevel = aScanLevel; + mTileEntity = aTileEntity; + mBlock = aBlock; + mList = aList; + mSide = side; + mX = aX; + mY = aY; + mZ = aZ; + mClickX = aClickX; + mClickY = aClickY; + mClickZ = aClickZ; + } +} diff --git a/src/main/java/gregtech/api/fluid/FluidTankGT.java b/src/main/java/gregtech/api/fluid/FluidTankGT.java new file mode 100644 index 0000000000..0c224985e6 --- /dev/null +++ b/src/main/java/gregtech/api/fluid/FluidTankGT.java @@ -0,0 +1,485 @@ +package gregtech.api.fluid; + +import static com.google.common.primitives.Ints.saturatedCast; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import javax.annotation.Nonnull; + +import net.minecraft.nbt.NBTTagCompound; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.FluidTankInfo; +import net.minecraftforge.fluids.IFluidTank; + +import gregtech.api.interfaces.fluid.IFluidStore; +import gregtech.api.util.GT_Utility; + +public class FluidTankGT implements IFluidTank, IFluidStore { + + public final FluidTankGT[] AS_ARRAY = new FluidTankGT[] { this }; + private FluidStack mFluid; + private long mCapacity = 0, mAmount = 0; + private boolean mPreventDraining = false, mVoidExcess = false, mChangedFluids = false; + /** HashMap of adjustable Tank Sizes based on Fluids if needed. */ + private Map<String, Long> mAdjustableCapacity = null; + + private long mAdjustableMultiplier = 1; + /** Gives you a Tank Index in case there is multiple Tanks on a TileEntity that cares. */ + public int mIndex = 0; + + public FluidTankGT() { + mCapacity = Long.MAX_VALUE; + } + + public FluidTankGT(long aCapacity) { + mCapacity = aCapacity; + } + + public FluidTankGT(FluidStack aFluid) { + mFluid = aFluid; + if (aFluid != null) { + mCapacity = aFluid.amount; + mAmount = aFluid.amount; + } + } + + public FluidTankGT(FluidStack aFluid, long aCapacity) { + mFluid = aFluid; + mCapacity = aCapacity; + mAmount = (aFluid == null ? 0 : aFluid.amount); + } + + public FluidTankGT(FluidStack aFluid, long aAmount, long aCapacity) { + mFluid = aFluid; + mCapacity = aCapacity; + mAmount = (aFluid == null ? 0 : aAmount); + } + + public FluidTankGT(Fluid aFluid, long aAmount) { + this(new FluidStack(aFluid, saturatedCast(aAmount))); + mAmount = aAmount; + } + + public FluidTankGT(Fluid aFluid, long aAmount, long aCapacity) { + this(new FluidStack(aFluid, saturatedCast(aAmount)), aCapacity); + mAmount = aAmount; + } + + public FluidTankGT(NBTTagCompound aNBT, String aKey, long aCapacity) { + this(aNBT.hasKey(aKey) ? aNBT.getCompoundTag(aKey) : null, aCapacity); + } + + public FluidTankGT(NBTTagCompound aNBT, long aCapacity) { + mCapacity = aCapacity; + if (aNBT != null && !aNBT.hasNoTags()) { + mFluid = FluidStack.loadFluidStackFromNBT(aNBT); + mAmount = (isEmpty() ? 0 : aNBT.hasKey("LAmount") ? aNBT.getLong("LAmount") : mFluid.amount); + } + } + + public FluidTankGT readFromNBT(NBTTagCompound aNBT, String aKey) { + if (aNBT.hasKey(aKey)) { + aNBT = aNBT.getCompoundTag(aKey); + if (aNBT != null && !aNBT.hasNoTags()) { + mFluid = FluidStack.loadFluidStackFromNBT(aNBT); + mAmount = (isEmpty() ? 0 : aNBT.hasKey("LAmount") ? aNBT.getLong("LAmount") : mFluid.amount); + } + } + return this; + } + + public NBTTagCompound writeToNBT(NBTTagCompound aNBT, String aKey) { + if (mFluid != null && (mPreventDraining || mAmount > 0)) { + final NBTTagCompound tNBT = new NBTTagCompound(); + mFluid.amount = (int) mAmount; + aNBT.setTag(aKey, mFluid.writeToNBT(tNBT)); + if (mAmount > Integer.MAX_VALUE) tNBT.setLong("LAmount", mAmount); + } else { + aNBT.removeTag(aKey); + } + return aNBT; + } + + public FluidStack drain(int aDrained) { + return drain(aDrained, true); + } + + @Override + public FluidStack drain(int aDrained, boolean aDoDrain) { + if (isEmpty() || aDrained <= 0) return null; + if (mAmount < aDrained) aDrained = (int) mAmount; + final FluidStack rFluid = new FluidStack(mFluid, aDrained); + if (aDoDrain) { + mAmount -= aDrained; + if (mAmount <= 0) { + if (mPreventDraining) { + mAmount = 0; + } else { + setEmpty(); + } + } + } + return rFluid; + } + + public boolean drainAll(long aDrained) { + if (isEmpty() || mAmount < aDrained) return false; + mAmount -= aDrained; + if (mAmount <= 0) { + if (mPreventDraining) { + mAmount = 0; + } else { + setEmpty(); + } + } + return true; + } + + public long remove(long aDrained) { + if (isEmpty() || mAmount <= 0 || aDrained <= 0) return 0; + if (mAmount < aDrained) aDrained = mAmount; + mAmount -= aDrained; + if (mAmount <= 0) { + if (mPreventDraining) { + mAmount = 0; + } else { + setEmpty(); + } + } + return aDrained; + } + + public long add(long aFilled) { + if (isEmpty() || aFilled <= 0) return 0; + final long tCapacity = capacity(); + if (mAmount + aFilled > tCapacity) { + if (!mVoidExcess) aFilled = tCapacity - mAmount; + mAmount = tCapacity; + return aFilled; + } + mAmount += aFilled; + return aFilled; + } + + public long add(long aFilled, FluidStack aFluid) { + if (aFluid == null || aFilled <= 0) return 0; + if (isEmpty()) { + mFluid = aFluid.copy(); + mChangedFluids = true; + mAmount = Math.min(capacity(aFluid), aFilled); + return mVoidExcess ? aFilled : mAmount; + } + return contains(aFluid) ? add(aFilled) : 0; + } + + public int fill(FluidStack aFluid) { + return fill(aFluid, true); + } + + @Override + public int fill(FluidStack aFluid, boolean aDoFill) { + if (aFluid == null) return 0; + if (aDoFill) { + if (isEmpty()) { + mFluid = aFluid.copy(); + mChangedFluids = true; + mAmount = Math.min(capacity(aFluid), aFluid.amount); + return mVoidExcess ? aFluid.amount : (int) mAmount; + } + if (!contains(aFluid)) return 0; + final long tCapacity = capacity(aFluid); + long tFilled = tCapacity - mAmount; + if (aFluid.amount < tFilled) { + mAmount += aFluid.amount; + tFilled = aFluid.amount; + } else mAmount = tCapacity; + return mVoidExcess ? aFluid.amount : (int) tFilled; + } + return saturatedCast( + isEmpty() ? mVoidExcess ? aFluid.amount : Math.min(capacity(aFluid), aFluid.amount) + : contains(aFluid) ? mVoidExcess ? aFluid.amount : Math.min(capacity(aFluid) - mAmount, aFluid.amount) + : 0); + } + + public boolean canFillAll(FluidStack aFluid) { + return aFluid == null || aFluid.amount <= 0 + || (isEmpty() ? mVoidExcess || aFluid.amount <= capacity(aFluid) + : contains(aFluid) && (mVoidExcess || mAmount + aFluid.amount <= capacity(aFluid))); + } + + public boolean canFillAll(long aAmount) { + return aAmount <= 0 || mVoidExcess || mAmount + aAmount <= capacity(); + } + + public boolean fillAll(FluidStack aFluid) { + if (aFluid == null || aFluid.amount <= 0) return true; + if (isEmpty()) { + final long tCapacity = capacity(aFluid); + if (aFluid.amount <= tCapacity || mVoidExcess) { + mFluid = aFluid.copy(); + mChangedFluids = true; + mAmount = aFluid.amount; + if (mAmount > tCapacity) mAmount = tCapacity; + return true; + } + return false; + } + if (contains(aFluid)) { + if (mAmount + aFluid.amount <= capacity()) { + mAmount += aFluid.amount; + return true; + } + if (mVoidExcess) { + mAmount = capacity(); + return true; + } + } + return false; + } + + public boolean fillAll(FluidStack aFluid, long aMultiplier) { + if (aMultiplier <= 0) return true; + if (aMultiplier == 1) return fillAll(aFluid); + if (aFluid == null || aFluid.amount <= 0) return true; + if (isEmpty()) { + final long tCapacity = capacity(aFluid); + if (aFluid.amount * aMultiplier <= tCapacity || mVoidExcess) { + mFluid = aFluid.copy(); + mChangedFluids = true; + mAmount = aFluid.amount * aMultiplier; + if (mAmount > tCapacity) mAmount = tCapacity; + return true; + } + return false; + } + if (contains(aFluid)) { + if (mAmount + aFluid.amount * aMultiplier <= capacity()) { + mAmount += aFluid.amount * aMultiplier; + return true; + } + if (mVoidExcess) { + mAmount = capacity(); + return true; + } + } + return false; + } + + /** Resets Tank Contents entirely */ + public FluidTankGT setEmpty() { + mFluid = null; + mChangedFluids = true; + mAmount = 0; + return this; + } + + /** Sets Fluid Content, taking Amount from the Fluid Parameter */ + public FluidTankGT setFluid(FluidStack aFluid) { + mFluid = aFluid; + mChangedFluids = true; + mAmount = (aFluid == null ? 0 : aFluid.amount); + return this; + } + + /** Sets Fluid Content and Amount */ + public FluidTankGT setFluid(FluidStack aFluid, long aAmount) { + mFluid = aFluid; + mChangedFluids = true; + mAmount = (aFluid == null ? 0 : aAmount); + return this; + } + + /** Sets Fluid Content, taking Amount from the Tank Parameter */ + public FluidTankGT setFluid(FluidTankGT aTank) { + mFluid = new FluidStack(aTank.mFluid, saturatedCast(aTank.mAmount)); + mChangedFluids = true; + mAmount = aTank.mAmount; + return this; + } + + /** Sets the Tank Index for easier Reverse Mapping. */ + public FluidTankGT setIndex(int aIndex) { + mIndex = aIndex; + return this; + } + + /** Sets the Capacity */ + public FluidTankGT setCapacity(long aCapacity) { + if (aCapacity >= 0) mCapacity = aCapacity; + return this; + } + + /** Sets the Capacity Multiplier */ + public FluidTankGT setCapacityMultiplier(long aCapacityMultiplier) { + if (aCapacityMultiplier >= 0) mAdjustableMultiplier = aCapacityMultiplier; + return this; + } + + /** Sets Tank capacity Map, should it be needed. */ + public FluidTankGT setCapacity(Map<String, Long> aMap, long aCapacityMultiplier) { + mAdjustableCapacity = aMap; + mAdjustableMultiplier = aCapacityMultiplier; + return this; + } + + /** Always keeps at least 0 Liters of Fluid instead of setting it to null */ + public FluidTankGT setPreventDraining() { + return setPreventDraining(true); + } + + /** Always keeps at least 0 Liters of Fluid instead of setting it to null */ + public FluidTankGT setPreventDraining(boolean aPrevent) { + mPreventDraining = aPrevent; + return this; + } + + /** Voids any Overlow */ + public FluidTankGT setVoidExcess() { + return setVoidExcess(true); + } + + /** Voids any Overlow */ + public FluidTankGT setVoidExcess(boolean aVoidExcess) { + mVoidExcess = aVoidExcess; + return this; + } + + public boolean isFull() { + return mFluid != null && mAmount >= capacity(); + } + + public long capacity() { + return capacity(mFluid); + } + + public long capacity(FluidStack aFluid) { + if (mAdjustableCapacity == null || aFluid == null) return mCapacity; + return capacity(aFluid.getFluid()); + } + + public long capacity(Fluid aFluid) { + if (mAdjustableCapacity == null || aFluid == null) return mCapacity; + return capacity(aFluid.getName()); + } + + public long capacity(String aFluid) { + if (mAdjustableCapacity == null || aFluid == null) return mCapacity; + + final Long tSize = mAdjustableCapacity.get(aFluid); + return tSize == null ? Math.max(mAmount, mCapacity) + : Math.max(tSize * mAdjustableMultiplier, Math.max(mAmount, mCapacity)); + } + + public boolean isHalf() { + return mFluid != null && mAmount * 2 >= capacity(); + } + + public boolean contains(Fluid aFluid) { + return mFluid != null && mFluid.getFluid() == aFluid; + } + + public boolean contains(FluidStack aFluid) { + return GT_Utility.areFluidsEqual(mFluid, aFluid); + } + + public boolean has(long aAmount) { + return mAmount >= aAmount; + } + + public boolean has() { + return mAmount > 0; + } + + public boolean check() { + if (mChangedFluids) { + mChangedFluids = false; + return true; + } + return false; + } + + public boolean update() { + return mChangedFluids = true; + } + + public boolean changed() { + return mChangedFluids; + } + + public long amount() { + return isEmpty() ? 0 : mAmount; + } + + public boolean isEmpty() { + return mFluid == null; + } + + public long amount(long aMax) { + return isEmpty() || aMax <= 0 ? 0 : Math.min(mAmount, aMax); + } + + public String name() { + return mFluid == null ? null + : mFluid.getFluid() + .getName(); + } + + public FluidStack get() { + return mFluid; + } + + public FluidStack get(long aMax) { + return isEmpty() || aMax <= 0 ? null : new FluidStack(mFluid, saturatedCast(Math.min(mAmount, aMax))); + } + + @Override + public FluidStack getFluid() { + if (mFluid != null) mFluid.amount = saturatedCast(mAmount); + return mFluid; + } + + @Override + public int getFluidAmount() { + return saturatedCast(mAmount); + } + + @Override + public int getCapacity() { + return saturatedCast(capacity()); + } + + public long getCapacityMultiplier() { + return mAdjustableMultiplier; + } + + @Override + public FluidTankInfo getInfo() { + return new FluidTankInfo(isEmpty() ? null : mFluid.copy(), saturatedCast(capacity())); + } + + public static FluidStack[] getFluidsFromTanks(FluidTankGT[] tanks) { + if (tanks == null) { + return null; + } + List<FluidStack> fluidStacks = new ArrayList<>(); + for (FluidTankGT tank : tanks) { + if (tank.getFluid() != null) { + fluidStacks.add(tank.getFluid()); + } + } + return fluidStacks.toArray(new FluidStack[0]); + } + + @Override + public boolean isEmptyAndAcceptsAnyFluid() { + return getFluidAmount() == 0; + } + + @Override + public boolean canStoreFluid(@Nonnull FluidStack fluidStack) { + return GT_Utility.areFluidsEqual(mFluid, fluidStack); + } +} diff --git a/src/main/java/gregtech/api/fluid/GT_FluidFactory.java b/src/main/java/gregtech/api/fluid/GT_FluidFactory.java new file mode 100644 index 0000000000..7c65956c69 --- /dev/null +++ b/src/main/java/gregtech/api/fluid/GT_FluidFactory.java @@ -0,0 +1,90 @@ +package gregtech.api.fluid; + +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidContainerRegistry; +import net.minecraftforge.fluids.FluidRegistry; + +import gregtech.api.enums.FluidState; +import gregtech.api.enums.Materials; +import gregtech.api.interfaces.fluid.IGT_Fluid; +import gregtech.api.interfaces.fluid.IGT_FluidBuilder; +import gregtech.common.fluid.GT_FluidBuilder; + +/** + * <p> + * This class contains helpers factory methods to: + * </p> + * <ol> + * <li> + * <p> + * Build {@link IGT_Fluid} instances. + * </p> + * </li> + * <li> + * <p> + * Register the corresponding {@link Fluid}, built from an {@link IGT_Fluid}, to the {@link FluidRegistry}: + * </p> + * <ul> + * <li> + * <p> + * Register the optionally associated containers to the {@link FluidContainerRegistry}. + * </p> + * </li> + * <li> + * <p> + * Add the needed Fluid Canner recipes. + * </p> + * </li> + * </ul> + * </li> + * </ol> + */ +@SuppressWarnings("unused") // API might legitimately expose unused methods within this local project's scope +public class GT_FluidFactory { + + /** + * Helper for quick fluid creation and registration + * + * @param fluidName The name key of the {@link Fluid} to register in the {@link FluidRegistry} + * @param localizedName The localized name of this {@link IGT_Fluid} + * @param material The {@link Materials} of this {@link IGT_Fluid} + * @param state The {@link FluidState} of this {@link IGT_Fluid} + * @param temperature The fluid temperature in Kelvin + * @return the registered {@link Fluid} + */ + public static Fluid of(final String fluidName, final String localizedName, final Materials material, + final FluidState state, final int temperature) { + return builder(fluidName).withLocalizedName(localizedName) + .withStateAndTemperature(state, temperature) + .buildAndRegister() + .configureMaterials(material) + .asFluid(); + } + + /** + * Helper for quick fluid creation and registration + * + * @param fluidName The name key of the {@link Fluid} to register in the {@link FluidRegistry} + * @param localizedName The localized name of this {@link IGT_Fluid} + * @param state The {@link FluidState} of this {@link IGT_Fluid} + * @param temperature The fluid temperature in Kelvin + * @return the registered {@link Fluid} + */ + public static Fluid of(final String fluidName, final String localizedName, final FluidState state, + final int temperature) { + return builder(fluidName).withLocalizedName(localizedName) + .withStateAndTemperature(state, temperature) + .buildAndRegister() + .asFluid(); + } + + /** + * Gets an {@link IGT_Fluid} builder instance + * + * @param fluidName The name key of the {@link Fluid} to register in the {@link FluidRegistry} + * @return the {@link IGT_FluidBuilder} instance + */ + public static IGT_FluidBuilder builder(final String fluidName) { + return new GT_FluidBuilder(fluidName); + } +} diff --git a/src/main/java/gregtech/api/graphs/GenerateNodeMap.java b/src/main/java/gregtech/api/graphs/GenerateNodeMap.java new file mode 100644 index 0000000000..7289f0faad --- /dev/null +++ b/src/main/java/gregtech/api/graphs/GenerateNodeMap.java @@ -0,0 +1,202 @@ +package gregtech.api.graphs; + +import static gregtech.api.enums.GT_Values.ALL_VALID_SIDES; + +import java.util.ArrayList; +import java.util.HashSet; + +import net.minecraft.tileentity.TileEntity; +import net.minecraftforge.common.util.ForgeDirection; + +import gregtech.api.graphs.consumers.ConsumerNode; +import gregtech.api.graphs.paths.NodePath; +import gregtech.api.metatileentity.BaseMetaPipeEntity; +import gregtech.api.metatileentity.MetaPipeEntity; + +// generates the node map +public abstract class GenerateNodeMap { + + // clearing the node map to make sure it is gone on reset + public static void clearNodeMap(Node aNode, int aReturnNodeValue) { + if (aNode.mTileEntity instanceof BaseMetaPipeEntity tPipe) { + tPipe.setNode(null); + tPipe.setNodePath(null); + if (aNode.mSelfPath != null) { + aNode.mSelfPath.clearPath(); + aNode.mSelfPath = null; + } + } + for (byte side : ALL_VALID_SIDES) { + final NodePath tPath = aNode.mNodePaths[side]; + if (tPath != null) { + tPath.clearPath(); + aNode.mNodePaths[side] = null; + } + final Node tNextNode = aNode.mNeighbourNodes[side]; + if (tNextNode == null) continue; + if (tNextNode.mNodeValue != aReturnNodeValue) clearNodeMap(tNextNode, aNode.mNodeValue); + aNode.mNeighbourNodes[side] = null; + } + } + + // get how many connections the pipe have + private static int getNumberOfConnections(MetaPipeEntity aPipe) { + int tCons = 0; + for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) { + if (aPipe.isConnectedAtSide(side)) tCons++; + } + return tCons; + } + + // gets the next node + protected void generateNextNode(BaseMetaPipeEntity aPipe, Node aPipeNode, ForgeDirection aInvalidSide, + int aNextNodeValue, ArrayList<ConsumerNode> tConsumers, HashSet<Node> tNodeMap) { + final MetaPipeEntity tMetaPipe = (MetaPipeEntity) aPipe.getMetaTileEntity(); + for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) { + if (side == aInvalidSide) { + continue; + } + final TileEntity tNextTileEntity = aPipe.getTileEntityAtSide(side); + if (tNextTileEntity == null || (tMetaPipe != null && !tMetaPipe.isConnectedAtSide(side))) continue; + final ArrayList<MetaPipeEntity> tNewPipes = new ArrayList<>(); + final Pair nextTileEntity = getNextValidTileEntity(tNextTileEntity, tNewPipes, side, tNodeMap); + if (nextTileEntity != null) { + final Node tNextNode = generateNode( + nextTileEntity.mTileEntity, + aPipeNode, + aNextNodeValue + 1, + tNewPipes, + nextTileEntity.mSide, + tConsumers, + tNodeMap); + if (tNextNode != null) { + final int i = side.ordinal(); + aNextNodeValue = tNextNode.mHighestNodeValue; + aPipeNode.mHighestNodeValue = tNextNode.mHighestNodeValue; + aPipeNode.mNeighbourNodes[i] = tNextNode; + aPipeNode.mNodePaths[i] = aPipeNode.returnValues.mReturnPath; + aPipeNode.locks[i] = aPipeNode.returnValues.returnLock; + aPipeNode.mNodePaths[i].reloadLocks(); + } + } + } + aPipe.reloadLocks(); + } + + // on a valid tile entity create a new node + protected Node generateNode(TileEntity aTileEntity, Node aPreviousNode, int aNextNodeValue, + ArrayList<MetaPipeEntity> aPipes, ForgeDirection side, ArrayList<ConsumerNode> aConsumers, + HashSet<Node> aNodeMap) { + if (aTileEntity.isInvalid()) return null; + final ForgeDirection oppositeSide = side.getOpposite(); + final ForgeDirection tInvalidSide = aPreviousNode == null ? ForgeDirection.UNKNOWN : oppositeSide; + Node tThisNode = null; + if (isPipe(aTileEntity)) { + final BaseMetaPipeEntity tPipe = (BaseMetaPipeEntity) aTileEntity; + final MetaPipeEntity tMetaPipe = (MetaPipeEntity) tPipe.getMetaTileEntity(); + final int tConnections = getNumberOfConnections(tMetaPipe); + final Node tPipeNode; + if (tConnections == 1) { + tPipeNode = getEmptyNode(aNextNodeValue, oppositeSide, aTileEntity, aConsumers); + if (tPipeNode == null) return null; + } else { + tPipeNode = getPipeNode(aNextNodeValue, oppositeSide, aTileEntity, aConsumers); + } + tPipe.setNode(tPipeNode); + aNodeMap.add(tPipeNode); + tPipeNode.mSelfPath = getNewPath(new MetaPipeEntity[] { tMetaPipe }); + tThisNode = tPipeNode; + if (tInvalidSide != ForgeDirection.UNKNOWN) { + final int iInvalid = tInvalidSide.ordinal(); + tPipeNode.mNeighbourNodes[iInvalid] = aPreviousNode; + tPipeNode.mNodePaths[iInvalid] = getNewPath(aPipes.toArray(new MetaPipeEntity[0])); + final Lock lock = new Lock(); + tPipeNode.mNodePaths[oppositeSide.ordinal()].lock = lock; + tPipeNode.locks[iInvalid] = lock; + aPreviousNode.returnValues.mReturnPath = tPipeNode.mNodePaths[iInvalid]; + aPreviousNode.returnValues.returnLock = lock; + } + if (tConnections > 1) + generateNextNode(tPipe, tPipeNode, tInvalidSide, aNextNodeValue, aConsumers, aNodeMap); + } else if (addConsumer(aTileEntity, oppositeSide, aNextNodeValue, aConsumers)) { + final int oppositeSideOrdinal = oppositeSide.ordinal(); + final ConsumerNode tConsumeNode = aConsumers.get(aConsumers.size() - 1); + tConsumeNode.mNeighbourNodes[oppositeSideOrdinal] = aPreviousNode; + tConsumeNode.mNodePaths[oppositeSideOrdinal] = getNewPath(aPipes.toArray(new MetaPipeEntity[0])); + final Lock lock = new Lock(); + tConsumeNode.mNodePaths[oppositeSideOrdinal].lock = lock; + aPreviousNode.returnValues.mReturnPath = tConsumeNode.mNodePaths[oppositeSideOrdinal]; + aPreviousNode.returnValues.returnLock = lock; + tThisNode = tConsumeNode; + } + return tThisNode; + } + + // go over the pipes until we see a valid tile entity that needs a node + protected Pair getNextValidTileEntity(TileEntity aTileEntity, ArrayList<MetaPipeEntity> aPipes, ForgeDirection side, + HashSet<Node> aNodeMap) { + if (isPipe(aTileEntity)) { + final BaseMetaPipeEntity tPipe = (BaseMetaPipeEntity) aTileEntity; + final MetaPipeEntity tMetaPipe = (MetaPipeEntity) tPipe.getMetaTileEntity(); + final Node tNode = tPipe.getNode(); + if (tNode != null) { + if (aNodeMap.contains(tNode)) return null; + } + final int tConnections = getNumberOfConnections(tMetaPipe); + if (tConnections == 2) { + final ForgeDirection tSideOp = side.getOpposite(); + for (final ForgeDirection s : ForgeDirection.VALID_DIRECTIONS) { + if (s == tSideOp || !(tMetaPipe.isConnectedAtSide(s))) continue; + final TileEntity tNewTileEntity = tPipe.getTileEntityAtSide(s); + if (tNewTileEntity == null) continue; + if (isPipe(tNewTileEntity)) { + aPipes.add(tMetaPipe); + return getNextValidTileEntity(tNewTileEntity, aPipes, s, aNodeMap); + } else { + return new Pair(aTileEntity, s); + } + } + } else { + return new Pair(aTileEntity, side); + } + } else { + return new Pair(aTileEntity, side); + } + return null; + } + + // check if the tile entity is the correct pipe + protected boolean isPipe(TileEntity aTileEntity) { + return aTileEntity instanceof BaseMetaPipeEntity; + } + + // checks if the tile entity is a consumer and add to the list + protected abstract boolean addConsumer(TileEntity aTileEntity, ForgeDirection side, int aNodeValue, + ArrayList<ConsumerNode> aConsumers); + + // get correct pathClass that you need for your node network + protected abstract NodePath getNewPath(MetaPipeEntity[] aPipes); + + // used for if you need to use dead ends for something can be null + protected Node getEmptyNode(int aNodeValue, ForgeDirection side, TileEntity aTileEntity, + ArrayList<ConsumerNode> aConsumers) { + return null; + } + + // get correct node type you need for your network + protected Node getPipeNode(int aNodeValue, ForgeDirection side, TileEntity aTileEntity, + ArrayList<ConsumerNode> aConsumers) { + return new Node(aNodeValue, aTileEntity, aConsumers); + } + + private static class Pair { + + public ForgeDirection mSide; + public TileEntity mTileEntity; + + public Pair(TileEntity aTileEntity, ForgeDirection side) { + this.mTileEntity = aTileEntity; + this.mSide = side; + } + } +} diff --git a/src/main/java/gregtech/api/graphs/GenerateNodeMapPower.java b/src/main/java/gregtech/api/graphs/GenerateNodeMapPower.java new file mode 100644 index 0000000000..95f9aee32d --- /dev/null +++ b/src/main/java/gregtech/api/graphs/GenerateNodeMapPower.java @@ -0,0 +1,104 @@ +package gregtech.api.graphs; + +import java.util.ArrayList; +import java.util.HashSet; + +import net.minecraft.tileentity.TileEntity; +import net.minecraftforge.common.util.ForgeDirection; + +import cofh.api.energy.IEnergyReceiver; +import gregtech.api.GregTech_API; +import gregtech.api.graphs.consumers.ConsumerNode; +import gregtech.api.graphs.consumers.EmptyPowerConsumer; +import gregtech.api.graphs.consumers.NodeEnergyConnected; +import gregtech.api.graphs.consumers.NodeEnergyReceiver; +import gregtech.api.graphs.consumers.NodeEnergySink; +import gregtech.api.graphs.consumers.NodeGTBaseMetaTile; +import gregtech.api.graphs.paths.NodePath; +import gregtech.api.graphs.paths.PowerNodePath; +import gregtech.api.interfaces.tileentity.IEnergyConnected; +import gregtech.api.metatileentity.BaseMetaPipeEntity; +import gregtech.api.metatileentity.BaseMetaTileEntity; +import gregtech.api.metatileentity.MetaPipeEntity; +import gregtech.api.metatileentity.implementations.GT_MetaPipeEntity_Cable; +import ic2.api.energy.tile.IEnergySink; + +// node map generator for power distribution +public class GenerateNodeMapPower extends GenerateNodeMap { + + public GenerateNodeMapPower(BaseMetaPipeEntity aTileEntity) { + generateNode(aTileEntity, null, 1, null, ForgeDirection.UNKNOWN, new ArrayList<>(), new HashSet<>()); + } + + @Override + protected boolean isPipe(TileEntity aTileEntity) { + return super.isPipe(aTileEntity) + && ((BaseMetaPipeEntity) aTileEntity).getMetaTileEntity() instanceof GT_MetaPipeEntity_Cable; + } + + @Override + protected boolean addConsumer(TileEntity aTileEntity, ForgeDirection side, int aNodeValue, + ArrayList<ConsumerNode> aConsumers) { + if (aTileEntity instanceof BaseMetaTileEntity tBaseTileEntity) { + if (tBaseTileEntity.inputEnergyFrom(side, false)) { + ConsumerNode tConsumerNode = new NodeGTBaseMetaTile(aNodeValue, tBaseTileEntity, side, aConsumers); + aConsumers.add(tConsumerNode); + return true; + } + + } else if (aTileEntity instanceof IEnergyConnected tTileEntity) { + if (tTileEntity.inputEnergyFrom(side, false)) { + ConsumerNode tConsumerNode = new NodeEnergyConnected(aNodeValue, tTileEntity, side, aConsumers); + aConsumers.add(tConsumerNode); + return true; + } + } else if (aTileEntity instanceof IEnergySink sink) { + // ic2 wants the tilentity next to it of that side not going to add a bunch of arguments just for ic2 + // crossborder checks to not load chuncks just to make sure + int dX = aTileEntity.xCoord + side.offsetX; + int dY = aTileEntity.yCoord + side.offsetY; + int dZ = aTileEntity.zCoord + side.offsetZ; + boolean crossesChuncks = dX >> 4 != aTileEntity.xCoord >> 4 || dZ >> 4 != aTileEntity.zCoord >> 4; + TileEntity tNextTo = null; + if (!crossesChuncks || !aTileEntity.getWorldObj() + .blockExists(dX, dY, dZ)) + tNextTo = aTileEntity.getWorldObj() + .getTileEntity(dX, dY, dZ); + + if (sink.acceptsEnergyFrom(tNextTo, side)) { + ConsumerNode tConsumerNode = new NodeEnergySink( + aNodeValue, + (IEnergySink) aTileEntity, + side, + aConsumers); + aConsumers.add(tConsumerNode); + return true; + } + } else if (GregTech_API.mOutputRF && aTileEntity instanceof IEnergyReceiver receiver) { + ConsumerNode tConsumerNode = new NodeEnergyReceiver(aNodeValue, receiver, side, aConsumers); + aConsumers.add(tConsumerNode); + return true; + } + return false; + } + + @Override + protected NodePath getNewPath(MetaPipeEntity[] aPipes) { + return new PowerNodePath(aPipes); + } + + // used to apply voltage on dead ends + @Override + protected Node getEmptyNode(int aNodeValue, ForgeDirection side, TileEntity aTileEntity, + ArrayList<ConsumerNode> aConsumers) { + ConsumerNode tNode = new EmptyPowerConsumer(aNodeValue, aTileEntity, side, aConsumers); + aConsumers.add(tNode); + return tNode; + } + + @Override + protected Node getPipeNode(int aNodeValue, ForgeDirection side, TileEntity aTileEntity, + ArrayList<ConsumerNode> aConsumers) { + return new PowerNode(aNodeValue, aTileEntity, aConsumers); + } +} diff --git a/src/main/java/gregtech/api/graphs/Lock.java b/src/main/java/gregtech/api/graphs/Lock.java new file mode 100644 index 0000000000..d3c8c49169 --- /dev/null +++ b/src/main/java/gregtech/api/graphs/Lock.java @@ -0,0 +1,38 @@ +package gregtech.api.graphs; + +import java.util.ArrayList; + +import net.minecraft.tileentity.TileEntity; + +public class Lock { + + protected ArrayList<TileEntity> tiles = new ArrayList<>(); + + public void addTileEntity(TileEntity tileEntity) { + int i = contains(tileEntity); + if (i == -1) { + tiles.add(tileEntity); + } + } + + public void removeTileEntity(TileEntity tileEntity) { + int i = contains(tileEntity); + if (i > -1) { + tiles.remove(i); + } + } + + public boolean isLocked() { + return !tiles.isEmpty(); + } + + // i want to check for the exact object not equals + protected int contains(TileEntity tileEntity) { + for (int i = 0; i < tiles.size(); i++) { + if (tiles.get(i) == tileEntity) { + return i; + } + } + return -1; + } +} diff --git a/src/main/java/gregtech/api/graphs/Node.java b/src/main/java/gregtech/api/graphs/Node.java new file mode 100644 index 0000000000..9afe009d3e --- /dev/null +++ b/src/main/java/gregtech/api/graphs/Node.java @@ -0,0 +1,40 @@ +package gregtech.api.graphs; + +import java.util.ArrayList; + +import net.minecraft.server.MinecraftServer; +import net.minecraft.tileentity.TileEntity; + +import gregtech.api.graphs.consumers.ConsumerNode; +import gregtech.api.graphs.paths.NodePath; + +// base Node class +public class Node { + + public Node(int aNodeValue, TileEntity aTileEntity, ArrayList<ConsumerNode> aConsumers) { + this.mNodeValue = aNodeValue; + this.mTileEntity = aTileEntity; + this.mConsumers = aConsumers; + mHighestNodeValue = aNodeValue; + // you don't want to generate map multiple times in the same tick + mCreationTime = MinecraftServer.getServer() + .getTickCounter(); + } + + public final TileEntity mTileEntity; + public Node[] mNeighbourNodes = new Node[6]; + public NodePath[] mNodePaths = new NodePath[6]; + public Lock[] locks = new Lock[6]; + public ReturnPair returnValues = new ReturnPair(); + public NodePath mSelfPath; + public ArrayList<ConsumerNode> mConsumers; + public int mCreationTime; + public int mNodeValue; + public int mHighestNodeValue; + + public static class ReturnPair { + + public NodePath mReturnPath; + public Lock returnLock; + } +} diff --git a/src/main/java/gregtech/api/graphs/NodeList.java b/src/main/java/gregtech/api/graphs/NodeList.java new file mode 100644 index 0000000000..899384b3d4 --- /dev/null +++ b/src/main/java/gregtech/api/graphs/NodeList.java @@ -0,0 +1,22 @@ +package gregtech.api.graphs; + +// keep track on which node is being looked for across the recursive functions +public class NodeList { + + Node[] mNodes; + int mCounter = 0; + + public NodeList(Node[] mNodes) { + this.mNodes = mNodes; + } + + Node getNextNode() { + if (++mCounter < mNodes.length) return mNodes[mCounter]; + else return null; + } + + Node getNode() { + if (mCounter < mNodes.length) return mNodes[mCounter]; + else return null; + } +} diff --git a/src/main/java/gregtech/api/graphs/PowerNode.java b/src/main/java/gregtech/api/graphs/PowerNode.java new file mode 100644 index 0000000000..75a8e8d73b --- /dev/null +++ b/src/main/java/gregtech/api/graphs/PowerNode.java @@ -0,0 +1,17 @@ +package gregtech.api.graphs; + +import java.util.ArrayList; + +import net.minecraft.tileentity.TileEntity; + +import gregtech.api.graphs.consumers.ConsumerNode; + +// base node for power networks +public class PowerNode extends Node { + + public boolean mHadVoltage = false; + + public PowerNode(int aNodeValue, TileEntity aTileEntity, ArrayList<ConsumerNode> aConsumers) { + super(aNodeValue, aTileEntity, aConsumers); + } +} diff --git a/src/main/java/gregtech/api/graphs/PowerNodes.java b/src/main/java/gregtech/api/graphs/PowerNodes.java new file mode 100644 index 0000000000..98d35e2971 --- /dev/null +++ b/src/main/java/gregtech/api/graphs/PowerNodes.java @@ -0,0 +1,182 @@ +package gregtech.api.graphs; + +import gregtech.api.graphs.consumers.ConsumerNode; +import gregtech.api.graphs.paths.PowerNodePath; + +/* + * look for and power node that need power how this works a node only contains nodes that has a higher value then it + * self except for 1 which is the return node this node also contains the highest known node value of its network this + * network only includes nodes that have a higher value then it self so it does not know the highest known value that + * the return node knows with these rules we can know for the target node to be in the network of a node, the target + * node must have a value no less than the node we are looking and no greater than the highest value that node knows + * this way we don't have to go over the entire network to look for it we also hold a list of all consumers so we can + * check before looking if that consumer actually needs power and only look for nodes that actually need power + */ +public class PowerNodes { + + // check if the looked for node is next to or get the next node that is closer to it + public static long powerNode(Node aCurrentNode, Node aPreviousNode, NodeList aConsumers, long aVoltage, + long aMaxAmps) { + long tAmpsUsed = 0; + ConsumerNode tConsumer = (ConsumerNode) aConsumers.getNode(); + int tLoopProtection = 0; + while (tConsumer != null) { + int tTargetNodeValue = tConsumer.mNodeValue; + // if the target node has a value less then the current node + if (tTargetNodeValue < aCurrentNode.mNodeValue || tTargetNodeValue > aCurrentNode.mHighestNodeValue) { + for (int j = 0; j < 6; j++) { + final Node tNextNode = aCurrentNode.mNeighbourNodes[j]; + if (tNextNode != null && tNextNode.mNodeValue < aCurrentNode.mNodeValue) { + if (tNextNode.mNodeValue == tConsumer.mNodeValue) { + tAmpsUsed += processNodeInject(aCurrentNode, tConsumer, j, aMaxAmps - tAmpsUsed, aVoltage); + tConsumer = (ConsumerNode) aConsumers.getNextNode(); + } else { + if (aPreviousNode == tNextNode) return tAmpsUsed; + tAmpsUsed += processNextNode( + aCurrentNode, + tNextNode, + aConsumers, + j, + aMaxAmps - tAmpsUsed, + aVoltage); + tConsumer = (ConsumerNode) aConsumers.getNode(); + } + break; + } + } + } else { + // if the target node has a node value greater then current node value + for (int side = 5; side > -1; side--) { + final Node tNextNode = aCurrentNode.mNeighbourNodes[side]; + if (tNextNode == null) continue; + if (tNextNode.mNodeValue > aCurrentNode.mNodeValue && tNextNode.mNodeValue < tTargetNodeValue) { + if (tNextNode == aPreviousNode) return tAmpsUsed; + tAmpsUsed += processNextNodeAbove( + aCurrentNode, + tNextNode, + aConsumers, + side, + aMaxAmps - tAmpsUsed, + aVoltage); + tConsumer = (ConsumerNode) aConsumers.getNode(); + break; + } else if (tNextNode.mNodeValue == tTargetNodeValue) { + tAmpsUsed += processNodeInject(aCurrentNode, tConsumer, side, aMaxAmps - tAmpsUsed, aVoltage); + tConsumer = (ConsumerNode) aConsumers.getNextNode(); + break; + } + } + } + if (aMaxAmps - tAmpsUsed <= 0) { + return tAmpsUsed; + } + if (tLoopProtection++ > 20) { + throw new NullPointerException("infinite loop in powering nodes "); + } + } + return tAmpsUsed; + } + + // checking if target node is next to it or has a higher value then current node value + // these functions are different to either go down or up the stack + protected static long powerNodeAbove(Node aCurrentNode, Node aPreviousNode, NodeList aConsumers, long aVoltage, + long aMaxAmps) { + long tAmpsUsed = 0; + int tLoopProtection = 0; + ConsumerNode tConsumer = (ConsumerNode) aConsumers.getNode(); + while (tConsumer != null) { + int tTargetNodeValue = tConsumer.mNodeValue; + if (tTargetNodeValue > aCurrentNode.mHighestNodeValue || tTargetNodeValue < aCurrentNode.mNodeValue) { + return tAmpsUsed; + } else { + for (int side = 5; side > -1; side--) { + final Node tNextNode = aCurrentNode.mNeighbourNodes[side]; + if (tNextNode == null) continue; + if (tNextNode.mNodeValue > aCurrentNode.mNodeValue && tNextNode.mNodeValue < tTargetNodeValue) { + if (tNextNode == aPreviousNode) return tAmpsUsed; + tAmpsUsed += processNextNodeAbove( + aCurrentNode, + tNextNode, + aConsumers, + side, + aMaxAmps - tAmpsUsed, + aVoltage); + tConsumer = (ConsumerNode) aConsumers.getNode(); + break; + } else if (tNextNode.mNodeValue == tTargetNodeValue) { + tAmpsUsed += processNodeInject(aCurrentNode, tConsumer, side, aMaxAmps - tAmpsUsed, aVoltage); + tConsumer = (ConsumerNode) aConsumers.getNextNode(); + break; + } + } + } + if (aMaxAmps - tAmpsUsed <= 0) { + return tAmpsUsed; + } + if (tLoopProtection++ > 20) { + throw new NullPointerException("infinite loop in powering nodes "); + } + } + return tAmpsUsed; + } + + protected static long processNextNode(Node aCurrentNode, Node aNextNode, NodeList aConsumers, int ordinalSide, + long aMaxAmps, long aVoltage) { + if (aCurrentNode.locks[ordinalSide].isLocked()) { + aConsumers.getNextNode(); + return 0; + } + final PowerNodePath tPath = (PowerNodePath) aCurrentNode.mNodePaths[ordinalSide]; + final PowerNodePath tSelfPath = (PowerNodePath) aCurrentNode.mSelfPath; + long tVoltLoss = 0; + if (tSelfPath != null) { + tVoltLoss += tSelfPath.getLoss(); + tSelfPath.applyVoltage(aVoltage, false); + } + tPath.applyVoltage(aVoltage - tVoltLoss, true); + tVoltLoss += tPath.getLoss(); + long tAmps = powerNode(aNextNode, aCurrentNode, aConsumers, aVoltage - tVoltLoss, aMaxAmps); + tPath.addAmps(tAmps); + if (tSelfPath != null) tSelfPath.addAmps(tAmps); + return tAmps; + } + + protected static long processNextNodeAbove(Node aCurrentNode, Node aNextNode, NodeList aConsumers, int ordinalSide, + long aMaxAmps, long aVoltage) { + if (aCurrentNode.locks[ordinalSide].isLocked()) { + aConsumers.getNextNode(); + return 0; + } + final PowerNodePath tPath = (PowerNodePath) aCurrentNode.mNodePaths[ordinalSide]; + final PowerNodePath tSelfPath = (PowerNodePath) aCurrentNode.mSelfPath; + long tVoltLoss = 0; + if (tSelfPath != null) { + tVoltLoss += tSelfPath.getLoss(); + tSelfPath.applyVoltage(aVoltage, false); + } + tPath.applyVoltage(aVoltage - tVoltLoss, true); + tVoltLoss += tPath.getLoss(); + long tAmps = powerNodeAbove(aNextNode, aCurrentNode, aConsumers, aVoltage - tVoltLoss, aMaxAmps); + tPath.addAmps(tAmps); + if (tSelfPath != null) tSelfPath.addAmps(tAmps); + return tAmps; + } + + protected static long processNodeInject(Node aCurrentNode, ConsumerNode aConsumer, int ordinalSide, long aMaxAmps, + long aVoltage) { + if (aCurrentNode.locks[ordinalSide].isLocked()) return 0; + final PowerNodePath tPath = (PowerNodePath) aCurrentNode.mNodePaths[ordinalSide]; + final PowerNodePath tSelfPath = (PowerNodePath) aCurrentNode.mSelfPath; + long tVoltLoss = 0; + if (tSelfPath != null) { + tVoltLoss += tSelfPath.getLoss(); + tSelfPath.applyVoltage(aVoltage, false); + } + tPath.applyVoltage(aVoltage - tVoltLoss, true); + tVoltLoss += tPath.getLoss(); + long tAmps = aConsumer.injectEnergy(aVoltage - tVoltLoss, aMaxAmps); + tPath.addAmps(tAmps); + if (tSelfPath != null) tSelfPath.addAmps(tAmps); + return tAmps; + } +} diff --git a/src/main/java/gregtech/api/graphs/consumers/ConsumerNode.java b/src/main/java/gregtech/api/graphs/consumers/ConsumerNode.java new file mode 100644 index 0000000000..392fe74a32 --- /dev/null +++ b/src/main/java/gregtech/api/graphs/consumers/ConsumerNode.java @@ -0,0 +1,30 @@ +package gregtech.api.graphs.consumers; + +import java.util.ArrayList; + +import net.minecraft.tileentity.TileEntity; +import net.minecraftforge.common.util.ForgeDirection; + +import gregtech.api.graphs.Node; + +/** + * A node attached to a {@code TileEntity} that can consume stuff from the network. + */ +public class ConsumerNode extends Node { + + public ForgeDirection mSide; + + public ConsumerNode(int aNodeValue, TileEntity aTileEntity, ForgeDirection side, + ArrayList<ConsumerNode> aConsumers) { + super(aNodeValue, aTileEntity, aConsumers); + this.mSide = side; + } + + public boolean needsEnergy() { + return !mTileEntity.isInvalid(); + } + + public int injectEnergy(long aVoltage, long aMaxAmps) { + return 0; + } +} diff --git a/src/main/java/gregtech/api/graphs/consumers/EmptyPowerConsumer.java b/src/main/java/gregtech/api/graphs/consumers/EmptyPowerConsumer.java new file mode 100644 index 0000000000..48cf330bdb --- /dev/null +++ b/src/main/java/gregtech/api/graphs/consumers/EmptyPowerConsumer.java @@ -0,0 +1,31 @@ +package gregtech.api.graphs.consumers; + +import java.util.ArrayList; + +import net.minecraft.tileentity.TileEntity; +import net.minecraftforge.common.util.ForgeDirection; + +import gregtech.api.graphs.paths.PowerNodePath; +import gregtech.api.metatileentity.BaseMetaPipeEntity; + +// this is here to apply voltage to dead ends +public class EmptyPowerConsumer extends ConsumerNode { + + public EmptyPowerConsumer(int aNodeValue, TileEntity aTileEntity, ForgeDirection side, + ArrayList<ConsumerNode> aConsumers) { + super(aNodeValue, aTileEntity, side, aConsumers); + } + + @Override + public boolean needsEnergy() { + return false; + } + + @Override + public int injectEnergy(long aVoltage, long aMaxAmps) { + BaseMetaPipeEntity tPipe = (BaseMetaPipeEntity) mTileEntity; + PowerNodePath tPath = (PowerNodePath) tPipe.getNodePath(); + tPath.applyVoltage(aVoltage, true); + return 0; + } +} diff --git a/src/main/java/gregtech/api/graphs/consumers/NodeEnergyConnected.java b/src/main/java/gregtech/api/graphs/consumers/NodeEnergyConnected.java new file mode 100644 index 0000000000..fb0a8cf287 --- /dev/null +++ b/src/main/java/gregtech/api/graphs/consumers/NodeEnergyConnected.java @@ -0,0 +1,21 @@ +package gregtech.api.graphs.consumers; + +import java.util.ArrayList; + +import net.minecraft.tileentity.TileEntity; +import net.minecraftforge.common.util.ForgeDirection; + +import gregtech.api.interfaces.tileentity.IEnergyConnected; + +public class NodeEnergyConnected extends ConsumerNode { + + public NodeEnergyConnected(int aNodeValue, IEnergyConnected aTileEntity, ForgeDirection side, + ArrayList<ConsumerNode> aConsumers) { + super(aNodeValue, (TileEntity) aTileEntity, side, aConsumers); + } + + @Override + public int injectEnergy(long aVoltage, long aMaxAmps) { + return (int) ((IEnergyConnected) mTileEntity).injectEnergyUnits(mSide, aVoltage, aMaxAmps); + } +} diff --git a/src/main/java/gregtech/api/graphs/consumers/NodeEnergyReceiver.java b/src/main/java/gregtech/api/graphs/consumers/NodeEnergyReceiver.java new file mode 100644 index 0000000000..4f35922029 --- /dev/null +++ b/src/main/java/gregtech/api/graphs/consumers/NodeEnergyReceiver.java @@ -0,0 +1,68 @@ +package gregtech.api.graphs.consumers; + +import java.util.ArrayList; + +import net.minecraft.init.Blocks; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.world.World; +import net.minecraftforge.common.util.ForgeDirection; + +import cofh.api.energy.IEnergyReceiver; +import gregtech.GT_Mod; +import gregtech.api.GregTech_API; +import gregtech.api.enums.GT_Values; +import gregtech.api.enums.SoundResource; +import gregtech.api.util.GT_Utility; +import gregtech.api.util.WorldSpawnedEventBuilder; +import gregtech.common.GT_Pollution; + +// consumer for RF machines +public class NodeEnergyReceiver extends ConsumerNode { + + int mRestRF = 0; + + public NodeEnergyReceiver(int aNodeValue, IEnergyReceiver aTileEntity, ForgeDirection side, + ArrayList<ConsumerNode> aConsumers) { + super(aNodeValue, (TileEntity) aTileEntity, side, aConsumers); + } + + @Override + public int injectEnergy(long aVoltage, long aMaxAmps) { + ForgeDirection tDirection = mSide; + int rfOut = GT_Utility.safeInt(aVoltage * GregTech_API.mEUtoRF / 100); + int ampsUsed = 0; + if (mRestRF < rfOut) { + mRestRF += rfOut; + ampsUsed = 1; + } + if (((IEnergyReceiver) mTileEntity).receiveEnergy(tDirection, mRestRF, true) > 0) { + int consumed = ((IEnergyReceiver) mTileEntity).receiveEnergy(tDirection, mRestRF, false); + mRestRF -= consumed; + return ampsUsed; + } + if (GregTech_API.mRFExplosions && GregTech_API.sMachineExplosions + && ((IEnergyReceiver) mTileEntity).getMaxEnergyStored(tDirection) < rfOut * 600L) { + explode(rfOut); + } + return 0; + } + + // copied from IEnergyConnected + private void explode(int aRfOut) { + if (aRfOut > 32L * GregTech_API.mEUtoRF / 100L) { + float tStrength = GT_Values.getExplosionPowerForVoltage(aRfOut); + int tX = mTileEntity.xCoord, tY = mTileEntity.yCoord, tZ = mTileEntity.zCoord; + World tWorld = mTileEntity.getWorldObj(); + GT_Utility.sendSoundToPlayers(tWorld, SoundResource.IC2_MACHINES_MACHINE_OVERLOAD, 1.0F, -1, tX, tY, tZ); + tWorld.setBlock(tX, tY, tZ, Blocks.air); + if (GregTech_API.sMachineExplosions) if (GT_Mod.gregtechproxy.mPollution) GT_Pollution + .addPollution(tWorld.getChunkFromBlockCoords(tX, tZ), GT_Mod.gregtechproxy.mPollutionOnExplosion); + + new WorldSpawnedEventBuilder.ExplosionEffectEventBuilder().setStrength(tStrength) + .setSmoking(true) + .setPosition(tX + 0.5, tY + 0.5, tZ + 0.5) + .setWorld(tWorld) + .run(); + } + } +} diff --git a/src/main/java/gregtech/api/graphs/consumers/NodeEnergySink.java b/src/main/java/gregtech/api/graphs/consumers/NodeEnergySink.java new file mode 100644 index 0000000000..44fb88e5e8 --- /dev/null +++ b/src/main/java/gregtech/api/graphs/consumers/NodeEnergySink.java @@ -0,0 +1,30 @@ +package gregtech.api.graphs.consumers; + +import java.util.ArrayList; + +import net.minecraft.tileentity.TileEntity; +import net.minecraftforge.common.util.ForgeDirection; + +import ic2.api.energy.tile.IEnergySink; + +// consumer for IC2 machines +public class NodeEnergySink extends ConsumerNode { + + public NodeEnergySink(int nodeValue, IEnergySink tileEntity, ForgeDirection side, + ArrayList<ConsumerNode> consumers) { + super(nodeValue, (TileEntity) tileEntity, side, consumers); + } + + @Override + public boolean needsEnergy() { + return super.needsEnergy() && ((IEnergySink) mTileEntity).getDemandedEnergy() > 0; + } + + @Override + public int injectEnergy(long aVoltage, long aMaxAmps) { + int tUsedAmps = 0; + while (aMaxAmps > tUsedAmps && ((IEnergySink) mTileEntity).getDemandedEnergy() > 0 + && ((IEnergySink) mTileEntity).injectEnergy(mSide, aVoltage, aVoltage) < aVoltage) tUsedAmps++; + return tUsedAmps; + } +} diff --git a/src/main/java/gregtech/api/graphs/consumers/NodeGTBaseMetaTile.java b/src/main/java/gregtech/api/graphs/consumers/NodeGTBaseMetaTile.java new file mode 100644 index 0000000000..e8d8304eb3 --- /dev/null +++ b/src/main/java/gregtech/api/graphs/consumers/NodeGTBaseMetaTile.java @@ -0,0 +1,28 @@ +package gregtech.api.graphs.consumers; + +import java.util.ArrayList; + +import net.minecraftforge.common.util.ForgeDirection; + +import gregtech.api.interfaces.tileentity.IEnergyConnected; +import gregtech.api.metatileentity.BaseMetaTileEntity; + +// consumer for gt machines +public class NodeGTBaseMetaTile extends ConsumerNode { + + public NodeGTBaseMetaTile(int aNodeValue, BaseMetaTileEntity aTileEntity, ForgeDirection side, + ArrayList<ConsumerNode> aConsumers) { + super(aNodeValue, aTileEntity, side, aConsumers); + } + + @Override + public int injectEnergy(long aVoltage, long aMaxAmps) { + return (int) ((IEnergyConnected) mTileEntity).injectEnergyUnits(mSide, aVoltage, aMaxAmps); + } + + @Override + public boolean needsEnergy() { + BaseMetaTileEntity tTileEntity = (BaseMetaTileEntity) mTileEntity; + return super.needsEnergy() && tTileEntity.getStoredEU() < tTileEntity.getEUCapacity(); + } +} diff --git a/src/main/java/gregtech/api/graphs/paths/NodePath.java b/src/main/java/gregtech/api/graphs/paths/NodePath.java new file mode 100644 index 0000000000..0e852bd484 --- /dev/null +++ b/src/main/java/gregtech/api/graphs/paths/NodePath.java @@ -0,0 +1,42 @@ +package gregtech.api.graphs.paths; + +import gregtech.api.graphs.Lock; +import gregtech.api.metatileentity.BaseMetaPipeEntity; +import gregtech.api.metatileentity.MetaPipeEntity; + +// to contain all info about the path between nodes +public class NodePath { + + protected MetaPipeEntity[] mPipes; + public Lock lock = new Lock(); + + public NodePath(MetaPipeEntity[] aCables) { + this.mPipes = aCables; + processPipes(); + } + + protected void processPipes() { + for (MetaPipeEntity tPipe : mPipes) { + BaseMetaPipeEntity basePipe = (BaseMetaPipeEntity) tPipe.getBaseMetaTileEntity(); + basePipe.setNodePath(this); + } + } + + public void clearPath() { + for (MetaPipeEntity mPipe : mPipes) { + BaseMetaPipeEntity tBasePipe = (BaseMetaPipeEntity) mPipe.getBaseMetaTileEntity(); + if (tBasePipe != null) { + tBasePipe.setNodePath(null); + } + } + } + + public void reloadLocks() { + for (MetaPipeEntity pipe : mPipes) { + BaseMetaPipeEntity basePipe = (BaseMetaPipeEntity) pipe.getBaseMetaTileEntity(); + if (basePipe != null) { + basePipe.reloadLocks(); + } + } + } +} diff --git a/src/main/java/gregtech/api/graphs/paths/PowerNodePath.java b/src/main/java/gregtech/api/graphs/paths/PowerNodePath.java new file mode 100644 index 0000000000..8a869c333e --- /dev/null +++ b/src/main/java/gregtech/api/graphs/paths/PowerNodePath.java @@ -0,0 +1,153 @@ +package gregtech.api.graphs.paths; + +import net.minecraft.server.MinecraftServer; + +import gregtech.api.enums.TickTime; +import gregtech.api.metatileentity.BaseMetaPipeEntity; +import gregtech.api.metatileentity.MetaPipeEntity; +import gregtech.api.metatileentity.implementations.GT_MetaPipeEntity_Cable; +import gregtech.api.util.AveragePerTickCounter; + +// path for cables +// all calculations like amp and voltage happens here +public class PowerNodePath extends NodePath { + + long mMaxAmps; + long mAmps = 0; + long mLoss; + long mVoltage = 0; + long mMaxVoltage; + int mTick = 0; + boolean mCountUp = true; + + private AveragePerTickCounter avgAmperageCounter = new AveragePerTickCounter(TickTime.SECOND); + private AveragePerTickCounter avgVoltageCounter = new AveragePerTickCounter(TickTime.SECOND); + + public PowerNodePath(MetaPipeEntity[] aCables) { + super(aCables); + } + + public long getLoss() { + return mLoss; + } + + public void applyVoltage(long aVoltage, boolean aCountUp) { + + avgVoltageCounter.addValue(Math.max(aVoltage - mLoss, 0)); + + int tNewTime = MinecraftServer.getServer() + .getTickCounter(); + if (mTick != tNewTime) { + reset(tNewTime - mTick); + mTick = tNewTime; + this.mVoltage = aVoltage; + this.mCountUp = aCountUp; + } else if (this.mCountUp != aCountUp && (aVoltage - mLoss) > this.mVoltage || aVoltage > this.mVoltage) { + this.mCountUp = aCountUp; + this.mVoltage = aVoltage; + } + if (aVoltage > mMaxVoltage) { + lock.addTileEntity(null); + for (MetaPipeEntity tCable : mPipes) { + if (((GT_MetaPipeEntity_Cable) tCable).mVoltage < this.mVoltage) { + BaseMetaPipeEntity tBaseCable = (BaseMetaPipeEntity) tCable.getBaseMetaTileEntity(); + if (tBaseCable != null) { + tBaseCable.setToFire(); + } + } + } + } + } + + private void reset(int aTimePassed) { + if (aTimePassed < 0 || aTimePassed > 100) { + mAmps = 0; + return; + } + mAmps = Math.max(0, mAmps - (mMaxAmps * aTimePassed)); + } + + public void addAmps(long aAmps) { + + avgAmperageCounter.addValue(aAmps); + + this.mAmps += aAmps; + if (this.mAmps > mMaxAmps * 40) { + lock.addTileEntity(null); + for (MetaPipeEntity tCable : mPipes) { + if (((GT_MetaPipeEntity_Cable) tCable).mAmperage * 40 < this.mAmps) { + BaseMetaPipeEntity tBaseCable = (BaseMetaPipeEntity) tCable.getBaseMetaTileEntity(); + if (tBaseCable != null) { + tBaseCable.setToFire(); + } + } + } + } + } + + // if no amps pass through for more than 0.5 second reduce them to minimize wrong results + // but still allow the player to see if activity is happening + @Deprecated + public long getAmps() { + int tTime = MinecraftServer.getServer() + .getTickCounter() - 10; + if (mTick < tTime) { + reset(tTime - mTick); + mTick = tTime; + } + return mAmps; + } + + @Deprecated + public long getVoltage(MetaPipeEntity aCable) { + int tLoss = 0; + if (mCountUp) { + for (MetaPipeEntity mPipe : mPipes) { + GT_MetaPipeEntity_Cable tCable = (GT_MetaPipeEntity_Cable) mPipe; + tLoss += tCable.mCableLossPerMeter; + if (aCable == tCable) { + return Math.max(mVoltage - tLoss, 0); + } + } + } else { + for (int i = mPipes.length - 1; i >= 0; i--) { + GT_MetaPipeEntity_Cable tCable = (GT_MetaPipeEntity_Cable) mPipes[i]; + tLoss += tCable.mCableLossPerMeter; + if (aCable == tCable) { + return Math.max(mVoltage - tLoss, 0); + } + } + } + return -1; + } + + public long getAmperage() { + return avgAmperageCounter.getLast(); + } + + public double getAvgAmperage() { + return avgAmperageCounter.getAverage(); + } + + public long getVoltage() { + return avgVoltageCounter.getLast(); + } + + public double getAvgVoltage() { + return avgVoltageCounter.getAverage(); + } + + @Override + protected void processPipes() { + super.processPipes(); + mMaxAmps = Integer.MAX_VALUE; + mMaxVoltage = Integer.MAX_VALUE; + for (MetaPipeEntity tCable : mPipes) { + if (tCable instanceof GT_MetaPipeEntity_Cable) { + mMaxAmps = Math.min(((GT_MetaPipeEntity_Cable) tCable).mAmperage, mMaxAmps); + mLoss += ((GT_MetaPipeEntity_Cable) tCable).mCableLossPerMeter; + mMaxVoltage = Math.min(((GT_MetaPipeEntity_Cable) tCable).mVoltage, mMaxVoltage); + } + } + } +} diff --git a/src/main/java/gregtech/api/gui/GT_Container.java b/src/main/java/gregtech/api/gui/GT_Container.java new file mode 100644 index 0000000000..851e1f6461 --- /dev/null +++ b/src/main/java/gregtech/api/gui/GT_Container.java @@ -0,0 +1,740 @@ +package gregtech.api.gui; + +import java.util.List; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.inventory.Container; +import net.minecraft.inventory.ICrafting; +import net.minecraft.inventory.IInventory; +import net.minecraft.inventory.Slot; +import net.minecraft.item.ItemStack; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.IFluidContainerItem; + +import gregtech.api.interfaces.IFluidAccess; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.util.GT_Log; +import gregtech.api.util.GT_Utility; + +/** + * The main Container class. It is used for all GregTech GUIs. + * <p> + * Never include this file in your mod - it will break your modpack. + */ +public class GT_Container extends Container { + + public IGregTechTileEntity mTileEntity; + public InventoryPlayer mPlayerInventory; + + public GT_Container(InventoryPlayer aPlayerInventory, IGregTechTileEntity aTileEntityInventory) { + + mTileEntity = aTileEntityInventory; + mPlayerInventory = aPlayerInventory; + mTileEntity.openInventory(); + } + + /** + * To add the Slots to your GUI + */ + public void addSlots(InventoryPlayer aPlayerInventory) { + // + } + + /** + * Amount of regular Slots in the GUI (so, non-HoloSlots) + */ + public int getSlotCount() { + return 0; + } + + /** + * Amount of ALL Slots in the GUI including HoloSlots and ArmorSlots, but excluding regular Player Slots + */ + protected final int getAllSlotCount() { + if (inventorySlots != null) { + if (doesBindPlayerInventory()) return inventorySlots.size() - 36; + return inventorySlots.size(); + } + return getSlotCount(); + } + + /** + * Start-Index of the usable Slots (the first non-HoloSlot) + */ + public int getSlotStartIndex() { + return 0; + } + + public int getShiftClickStartIndex() { + return getSlotStartIndex(); + } + + /** + * Amount of Slots in the GUI the player can Shift-Click into. Uses also getSlotStartIndex + */ + public int getShiftClickSlotCount() { + return 0; + } + + /** + * Is Player-Inventory visible? + */ + public boolean doesBindPlayerInventory() { + return true; + } + + /** + * Override this Function with something like "return mTileEntity.isUseableByPlayer(aPlayer);" + */ + @Override + public boolean canInteractWith(EntityPlayer aPlayer) { + return false; + } + + protected void bindPlayerInventory(InventoryPlayer aInventoryPlayer) { + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 9; j++) { + addSlotToContainer(new Slot(aInventoryPlayer, j + i * 9 + 9, 8 + j * 18, 84 + i * 18)); + } + } + + for (int i = 0; i < 9; i++) { + addSlotToContainer(new Slot(aInventoryPlayer, i, 8 + i * 18, 142)); + } + } + + @Override + public ItemStack slotClick(int aSlotIndex, int aMouseclick, int aShifthold, EntityPlayer aPlayer) { + mTileEntity.markDirty(); + + if (aSlotIndex >= 0) { + if (inventorySlots.get(aSlotIndex) == null || inventorySlots.get(aSlotIndex) instanceof GT_Slot_Holo) + return null; + if (!(inventorySlots.get(aSlotIndex) instanceof GT_Slot_Armor)) if (aSlotIndex < getAllSlotCount()) + if (aSlotIndex < getSlotStartIndex() || aSlotIndex >= getSlotStartIndex() + getSlotCount()) return null; + } + + try { + return super.slotClick(aSlotIndex, aMouseclick, aShifthold, aPlayer); + } catch (Throwable e) { + e.printStackTrace(GT_Log.err); + } + + // It looks like the rest of this code should ideally never be + // called, and might in fact never be called. + + ItemStack rStack = null; + InventoryPlayer aPlayerInventory = aPlayer.inventory; + Slot aSlot; + ItemStack tTempStack; + int tTempStackSize; + ItemStack aHoldStack; + + if ((aShifthold == 0 || aShifthold == 1) && (aMouseclick == 0 || aMouseclick == 1)) { + if (aSlotIndex == -999) { + if (aPlayerInventory.getItemStack() != null) { + if (aMouseclick == 0) { + aPlayer.dropPlayerItemWithRandomChoice(aPlayerInventory.getItemStack(), true); + aPlayerInventory.setItemStack(null); + } + if (aMouseclick == 1) { + aPlayer.dropPlayerItemWithRandomChoice( + aPlayerInventory.getItemStack() + .splitStack(1), + true); + if (aPlayerInventory.getItemStack().stackSize == 0) { + aPlayerInventory.setItemStack(null); + } + } + } + } else if (aShifthold == 1) { + aSlot = this.inventorySlots.get(aSlotIndex); + if (aSlot != null && aSlot.canTakeStack(aPlayer)) { + tTempStack = this.transferStackInSlot(aPlayer, aSlotIndex); + if (tTempStack != null) { + rStack = GT_Utility.copyOrNull(tTempStack); + if (aSlot.getStack() != null && aSlot.getStack() + .getItem() == tTempStack.getItem()) { + slotClick(aSlotIndex, aMouseclick, aShifthold, aPlayer); + } + } + } + } else { + if (aSlotIndex < 0) { + return null; + } + aSlot = this.inventorySlots.get(aSlotIndex); + if (aSlot != null) { + tTempStack = aSlot.getStack(); + ItemStack mouseStack = aPlayerInventory.getItemStack(); + if (tTempStack != null) { + rStack = GT_Utility.copyOrNull(tTempStack); + } + if (tTempStack == null) { + if (mouseStack != null && aSlot.isItemValid(mouseStack)) { + tTempStackSize = aMouseclick == 0 ? mouseStack.stackSize : 1; + if (tTempStackSize > aSlot.getSlotStackLimit()) { + tTempStackSize = aSlot.getSlotStackLimit(); + } + aSlot.putStack(mouseStack.splitStack(tTempStackSize)); + + if (mouseStack.stackSize == 0) { + aPlayerInventory.setItemStack(null); + } + } + } else if (aSlot.canTakeStack(aPlayer)) { + if (mouseStack == null) { + tTempStackSize = aMouseclick == 0 ? tTempStack.stackSize : (tTempStack.stackSize + 1) / 2; + aHoldStack = aSlot.decrStackSize(tTempStackSize); + aPlayerInventory.setItemStack(aHoldStack); + if (tTempStack.stackSize == 0) { + aSlot.putStack(null); + } + aSlot.onPickupFromSlot(aPlayer, aPlayerInventory.getItemStack()); + } else if (aSlot.isItemValid(mouseStack)) { + if (tTempStack.getItem() == mouseStack.getItem() + && tTempStack.getItemDamage() == mouseStack.getItemDamage() + && ItemStack.areItemStackTagsEqual(tTempStack, mouseStack)) { + tTempStackSize = aMouseclick == 0 ? mouseStack.stackSize : 1; + if (tTempStackSize > aSlot.getSlotStackLimit() - tTempStack.stackSize) { + tTempStackSize = aSlot.getSlotStackLimit() - tTempStack.stackSize; + } + if (tTempStackSize > mouseStack.getMaxStackSize() - tTempStack.stackSize) { + tTempStackSize = mouseStack.getMaxStackSize() - tTempStack.stackSize; + } + mouseStack.splitStack(tTempStackSize); + if (mouseStack.stackSize == 0) { + aPlayerInventory.setItemStack(null); + } + tTempStack.stackSize += tTempStackSize; + } else if (mouseStack.stackSize <= aSlot.getSlotStackLimit()) { + aSlot.putStack(mouseStack); + aPlayerInventory.setItemStack(tTempStack); + } + } else if (tTempStack.getItem() == mouseStack.getItem() && mouseStack.getMaxStackSize() > 1 + && (!tTempStack.getHasSubtypes() + || tTempStack.getItemDamage() == mouseStack.getItemDamage()) + && ItemStack.areItemStackTagsEqual(tTempStack, mouseStack)) { + tTempStackSize = tTempStack.stackSize; + + if (tTempStackSize > 0 + && tTempStackSize + mouseStack.stackSize <= mouseStack.getMaxStackSize()) { + mouseStack.stackSize += tTempStackSize; + tTempStack = aSlot.decrStackSize(tTempStackSize); + + if (tTempStack.stackSize == 0) { + aSlot.putStack(null); + } + + aSlot.onPickupFromSlot(aPlayer, aPlayerInventory.getItemStack()); + } + } + } + aSlot.onSlotChanged(); + } + } + // Did the player try to swap a slot with his hotbar using a + // number key from 1 to 9 + // aMouseclick == 0 means number 1, aMouseclick == 8 means number 9 + } else if (aShifthold == 2 && aMouseclick >= 0 && aMouseclick < 9) { + aSlot = this.inventorySlots.get(aSlotIndex); + + if (aSlot.canTakeStack(aPlayer)) { + // get the stack at the specified hotbar slot. + tTempStack = aPlayerInventory.getStackInSlot(aMouseclick); + boolean canSwap = tTempStack == null + || aSlot.inventory == aPlayerInventory && aSlot.isItemValid(tTempStack); + tTempStackSize = -1; + + if (!canSwap) { + tTempStackSize = aPlayerInventory.getFirstEmptyStack(); + canSwap = tTempStackSize > -1; + } + + if (canSwap && aSlot.getHasStack()) { + aHoldStack = aSlot.getStack(); + aPlayerInventory.setInventorySlotContents(aMouseclick, aHoldStack); + + if (tTempStack != null && (aSlot.inventory != aPlayerInventory || !aSlot.isItemValid(tTempStack))) { + if (tTempStackSize > -1) { + aPlayerInventory.addItemStackToInventory(tTempStack); + aSlot.decrStackSize(aHoldStack.stackSize); + aSlot.putStack(null); + aSlot.onPickupFromSlot(aPlayer, aHoldStack); + } + } else { + aSlot.decrStackSize(aHoldStack.stackSize); + aSlot.putStack(tTempStack); + aSlot.onPickupFromSlot(aPlayer, aHoldStack); + } + } else if (tTempStack != null && !aSlot.getHasStack() && aSlot.isItemValid(tTempStack)) { + aPlayerInventory.setInventorySlotContents(aMouseclick, null); + aSlot.putStack(tTempStack); + } + } + } else if (aShifthold == 3 && aPlayer.capabilities.isCreativeMode + && aPlayerInventory.getItemStack() == null + && aSlotIndex >= 0) { + aSlot = this.inventorySlots.get(aSlotIndex); + if (aSlot != null && aSlot.getHasStack()) { + tTempStack = GT_Utility.copyOrNull(aSlot.getStack()); + tTempStack.stackSize = tTempStack.getMaxStackSize(); + aPlayerInventory.setItemStack(tTempStack); + } + } + return rStack; + } + + @Override + public ItemStack transferStackInSlot(EntityPlayer aPlayer, int aSlotIndex) { + ItemStack stack = null; + Slot slotObject = inventorySlots.get(aSlotIndex); + + mTileEntity.markDirty(); + + // null checks and checks if the item can be stacked (maxStackSize > 1) + if (getSlotCount() > 0 && slotObject != null + && slotObject.getHasStack() + && !(slotObject instanceof GT_Slot_Holo)) { + ItemStack stackInSlot = slotObject.getStack(); + stack = GT_Utility.copyOrNull(stackInSlot); + + // TileEntity -> Player + if (aSlotIndex < getAllSlotCount()) { + if (doesBindPlayerInventory()) + if (!mergeItemStack(stackInSlot, getAllSlotCount(), getAllSlotCount() + 36, true)) { + return null; + } + // Player -> TileEntity + } else if (!mergeItemStack( + stackInSlot, + getShiftClickStartIndex(), + getShiftClickStartIndex() + getShiftClickSlotCount(), + false)) { + return null; + } + + if (stackInSlot.stackSize == 0) { + slotObject.putStack(null); + } else { + slotObject.onSlotChanged(); + } + } + return stack; + } + + /** + * merges provided ItemStack with the first avaliable one in the container/player inventory + */ + @Override + protected boolean mergeItemStack(ItemStack aStack, int aStartIndex, int aSlotCount, boolean reverseOrder) { + boolean transferredStack = false; + int slotIndex = aStartIndex; + + mTileEntity.markDirty(); + + if (reverseOrder) { + slotIndex = aSlotCount - 1; + } + + Slot slot; + ItemStack itemStack; + + if (aStack.isStackable()) { + while (aStack.stackSize > 0 + && (!reverseOrder && slotIndex < aSlotCount || reverseOrder && slotIndex >= aStartIndex)) { + slot = this.inventorySlots.get(slotIndex); + itemStack = slot.getStack(); + if (!(slot instanceof GT_Slot_Holo) && !(slot instanceof GT_Slot_Output) + && slot.isItemValid(aStack) + && itemStack != null + && itemStack.getItem() == aStack.getItem() + && (!aStack.getHasSubtypes() || aStack.getItemDamage() == itemStack.getItemDamage()) + && ItemStack.areItemStackTagsEqual(aStack, itemStack)) { + int combinedStackSize = itemStack.stackSize + aStack.stackSize; + if (itemStack.stackSize < mTileEntity.getInventoryStackLimit()) { + if (combinedStackSize <= aStack.getMaxStackSize()) { + aStack.stackSize = 0; + itemStack.stackSize = combinedStackSize; + slot.onSlotChanged(); + transferredStack = true; + } else if (itemStack.stackSize < aStack.getMaxStackSize()) { + aStack.stackSize -= aStack.getMaxStackSize() - itemStack.stackSize; + itemStack.stackSize = aStack.getMaxStackSize(); + slot.onSlotChanged(); + transferredStack = true; + } + } + } + + if (reverseOrder) { + --slotIndex; + } else { + ++slotIndex; + } + } + } + if (aStack.stackSize > 0) { + if (reverseOrder) { + slotIndex = aSlotCount - 1; + } else { + slotIndex = aStartIndex; + } + + while (!reverseOrder && slotIndex < aSlotCount || reverseOrder && slotIndex >= aStartIndex) { + slot = this.inventorySlots.get(slotIndex); + itemStack = slot.getStack(); + + if (slot.isItemValid(aStack) && itemStack == null) { + int quantityToTransfer = Math.min(aStack.stackSize, mTileEntity.getInventoryStackLimit()); + slot.putStack(GT_Utility.copyAmount(quantityToTransfer, aStack)); + slot.onSlotChanged(); + aStack.stackSize -= quantityToTransfer; + transferredStack = true; + break; + } + + if (reverseOrder) { + --slotIndex; + } else { + ++slotIndex; + } + } + } + + return transferredStack; + } + + @Override + protected Slot addSlotToContainer(Slot slot) { + try { + return super.addSlotToContainer(slot); + } catch (Throwable e) { + e.printStackTrace(GT_Log.err); + } + return slot; + } + + @Override + public void addCraftingToCrafters(ICrafting player) { + try { + super.addCraftingToCrafters(player); + } catch (Throwable e) { + e.printStackTrace(GT_Log.err); + } + } + + @Override + public List<ItemStack> getInventory() { + try { + return super.getInventory(); + } catch (Throwable e) { + e.printStackTrace(GT_Log.err); + } + return null; + } + + @Override + public void removeCraftingFromCrafters(ICrafting player) { + try { + super.removeCraftingFromCrafters(player); + } catch (Throwable e) { + e.printStackTrace(GT_Log.err); + } + } + + @Override + public void detectAndSendChanges() { + try { + super.detectAndSendChanges(); + } catch (Throwable e) { + e.printStackTrace(GT_Log.err); + } + } + + @Override + public boolean enchantItem(EntityPlayer player, int slotIndex) { + try { + return super.enchantItem(player, slotIndex); + } catch (Throwable e) { + e.printStackTrace(GT_Log.err); + } + return false; + } + + @Override + public Slot getSlotFromInventory(IInventory inventory, int slotIndex) { + try { + return super.getSlotFromInventory(inventory, slotIndex); + } catch (Throwable e) { + e.printStackTrace(GT_Log.err); + } + return null; + } + + @Override + public Slot getSlot(int slotIndex) { + try { + if (this.inventorySlots.size() > slotIndex) return super.getSlot(slotIndex); + } catch (Throwable e) { + e.printStackTrace(GT_Log.err); + } + return null; + } + + @Override + public boolean func_94530_a(ItemStack itemStack, Slot slot) { + try { + return super.func_94530_a(itemStack, slot); + } catch (Throwable e) { + e.printStackTrace(GT_Log.err); + } + return true; + } + + @Override + protected void retrySlotClick(int slotIndex, int mouseButton, boolean aShifthold, EntityPlayer player) { + try { + super.retrySlotClick(slotIndex, mouseButton, aShifthold, player); + } catch (Throwable e) { + e.printStackTrace(GT_Log.err); + } + } + + @Override + public void onContainerClosed(EntityPlayer player) { + try { + super.onContainerClosed(player); + mTileEntity.closeInventory(); + } catch (Throwable e) { + e.printStackTrace(GT_Log.err); + } + } + + @Override + public void onCraftMatrixChanged(IInventory inventory) { + try { + super.onCraftMatrixChanged(inventory); + } catch (Throwable e) { + e.printStackTrace(GT_Log.err); + } + } + + @Override + public void putStackInSlot(int slotIndex, ItemStack itemStack) { + try { + super.putStackInSlot(slotIndex, itemStack); + } catch (Throwable e) { + e.printStackTrace(GT_Log.err); + } + } + + @Override + public void putStacksInSlots(ItemStack[] itemStacks) { + try { + super.putStacksInSlots(itemStacks); + } catch (Throwable e) { + e.printStackTrace(GT_Log.err); + } + } + + @Override + public void updateProgressBar(int id, int value) { + try { + super.updateProgressBar(id, value); + } catch (Throwable e) { + e.printStackTrace(GT_Log.err); + } + } + + @Override + public short getNextTransactionID(InventoryPlayer inventoryPlayer) { + try { + return super.getNextTransactionID(inventoryPlayer); + } catch (Throwable e) { + e.printStackTrace(GT_Log.err); + } + return 0; + } + + @Override + public boolean isPlayerNotUsingContainer(EntityPlayer player) { + try { + return super.isPlayerNotUsingContainer(player); + } catch (Throwable e) { + e.printStackTrace(GT_Log.err); + } + return true; + } + + @Override + public void setPlayerIsPresent(EntityPlayer player, boolean value) { + try { + super.setPlayerIsPresent(player, value); + } catch (Throwable e) { + e.printStackTrace(GT_Log.err); + } + } + + @Override + protected void func_94533_d() { + try { + super.func_94533_d(); + } catch (Throwable e) { + e.printStackTrace(GT_Log.err); + } + } + + @Override + public boolean canDragIntoSlot(Slot slot) { + try { + return super.canDragIntoSlot(slot); + } catch (Throwable e) { + e.printStackTrace(GT_Log.err); + } + return true; + } + + protected static ItemStack handleFluidSlotClick(IFluidAccess aFluidAccess, EntityPlayer aPlayer, + boolean aProcessFullStack, boolean aCanDrain, boolean aCanFill) { + ItemStack tStackHeld = aPlayer.inventory.getItemStack(); + ItemStack tStackSizedOne = GT_Utility.copyAmount(1, tStackHeld); + if (tStackSizedOne == null || tStackHeld.stackSize == 0) return null; + FluidStack tInputFluid = aFluidAccess.get(); + FluidStack tFluidHeld = GT_Utility.getFluidForFilledItem(tStackSizedOne, true); + if (tFluidHeld != null && tFluidHeld.amount <= 0) tFluidHeld = null; + if (tInputFluid == null) { + // tank empty, consider fill only from now on + if (!aCanFill) + // cannot fill and nothing to take, bail out + return null; + if (tFluidHeld == null) + // no fluid to fill + return null; + return fillFluid(aFluidAccess, aPlayer, tFluidHeld, aProcessFullStack); + } + // tank not empty, both action possible + if (tFluidHeld != null && tInputFluid.amount < aFluidAccess.getCapacity()) { + // both nonnull and have space left for filling. + if (aCanFill) + // actually both pickup and fill is reasonable, but I'll go with fill here + return fillFluid(aFluidAccess, aPlayer, tFluidHeld, aProcessFullStack); + if (!aCanDrain) + // cannot take AND cannot fill, why make this call then? + return null; + // the slot does not allow filling, so try take some + return drainFluid(aFluidAccess, aPlayer, aProcessFullStack); + } else { + // cannot fill and there is something to take + if (!aCanDrain) + // but the slot does not allow taking, so bail out + return null; + return drainFluid(aFluidAccess, aPlayer, aProcessFullStack); + } + } + + protected static ItemStack drainFluid(IFluidAccess aFluidAccess, EntityPlayer aPlayer, boolean aProcessFullStack) { + FluidStack tTankStack = aFluidAccess.get(); + if (tTankStack == null) return null; + ItemStack tStackHeld = aPlayer.inventory.getItemStack(); + ItemStack tStackSizedOne = GT_Utility.copyAmount(1, tStackHeld); + if (tStackSizedOne == null || tStackHeld.stackSize == 0) return null; + int tOriginalFluidAmount = tTankStack.amount; + ItemStack tFilledContainer = GT_Utility.fillFluidContainer(tTankStack, tStackSizedOne, true, false); + if (tFilledContainer == null && tStackSizedOne.getItem() instanceof IFluidContainerItem tContainerItem) { + int tFilledAmount = tContainerItem.fill(tStackSizedOne, tTankStack, true); + if (tFilledAmount > 0) { + tFilledContainer = tStackSizedOne; + tTankStack.amount -= tFilledAmount; + } + } + if (tFilledContainer != null) { + if (aProcessFullStack) { + int tFilledAmount = tOriginalFluidAmount - tTankStack.amount; + /* + * work out how many more items we can fill one cell is already used, so account for that the round down + * behavior will left over a fraction of a cell worth of fluid the user then get to decide what to do + * with it it will not be too fancy if it spills out partially filled cells + */ + int tAdditionalParallel = Math.min(tStackHeld.stackSize - 1, tTankStack.amount / tFilledAmount); + tTankStack.amount -= tFilledAmount * tAdditionalParallel; + tFilledContainer.stackSize += tAdditionalParallel; + } + replaceCursorItemStack(aPlayer, tFilledContainer); + } + aFluidAccess.verifyFluidStack(); + return tFilledContainer; + } + + protected static ItemStack fillFluid(IFluidAccess aFluidAccess, EntityPlayer aPlayer, FluidStack aFluidHeld, + boolean aProcessFullStack) { + // we are not using aMachine.fill() here any more, so we need to check for fluid type here ourselves + if (aFluidAccess.get() != null && !aFluidAccess.get() + .isFluidEqual(aFluidHeld)) return null; + ItemStack tStackHeld = aPlayer.inventory.getItemStack(); + ItemStack tStackSizedOne = GT_Utility.copyAmount(1, tStackHeld); + if (tStackSizedOne == null) return null; + + int tFreeSpace = aFluidAccess.getCapacity() - (aFluidAccess.get() != null ? aFluidAccess.get().amount : 0); + if (tFreeSpace <= 0) + // no space left + return null; + + // find out how much fluid can be taken + // some cells cannot be partially filled + ItemStack tStackEmptied = null; + int tAmountTaken = 0; + if (tFreeSpace >= aFluidHeld.amount) { + // fully accepted - try take it from item now + // IFluidContainerItem is intentionally not checked here. it will be checked later + tStackEmptied = GT_Utility.getContainerForFilledItem(tStackSizedOne, false); + tAmountTaken = aFluidHeld.amount; + } + if (tStackEmptied == null && tStackSizedOne.getItem() instanceof IFluidContainerItem container) { + // either partially accepted, or is IFluidContainerItem + FluidStack tDrained = container.drain(tStackSizedOne, tFreeSpace, true); + if (tDrained != null && tDrained.amount > 0) { + // something is actually drained - change the cell and drop it to player + tStackEmptied = tStackSizedOne; + tAmountTaken = tDrained.amount; + } + } + if (tStackEmptied == null) + // somehow the cell refuse to give out that amount of fluid, no op then + return null; + + // find out how many fill can we do + // same round down behavior as above + // however here the fluid stack is not changed at all, so the exact code will slightly differ + int tParallel = aProcessFullStack ? Math.min(tFreeSpace / tAmountTaken, tStackHeld.stackSize) : 1; + if (aFluidAccess.get() == null) { + FluidStack tNewFillableStack = aFluidHeld.copy(); + tNewFillableStack.amount = tAmountTaken * tParallel; + aFluidAccess.set(tNewFillableStack); + } else { + aFluidAccess.addAmount(tAmountTaken * tParallel); + } + tStackEmptied.stackSize = tParallel; + replaceCursorItemStack(aPlayer, tStackEmptied); + return tStackEmptied; + } + + private static void replaceCursorItemStack(EntityPlayer aPlayer, ItemStack tStackResult) { + int tStackResultMaxStackSize = tStackResult.getMaxStackSize(); + while (tStackResult.stackSize > tStackResultMaxStackSize) { + aPlayer.inventory.getItemStack().stackSize -= tStackResultMaxStackSize; + GT_Utility.addItemToPlayerInventory(aPlayer, tStackResult.splitStack(tStackResultMaxStackSize)); + } + if (aPlayer.inventory.getItemStack().stackSize == tStackResult.stackSize) { + // every cell is mutated. it could just stay on the cursor. + aPlayer.inventory.setItemStack(tStackResult); + } else { + // some cells not mutated. The mutated cells must go into the inventory + // or drop into the world if there isn't enough space. + ItemStack tStackHeld = aPlayer.inventory.getItemStack(); + tStackHeld.stackSize -= tStackResult.stackSize; + GT_Utility.addItemToPlayerInventory(aPlayer, tStackResult); + } + } +} diff --git a/src/main/java/gregtech/api/gui/GT_ContainerMetaTile_Machine.java b/src/main/java/gregtech/api/gui/GT_ContainerMetaTile_Machine.java new file mode 100644 index 0000000000..a77f376e00 --- /dev/null +++ b/src/main/java/gregtech/api/gui/GT_ContainerMetaTile_Machine.java @@ -0,0 +1,244 @@ +package gregtech.api.gui; + +import java.util.List; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.inventory.ICrafting; +import net.minecraft.item.ItemStack; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import gregtech.api.interfaces.IConfigurationCircuitSupport; +import gregtech.api.interfaces.metatileentity.IMetaTileEntity; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.util.GT_Utility; + +/** + * NEVER INCLUDE THIS FILE IN YOUR MOD!!! + * <p/> + * The Container I use for all my MetaTileEntities + */ +public class GT_ContainerMetaTile_Machine extends GT_Container { + + public int mActive = 0, mMaxProgressTime = 0, mProgressTime = 0, mEnergy = 0, mSteam = 0, mSteamStorage = 0, + mStorage = 0, mOutput = 0, mInput = 0, mID = 0, mDisplayErrorCode = 0; + public long mEnergyLong = 0, mStorageLong = 0; + private int oActive = 0, oMaxProgressTime = 0, oProgressTime = 0, oEnergy = 0, oSteam = 0, oSteamStorage = 0, + oStorage = 0, oOutput = 0, oInput = 0, oID = 0, oDisplayErrorCode = 0; + private long oEnergyLong = 0, oStorageLong = 0; + protected int mTimer = 0; + protected Runnable circuitSlotClickCallback; + + public GT_ContainerMetaTile_Machine(InventoryPlayer aInventoryPlayer, IGregTechTileEntity aTileEntity) { + super(aInventoryPlayer, aTileEntity); + + mTileEntity = aTileEntity; + + if (mTileEntity != null && mTileEntity.getMetaTileEntity() != null) { + addSlots(aInventoryPlayer); + if (doesBindPlayerInventory()) bindPlayerInventory(aInventoryPlayer); + detectAndSendChanges(); + } else { + aInventoryPlayer.player.openContainer = aInventoryPlayer.player.inventoryContainer; + } + } + + public GT_ContainerMetaTile_Machine(InventoryPlayer aInventoryPlayer, IGregTechTileEntity aTileEntity, + boolean doesBindInventory) { + super(aInventoryPlayer, aTileEntity); + mTileEntity = aTileEntity; + + if (mTileEntity != null && mTileEntity.getMetaTileEntity() != null) { + addSlots(aInventoryPlayer); + if (doesBindPlayerInventory() && doesBindInventory) bindPlayerInventory(aInventoryPlayer); + detectAndSendChanges(); + } else { + aInventoryPlayer.player.openContainer = aInventoryPlayer.player.inventoryContainer; + } + } + + protected void addCircuitSlot() { + if (mTileEntity.getMetaTileEntity() instanceof IConfigurationCircuitSupport ccs) { + GT_Slot_Render slotCircuit = new GT_Slot_Render( + mTileEntity, + ccs.getCircuitSlot(), + ccs.getCircuitSlotX(), + ccs.getCircuitSlotY()); + addSlotToContainer(slotCircuit); + slotCircuit.setEnabled(ccs.allowSelectCircuit()); + } + } + + @Override + public void addSlots(InventoryPlayer aInventoryPlayer) { + addCircuitSlot(); + } + + @Override + public void detectAndSendChanges() { + super.detectAndSendChanges(); + if (mTileEntity.isClientSide() || mTileEntity.getMetaTileEntity() == null) return; + mStorage = (int) Math.min(Integer.MAX_VALUE, mTileEntity.getEUCapacity()); + mStorageLong = mTileEntity.getEUCapacity(); + mEnergy = (int) Math.min(Integer.MAX_VALUE, mTileEntity.getStoredEU()); + mEnergyLong = mTileEntity.getStoredEU(); + mSteamStorage = (int) Math.min(Integer.MAX_VALUE, mTileEntity.getSteamCapacity()); + mSteam = (int) Math.min(Integer.MAX_VALUE, mTileEntity.getStoredSteam()); + mOutput = (int) Math.min(Integer.MAX_VALUE, mTileEntity.getOutputVoltage()); + mInput = (int) Math.min(Integer.MAX_VALUE, mTileEntity.getInputVoltage()); + mDisplayErrorCode = mTileEntity.getErrorDisplayID(); + mProgressTime = mTileEntity.getProgress(); + mMaxProgressTime = mTileEntity.getMaxProgress(); + mActive = mTileEntity.isActive() ? 1 : 0; + mTimer++; + + for (ICrafting player : this.crafters) { + if (mTimer % 500 == 10 || oEnergy != mEnergy) { + player.sendProgressBarUpdate(this, 0, mEnergy & 65535); + player.sendProgressBarUpdate(this, 1, mEnergy >>> 16); + } + if (mTimer % 500 == 10 || oStorage != mStorage) { + player.sendProgressBarUpdate(this, 2, mStorage & 65535); + player.sendProgressBarUpdate(this, 3, mStorage >>> 16); + } + if (mTimer % 500 == 10 || oOutput != mOutput) { + player.sendProgressBarUpdate(this, 4, mOutput); + } + if (mTimer % 500 == 10 || oInput != mInput) { + player.sendProgressBarUpdate(this, 5, mInput); + } + if (mTimer % 500 == 10 || oDisplayErrorCode != mDisplayErrorCode) { + player.sendProgressBarUpdate(this, 6, mDisplayErrorCode); + } + if (mTimer % 500 == 10 || oProgressTime != mProgressTime) { + player.sendProgressBarUpdate(this, 11, mProgressTime & 65535); + player.sendProgressBarUpdate(this, 12, mProgressTime >>> 16); + } + if (mTimer % 500 == 10 || oMaxProgressTime != mMaxProgressTime) { + player.sendProgressBarUpdate(this, 13, mMaxProgressTime & 65535); + player.sendProgressBarUpdate(this, 14, mMaxProgressTime >>> 16); + } + if (mTimer % 500 == 10 || oID != mID) { + player.sendProgressBarUpdate(this, 15, mID); + } + if (mTimer % 500 == 10 || oActive != mActive) { + player.sendProgressBarUpdate(this, 16, mActive); + } + if (mTimer % 500 == 10 || oSteam != mSteam) { + player.sendProgressBarUpdate(this, 17, mSteam & 65535); + player.sendProgressBarUpdate(this, 18, mSteam >>> 16); + } + if (mTimer % 500 == 10 || oSteamStorage != mSteamStorage) { + player.sendProgressBarUpdate(this, 19, mSteamStorage & 65535); + player.sendProgressBarUpdate(this, 20, mSteamStorage >>> 16); + } + if (mTimer % 500 == 10 || oEnergyLong != mEnergyLong) { + player.sendProgressBarUpdate(this, 21, (int) mEnergyLong); + player.sendProgressBarUpdate(this, 22, (int) (mEnergyLong >>> 32)); + } + if (mTimer % 500 == 10 || oStorageLong != mStorageLong) { + player.sendProgressBarUpdate(this, 23, (int) mStorageLong); + player.sendProgressBarUpdate(this, 24, (int) (mStorageLong >>> 32)); + } + } + + oID = mID; + oSteam = mSteam; + oInput = mInput; + oActive = mActive; + oOutput = mOutput; + oEnergy = mEnergy; + oEnergyLong = mEnergyLong; + oStorage = mStorage; + oStorageLong = mStorageLong; + oSteamStorage = mSteamStorage; + oProgressTime = mProgressTime; + oMaxProgressTime = mMaxProgressTime; + oDisplayErrorCode = mDisplayErrorCode; + } + + @SideOnly(Side.CLIENT) + @Override + public void updateProgressBar(int id, int value) { + super.updateProgressBar(id, value); + switch (id) { + case 0 -> mEnergy = mEnergy & 0xffff0000 | value & 0x0000ffff; + case 1 -> mEnergy = mEnergy & 0x0000ffff | value << 16; + case 2 -> mStorage = mStorage & 0xffff0000 | value & 0x0000ffff; + case 3 -> mStorage = mStorage & 0x0000ffff | value << 16; + case 4 -> mOutput = value; + case 5 -> mInput = value; + case 6 -> mDisplayErrorCode = value; + case 11 -> mProgressTime = mProgressTime & 0xffff0000 | value; + case 12 -> mProgressTime = mProgressTime & 0x0000ffff | value << 16; + case 13 -> mMaxProgressTime = mMaxProgressTime & 0xffff0000 | value & 0x0000ffff; + case 14 -> mMaxProgressTime = mMaxProgressTime & 0x0000ffff | value << 16; + case 15 -> mID = value; + case 16 -> mActive = value; + case 17 -> mSteam = mSteam & 0xffff0000 | value & 0x0000ffff; + case 18 -> mSteam = mSteam & 0x0000ffff | value << 16; + case 19 -> mSteamStorage = mSteamStorage & 0xffff0000 | value & 0x0000ffff; + case 20 -> mSteamStorage = mSteamStorage & 0x0000ffff | value << 16; + case 21 -> mEnergyLong = mEnergyLong & 0xffffffff00000000L | value & 0x00000000ffffffffL; + case 22 -> mEnergyLong = mEnergyLong & 0x00000000ffffffffL | (long) value << 32; + case 23 -> mStorageLong = mStorageLong & 0xffffffff00000000L | value & 0x00000000ffffffffL; + case 24 -> mStorageLong = mStorageLong & 0x00000000ffffffffL | (long) value << 32; + } + } + + @Override + public boolean canInteractWith(EntityPlayer player) { + return mTileEntity.isUseableByPlayer(player); + } + + @Deprecated + public String trans(String aKey, String aEnglish) { + return GT_Utility.trans(aKey, aEnglish); + } + + public void setCircuitSlotClickCallback(Runnable circuitSlotClickCallback) { + this.circuitSlotClickCallback = circuitSlotClickCallback; + } + + @Override + public ItemStack slotClick(int aSlotNumber, int aMouseclick, int aShifthold, EntityPlayer aPlayer) { + if (mTileEntity.getMetaTileEntity() instanceof IConfigurationCircuitSupport) { + IMetaTileEntity machine = mTileEntity.getMetaTileEntity(); + IConfigurationCircuitSupport ccs = (IConfigurationCircuitSupport) machine; + if (ccs.allowSelectCircuit() && aSlotNumber == ccs.getCircuitGUISlot() && aMouseclick < 2) { + ItemStack newCircuit; + if (aShifthold == 1) { + if (aMouseclick == 0) { + if (circuitSlotClickCallback != null) circuitSlotClickCallback.run(); + return null; + } else { + // clear + newCircuit = null; + } + } else { + ItemStack cursorStack = aPlayer.inventory.getItemStack(); + List<ItemStack> tCircuits = ccs.getConfigurationCircuits(); + int index = GT_Utility.findMatchingStackInList(tCircuits, cursorStack); + if (index < 0) { + int curIndex = GT_Utility + .findMatchingStackInList(tCircuits, machine.getStackInSlot(ccs.getCircuitSlot())) + 1; + if (aMouseclick == 0) { + curIndex += 1; + } else { + curIndex -= 1; + } + curIndex = Math.floorMod(curIndex, tCircuits.size() + 1) - 1; + newCircuit = curIndex < 0 ? null : tCircuits.get(curIndex); + } else { + // set to whatever it is + newCircuit = tCircuits.get(index); + } + } + mTileEntity.setInventorySlotContents(ccs.getCircuitSlot(), newCircuit); + return newCircuit; + } + } + return super.slotClick(aSlotNumber, aMouseclick, aShifthold, aPlayer); + } +} diff --git a/src/main/java/gregtech/api/gui/GT_Container_1by1.java b/src/main/java/gregtech/api/gui/GT_Container_1by1.java new file mode 100644 index 0000000000..06efaee5ef --- /dev/null +++ b/src/main/java/gregtech/api/gui/GT_Container_1by1.java @@ -0,0 +1,30 @@ +package gregtech.api.gui; + +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.inventory.Slot; + +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; + +@Deprecated +public class GT_Container_1by1 extends GT_ContainerMetaTile_Machine { + + public GT_Container_1by1(InventoryPlayer aInventoryPlayer, IGregTechTileEntity aTileEntity) { + super(aInventoryPlayer, aTileEntity); + } + + @Override + public void addSlots(InventoryPlayer aInventoryPlayer) { + addSlotToContainer(new Slot(mTileEntity, 0, 80, 35)); + super.addSlots(aInventoryPlayer); + } + + @Override + public int getSlotCount() { + return 1; + } + + @Override + public int getShiftClickSlotCount() { + return 1; + } +} diff --git a/src/main/java/gregtech/api/gui/GT_Container_2by2.java b/src/main/java/gregtech/api/gui/GT_Container_2by2.java new file mode 100644 index 0000000000..4e3584a0a6 --- /dev/null +++ b/src/main/java/gregtech/api/gui/GT_Container_2by2.java @@ -0,0 +1,33 @@ +package gregtech.api.gui; + +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.inventory.Slot; + +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; + +@Deprecated +public class GT_Container_2by2 extends GT_ContainerMetaTile_Machine { + + public GT_Container_2by2(InventoryPlayer aInventoryPlayer, IGregTechTileEntity aTileEntity) { + super(aInventoryPlayer, aTileEntity); + } + + @Override + public void addSlots(InventoryPlayer aInventoryPlayer) { + addSlotToContainer(new Slot(mTileEntity, 0, 71, 26)); + addSlotToContainer(new Slot(mTileEntity, 1, 89, 26)); + addSlotToContainer(new Slot(mTileEntity, 2, 71, 44)); + addSlotToContainer(new Slot(mTileEntity, 3, 89, 44)); + super.addSlots(aInventoryPlayer); + } + + @Override + public int getSlotCount() { + return 4; + } + + @Override + public int getShiftClickSlotCount() { + return 4; + } +} diff --git a/src/main/java/gregtech/api/gui/GT_Container_3by3.java b/src/main/java/gregtech/api/gui/GT_Container_3by3.java new file mode 100644 index 0000000000..4c0f7f946b --- /dev/null +++ b/src/main/java/gregtech/api/gui/GT_Container_3by3.java @@ -0,0 +1,38 @@ +package gregtech.api.gui; + +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.inventory.Slot; + +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; + +@Deprecated +public class GT_Container_3by3 extends GT_ContainerMetaTile_Machine { + + public GT_Container_3by3(InventoryPlayer aInventoryPlayer, IGregTechTileEntity aTileEntity) { + super(aInventoryPlayer, aTileEntity); + } + + @Override + public void addSlots(InventoryPlayer aInventoryPlayer) { + addSlotToContainer(new Slot(mTileEntity, 0, 62, 17)); + addSlotToContainer(new Slot(mTileEntity, 1, 80, 17)); + addSlotToContainer(new Slot(mTileEntity, 2, 98, 17)); + addSlotToContainer(new Slot(mTileEntity, 3, 62, 35)); + addSlotToContainer(new Slot(mTileEntity, 4, 80, 35)); + addSlotToContainer(new Slot(mTileEntity, 5, 98, 35)); + addSlotToContainer(new Slot(mTileEntity, 6, 62, 53)); + addSlotToContainer(new Slot(mTileEntity, 7, 80, 53)); + addSlotToContainer(new Slot(mTileEntity, 8, 98, 53)); + super.addSlots(aInventoryPlayer); + } + + @Override + public int getSlotCount() { + return 9; + } + + @Override + public int getShiftClickSlotCount() { + return 9; + } +} diff --git a/src/main/java/gregtech/api/gui/GT_Container_4by4.java b/src/main/java/gregtech/api/gui/GT_Container_4by4.java new file mode 100644 index 0000000000..db5cde4cfe --- /dev/null +++ b/src/main/java/gregtech/api/gui/GT_Container_4by4.java @@ -0,0 +1,45 @@ +package gregtech.api.gui; + +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.inventory.Slot; + +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; + +@Deprecated +public class GT_Container_4by4 extends GT_ContainerMetaTile_Machine { + + public GT_Container_4by4(InventoryPlayer aInventoryPlayer, IGregTechTileEntity aTileEntity) { + super(aInventoryPlayer, aTileEntity); + } + + @Override + public void addSlots(InventoryPlayer aInventoryPlayer) { + addSlotToContainer(new Slot(mTileEntity, 0, 53, 8)); + addSlotToContainer(new Slot(mTileEntity, 1, 71, 8)); + addSlotToContainer(new Slot(mTileEntity, 2, 89, 8)); + addSlotToContainer(new Slot(mTileEntity, 3, 107, 8)); + addSlotToContainer(new Slot(mTileEntity, 4, 53, 26)); + addSlotToContainer(new Slot(mTileEntity, 5, 71, 26)); + addSlotToContainer(new Slot(mTileEntity, 6, 89, 26)); + addSlotToContainer(new Slot(mTileEntity, 7, 107, 26)); + addSlotToContainer(new Slot(mTileEntity, 8, 53, 44)); + addSlotToContainer(new Slot(mTileEntity, 9, 71, 44)); + addSlotToContainer(new Slot(mTileEntity, 10, 89, 44)); + addSlotToContainer(new Slot(mTileEntity, 11, 107, 44)); + addSlotToContainer(new Slot(mTileEntity, 12, 53, 62)); + addSlotToContainer(new Slot(mTileEntity, 13, 71, 62)); + addSlotToContainer(new Slot(mTileEntity, 14, 89, 62)); + addSlotToContainer(new Slot(mTileEntity, 15, 107, 62)); + super.addSlots(aInventoryPlayer); + } + + @Override + public int getSlotCount() { + return 16; + } + + @Override + public int getShiftClickSlotCount() { + return 16; + } +} diff --git a/src/main/java/gregtech/api/gui/GT_Container_BasicTank.java b/src/main/java/gregtech/api/gui/GT_Container_BasicTank.java new file mode 100644 index 0000000000..403de4bab5 --- /dev/null +++ b/src/main/java/gregtech/api/gui/GT_Container_BasicTank.java @@ -0,0 +1,138 @@ +package gregtech.api.gui; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.inventory.ICrafting; +import net.minecraft.inventory.Slot; +import net.minecraft.item.ItemStack; +import net.minecraftforge.fluids.FluidStack; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import gregtech.api.interfaces.IFluidAccess; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_BasicTank; +import gregtech.api.util.GT_Utility; + +/** + * NEVER INCLUDE THIS FILE IN YOUR MOD!!! + * <p/> + * The Container I use for all my Basic Tanks + */ +public class GT_Container_BasicTank extends GT_ContainerMetaTile_Machine { + + public int mContent = 0; + protected int oContent = 0; + + public GT_Container_BasicTank(InventoryPlayer aInventoryPlayer, IGregTechTileEntity aTileEntity) { + super(aInventoryPlayer, aTileEntity); + } + + /** + * Subclasses must ensure third slot (aSlotIndex==2) is drainable fluid display item slot. Otherwise, subclasses + * must intercept the appropriate the slotClick event and call super.slotClick(2, xxx) if necessary + */ + @Override + public void addSlots(InventoryPlayer aInventoryPlayer) { + addSlotToContainer(new Slot(mTileEntity, 0, 80, 17)); + addSlotToContainer(new GT_Slot_Output(mTileEntity, 1, 80, 53)); + addSlotToContainer(new GT_Slot_Render(mTileEntity, 2, 59, 42)); + } + + @Override + public ItemStack slotClick(int aSlotIndex, int aMouseclick, int aShifthold, EntityPlayer aPlayer) { + if (aSlotIndex == 2 && aMouseclick < 2) { + GT_MetaTileEntity_BasicTank tTank = (GT_MetaTileEntity_BasicTank) mTileEntity.getMetaTileEntity(); + if (mTileEntity.isClientSide()) { + /* + * While a logical client don't really need to process fluid cells upon click (it could have just wait + * for server side to send the result), doing so would result in every fluid interaction having a + * noticeable delay between clicking and changes happening even on single player. I'd imagine this lag + * to become only more severe when playing MP over ethernet, which would have much more latency than a + * memory connection + */ + Slot slot = inventorySlots.get(aSlotIndex); + tTank.setDrainableStack(GT_Utility.getFluidFromDisplayStack(slot.getStack())); + } + IFluidAccess tDrainableAccess = constructFluidAccess(tTank, false); + return handleFluidSlotClick( + tDrainableAccess, + aPlayer, + aMouseclick == 0, + true, + !tTank.isDrainableStackSeparate()); + } + return super.slotClick(aSlotIndex, aMouseclick, aShifthold, aPlayer); + } + + @Override + public void detectAndSendChanges() { + super.detectAndSendChanges(); + if (mTileEntity.isClientSide() || mTileEntity.getMetaTileEntity() == null) return; + if (((GT_MetaTileEntity_BasicTank) mTileEntity.getMetaTileEntity()).mFluid != null) + mContent = ((GT_MetaTileEntity_BasicTank) mTileEntity.getMetaTileEntity()).mFluid.amount; + else mContent = 0; + sendProgressBar(); + oContent = mContent; + } + + public void sendProgressBar() { + for (ICrafting player : this.crafters) { + if (mTimer % 500 == 0 || oContent != mContent) { + player.sendProgressBarUpdate(this, 100, mContent & 65535); + player.sendProgressBarUpdate(this, 101, mContent >>> 16); + } + } + } + + @Override + @SideOnly(Side.CLIENT) + public void updateProgressBar(int id, int value) { + super.updateProgressBar(id, value); + switch (id) { + case 100 -> mContent = mContent & 0xffff0000 | value & 0x0000ffff; + case 101 -> mContent = mContent & 0xffff | value << 16; + } + } + + @Override + public int getSlotCount() { + return 2; + } + + @Override + public int getShiftClickSlotCount() { + return 1; + } + + protected IFluidAccess constructFluidAccess(GT_MetaTileEntity_BasicTank aTank, boolean aIsFillableStack) { + return new BasicTankFluidAccess(aTank, aIsFillableStack); + } + + static class BasicTankFluidAccess implements IFluidAccess { + + protected final GT_MetaTileEntity_BasicTank mTank; + protected final boolean mIsFillableStack; + + public BasicTankFluidAccess(GT_MetaTileEntity_BasicTank aTank, boolean aIsFillableStack) { + this.mTank = aTank; + this.mIsFillableStack = aIsFillableStack; + } + + @Override + public void set(FluidStack stack) { + if (mIsFillableStack) mTank.setFillableStack(stack); + else mTank.setDrainableStack(stack); + } + + @Override + public FluidStack get() { + return mIsFillableStack ? mTank.getFillableStack() : mTank.getDrainableStack(); + } + + @Override + public int getCapacity() { + return mTank.getCapacity(); + } + } +} diff --git a/src/main/java/gregtech/api/gui/GT_Container_MultiMachine.java b/src/main/java/gregtech/api/gui/GT_Container_MultiMachine.java new file mode 100644 index 0000000000..142b84e008 --- /dev/null +++ b/src/main/java/gregtech/api/gui/GT_Container_MultiMachine.java @@ -0,0 +1,39 @@ +package gregtech.api.gui; + +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.inventory.Slot; + +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; + +/** + * NEVER INCLUDE THIS FILE IN YOUR MOD!!! + * <p/> + * The Container I use for all my Basic Machines + */ +@Deprecated +public class GT_Container_MultiMachine extends GT_ContainerMetaTile_Machine { + + public GT_Container_MultiMachine(InventoryPlayer aInventoryPlayer, IGregTechTileEntity aTileEntity) { + super(aInventoryPlayer, aTileEntity); + } + + public GT_Container_MultiMachine(InventoryPlayer aInventoryPlayer, IGregTechTileEntity aTileEntity, + boolean bindInventory) { + super(aInventoryPlayer, aTileEntity, bindInventory); + } + + @Override + public void addSlots(InventoryPlayer aInventoryPlayer) { + addSlotToContainer(new Slot(mTileEntity, 1, 152, 5)); + } + + @Override + public int getSlotCount() { + return 1; + } + + @Override + public int getShiftClickSlotCount() { + return 1; + } +} diff --git a/src/main/java/gregtech/api/gui/GT_GUIColorOverride.java b/src/main/java/gregtech/api/gui/GT_GUIColorOverride.java new file mode 100644 index 0000000000..304e792a2a --- /dev/null +++ b/src/main/java/gregtech/api/gui/GT_GUIColorOverride.java @@ -0,0 +1,92 @@ +package gregtech.api.gui; + +import java.util.concurrent.ExecutionException; + +import javax.annotation.Nonnull; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.resources.IResource; +import net.minecraft.util.ResourceLocation; + +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import com.google.common.util.concurrent.UncheckedExecutionException; + +import cpw.mods.fml.relauncher.FMLLaunchHandler; +import cpw.mods.fml.relauncher.Side; +import gregtech.api.GregTech_API; +import gregtech.api.util.ColorsMetadataSection; + +public class GT_GUIColorOverride { + + private static final Object NOT_FOUND = new Object(); + private static final LoadingCache<ResourceLocation, Object> cache = CacheBuilder.newBuilder() + .softValues() + .build(new CacheLoader<>() { + + @Override + public Object load(@Nonnull ResourceLocation key) throws Exception { + IResource ir = Minecraft.getMinecraft() + .getResourceManager() + .getResource(key); + if (ir.hasMetadata()) return ir.getMetadata("colors"); + // return a dummy + // object because + // LoadingCache + // doesn't like null + return NOT_FOUND; + } + }); + private static final GT_GUIColorOverride FALLBACK = new GT_GUIColorOverride(); + private ColorsMetadataSection cmSection; + + public static GT_GUIColorOverride get(String fullLocation) { + // see other get for more info + if (FMLLaunchHandler.side() != Side.CLIENT) return FALLBACK; + return new GT_GUIColorOverride(new ResourceLocation(fullLocation)); + } + + public static GT_GUIColorOverride get(ResourceLocation path) { + // use dummy fallback if there isn't such thing as a resource pack. + // #side() usually has two possible return value, but since this might be called by test code, it might + // also return null when in test env. Using #isClient will cause a NPE. A plain inequality test won't. + // FMLCommonHandler's #getSide() might trigger a NPE when in test env, so no. + if (FMLLaunchHandler.side() != Side.CLIENT) return FALLBACK; + return new GT_GUIColorOverride(path); + } + + private GT_GUIColorOverride() { + cmSection = null; + } + + private GT_GUIColorOverride(ResourceLocation resourceLocation) { + try { + Object metadata = cache.get(resourceLocation); + if (metadata != NOT_FOUND) cmSection = (ColorsMetadataSection) metadata; + } catch (ExecutionException | UncheckedExecutionException ignore) { + // make sure it doesn't cache a failing entry + cache.invalidate(resourceLocation); + } + } + + public int getTextColorOrDefault(String textType, int defaultColor) { + return sLoaded() ? cmSection.getTextColorOrDefault(textType, defaultColor) : defaultColor; + } + + public int getGuiTintOrDefault(String key, int defaultColor) { + return sLoaded() ? cmSection.getGuiTintOrDefault(key, defaultColor) : defaultColor; + } + + public boolean sGuiTintingEnabled() { + return sLoaded() ? cmSection.sGuiTintingEnabled() : GregTech_API.sColoredGUI; + } + + public boolean sLoaded() { + return cmSection != null; + } + + public static void onResourceManagerReload() { + cache.invalidateAll(); + } +} diff --git a/src/main/java/gregtech/api/gui/GT_GUIContainer.java b/src/main/java/gregtech/api/gui/GT_GUIContainer.java new file mode 100644 index 0000000000..639bd56162 --- /dev/null +++ b/src/main/java/gregtech/api/gui/GT_GUIContainer.java @@ -0,0 +1,99 @@ +package gregtech.api.gui; + +import static gregtech.GT_Mod.GT_FML_LOGGER; + +import net.minecraft.client.gui.inventory.GuiContainer; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.inventory.Container; +import net.minecraft.inventory.Slot; +import net.minecraft.util.ResourceLocation; + +import org.lwjgl.input.Mouse; + +/** + * NEVER INCLUDE THIS FILE IN YOUR MOD!!! + * <p/> + * Main GUI-Container-Class which basically contains the Code needed to prevent crashes from improperly Coded Items. + */ +public class GT_GUIContainer extends GuiContainer { + + public boolean mCrashed = false; + + public ResourceLocation mGUIbackground; + + public GT_GUIColorOverride colorOverride; + + public String mGUIbackgroundPath; + + public GT_GUIContainer(Container aContainer, String aGUIbackground) { + super(aContainer); + mGUIbackground = new ResourceLocation(mGUIbackgroundPath = aGUIbackground); + colorOverride = GT_GUIColorOverride.get(aGUIbackground); + } + + protected int getTextColorOrDefault(String textType, int defaultColor) { + return colorOverride.getTextColorOrDefault(textType, defaultColor); + } + + public int getLeft() { + return guiLeft; + } + + public int getTop() { + return guiTop; + } + + @Override + protected void drawGuiContainerForegroundLayer(int mouseX, int mouseY) { + // + } + + @Override + protected void drawGuiContainerBackgroundLayer(float parTicks, int mouseX, int mouseY) { + mc.renderEngine.bindTexture(mGUIbackground); + } + + @Override + public void drawScreen(int mouseX, int mouseY, float parTicks) { + try { + super.drawScreen(mouseX, mouseY, parTicks); + } catch (Throwable e) { + try { + Tessellator.instance.draw(); + } catch (Throwable f) { + // + } + } + } + + @Override + public void handleMouseInput() { + int delta = Mouse.getEventDWheel(); + if (delta != 0) { + int i = Mouse.getEventX() * this.width / this.mc.displayWidth; + int j = this.height - Mouse.getEventY() * this.height / this.mc.displayHeight - 1; + onMouseWheel(i, j, delta); + } + super.handleMouseInput(); + } + + protected void onMouseWheel(int mx, int my, int delta) {} + + public boolean isMouseOverSlot(int slotIndex, int mx, int my) { + int size = inventorySlots.inventorySlots.size(); + if (slotIndex < 0 || slotIndex >= size) { + // slot does not exist somehow. log and carry on + GT_FML_LOGGER.error("Slot {} required where only {} is present", slotIndex, size); + return false; + } + Slot slot = inventorySlots.getSlot(slotIndex); + return this.func_146978_c(slot.xDisplayPosition, slot.yDisplayPosition, 16, 16, mx, my); + } + + /* + * @Override protected void drawSlotInventory(Slot slot) { try { super.drawSlotInventory(slot); } catch(Throwable e) + * { try { Tessellator.instance.draw(); } catch(Throwable f) {} if (!mCrashed) { GT_Log.out. + * println("Clientside Slot drawing Crash prevented. Seems one Itemstack causes Problems with negative Damage Values or the Wildcard Damage Value. This is absolutely NOT a Bug of the GregTech-Addon, so don't even think about reporting it to me, it's a Bug of the Mod, which belongs to the almost-crash-causing Item, so bug that Mods Author and not me! Did you hear it? NOT ME!!!" + * ); e.printStackTrace(); mCrashed = true; } } } + */ +} diff --git a/src/main/java/gregtech/api/gui/GT_GUIContainerMetaTile_Machine.java b/src/main/java/gregtech/api/gui/GT_GUIContainerMetaTile_Machine.java new file mode 100644 index 0000000000..df395858a9 --- /dev/null +++ b/src/main/java/gregtech/api/gui/GT_GUIContainerMetaTile_Machine.java @@ -0,0 +1,271 @@ +package gregtech.api.gui; + +import java.util.List; + +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.client.renderer.entity.RenderItem; +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.inventory.Slot; +import net.minecraft.item.ItemStack; +import net.minecraft.util.StatCollector; + +import org.lwjgl.opengl.GL11; + +import gregtech.GT_Mod; +import gregtech.api.GregTech_API; +import gregtech.api.enums.Dyes; +import gregtech.api.enums.GT_Values; +import gregtech.api.gui.widgets.GT_GuiCoverTabLine; +import gregtech.api.gui.widgets.GT_GuiIcon; +import gregtech.api.gui.widgets.GT_GuiSlotTooltip; +import gregtech.api.gui.widgets.GT_GuiTabLine.DisplayStyle; +import gregtech.api.gui.widgets.GT_GuiTabLine.GT_GuiTabIconSet; +import gregtech.api.gui.widgets.GT_GuiTabLine.GT_ITabRenderer; +import gregtech.api.gui.widgets.GT_GuiTooltip; +import gregtech.api.gui.widgets.GT_GuiTooltipManager; +import gregtech.api.gui.widgets.GT_GuiTooltipManager.GT_IToolTipRenderer; +import gregtech.api.interfaces.IConfigurationCircuitSupport; +import gregtech.api.interfaces.metatileentity.IMetaTileEntity; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.metatileentity.MetaTileEntity; +import gregtech.api.net.GT_Packet_SetConfigurationCircuit; +import gregtech.api.util.GT_TooltipDataCache; +import gregtech.api.util.GT_Util; +import gregtech.api.util.GT_Utility; + +/** + * NEVER INCLUDE THIS FILE IN YOUR MOD!!! + * <p/> + * The GUI-Container I use for all my MetaTileEntities + */ +public class GT_GUIContainerMetaTile_Machine extends GT_GUIContainer implements GT_IToolTipRenderer, GT_ITabRenderer { + + public final GT_ContainerMetaTile_Machine mContainer; + + protected final GT_GuiTooltipManager mTooltipManager = new GT_GuiTooltipManager(); + protected final GT_TooltipDataCache mTooltipCache = new GT_TooltipDataCache(); + private static final String GHOST_CIRCUIT_TOOLTIP = "GT5U.machines.select_circuit.tooltip"; + + private final int guiTint; + + // Cover Tabs support. Subclasses can override display position, style and visuals by overriding setupCoverTabs + public GT_GuiCoverTabLine coverTabs; + private static final int COVER_TAB_LEFT = -16, COVER_TAB_TOP = 1, COVER_TAB_HEIGHT = 20, COVER_TAB_WIDTH = 18, + COVER_TAB_SPACING = 2; + private static final DisplayStyle COVER_TAB_X_DIR = DisplayStyle.NONE, COVER_TAB_Y_DIR = DisplayStyle.NORMAL; + private static final GT_GuiTabIconSet TAB_ICONSET = new GT_GuiTabIconSet( + GT_GuiIcon.TAB_NORMAL, + GT_GuiIcon.TAB_HIGHLIGHT, + GT_GuiIcon.TAB_DISABLED); + + public GT_GUIContainerMetaTile_Machine(GT_ContainerMetaTile_Machine aContainer, String aGUIbackground) { + super(aContainer, aGUIbackground); + mContainer = aContainer; + + DisplayStyle preferredDisplayStyle = GT_Mod.gregtechproxy.mCoverTabsVisible + ? (GT_Mod.gregtechproxy.mCoverTabsFlipped ? DisplayStyle.INVERSE : DisplayStyle.NORMAL) + : DisplayStyle.NONE; + setupCoverTabs(preferredDisplayStyle); + + // Only setup tooltips if they're currently enabled. + if (GT_Mod.gregtechproxy.mTooltipVerbosity > 0 || GT_Mod.gregtechproxy.mTooltipShiftVerbosity > 0) { + setupTooltips(); + } + + guiTint = getColorization(); + mContainer.setCircuitSlotClickCallback(this::openSelectCircuitDialog); + } + + public GT_GUIContainerMetaTile_Machine(InventoryPlayer aInventoryPlayer, IGregTechTileEntity aTileEntity, + String aGUIbackground) { + this(new GT_ContainerMetaTile_Machine(aInventoryPlayer, aTileEntity), aGUIbackground); + } + + /** + * Initialize the coverTabs object according to client preferences + */ + protected void setupCoverTabs(DisplayStyle preferredDisplayStyle) { + coverTabs = new GT_GuiCoverTabLine( + this, + COVER_TAB_LEFT, + COVER_TAB_TOP, + COVER_TAB_HEIGHT, + COVER_TAB_WIDTH, + COVER_TAB_SPACING, + COVER_TAB_X_DIR, + COVER_TAB_Y_DIR, + preferredDisplayStyle, + getTabBackground(), + getMachine().getBaseMetaTileEntity(), + getColorization()); + } + + @Override + public void drawScreen(int mouseX, int mouseY, float parTicks) { + super.drawScreen(mouseX, mouseY, parTicks); + if (mc.thePlayer.inventory.getItemStack() == null) { + GL11.glPushMatrix(); + GL11.glTranslatef(guiLeft, guiTop, 0.0F); + mTooltipManager.onTick(this, mouseX, mouseY); + GL11.glPopMatrix(); + } + } + + @Override + protected void drawGuiContainerBackgroundLayer(float parTicks, int mouseX, int mouseY) { + // Drawing tabs + coverTabs.drawTabs(parTicks, mouseX, mouseY); + + // Applying machine coloration, which subclasses rely on + GL11.glColor3ub((byte) ((guiTint >> 16) & 0xFF), (byte) ((guiTint >> 8) & 0xFF), (byte) (guiTint & 0xFF)); + + // Binding machine's own texture, which subclasses rely on being set + super.drawGuiContainerBackgroundLayer(parTicks, mouseX, mouseY); + } + + /** + * @return The color used to render this machine's GUI + */ + private int getColorization() { + Dyes dye = Dyes.dyeWhite; + if (this.colorOverride.sLoaded()) { + if (this.colorOverride.sGuiTintingEnabled()) { + dye = getDyeFromIndex(mContainer.mTileEntity.getColorization()); + return this.colorOverride.getGuiTintOrDefault(dye.mName, GT_Util.getRGBInt(dye.getRGBA())); + } + } else if (GregTech_API.sColoredGUI) { + if (GregTech_API.sMachineMetalGUI) { + dye = Dyes.MACHINE_METAL; + } else if (mContainer != null && mContainer.mTileEntity != null) { + dye = getDyeFromIndex(mContainer.mTileEntity.getColorization()); + } + } + return GT_Util.getRGBInt(dye.getRGBA()); + } + + private Dyes getDyeFromIndex(short index) { + return index != -1 ? Dyes.get(index) : Dyes.MACHINE_METAL; + } + + /** + * @return This machine's MetaTileEntity + */ + private MetaTileEntity getMachine() { + return (MetaTileEntity) mContainer.mTileEntity.getMetaTileEntity(); + } + + // Tabs support + + @Override + protected void mouseClicked(int mouseX, int mouseY, int mouseButton) { + super.mouseClicked(mouseX, mouseY, mouseButton); + // Check for clicked tabs + coverTabs.onMouseClicked(mouseX, mouseY, mouseButton); + } + + @Override + public void initGui() { + super.initGui(); + // Perform layout of tabs + coverTabs.onInit(); + } + + /** + * @return the background textures used by this machine GUI's tabs + */ + protected GT_GuiTabIconSet getTabBackground() { + return TAB_ICONSET; + } + + // Tooltips support + + /** + * Load data for and create appropriate tooltips for this machine. Only called when one of regular or shift tooltips + * are enabled. + */ + protected void setupTooltips() { + if (mContainer.mTileEntity.getMetaTileEntity() instanceof IConfigurationCircuitSupport ccs) { + if (ccs.allowSelectCircuit()) addToolTip( + new GT_GuiSlotTooltip( + mContainer.getSlot(ccs.getCircuitGUISlot()), + mTooltipCache.getData(GHOST_CIRCUIT_TOOLTIP))); + } + } + + // GT_IToolTipRenderer and GT_ITabRenderer implementations + @Override + public void drawHoveringText(List<String> text, int mouseX, int mouseY, FontRenderer font) { + super.drawHoveringText(text, mouseX, mouseY, font); + } + + @Override + public int getGuiTop() { + return guiTop; + } + + @Override + public int getGuiLeft() { + return guiLeft; + } + + @Override + public int getXSize() { + return xSize; + } + + @Override + public FontRenderer getFontRenderer() { + return fontRendererObj; + } + + @Override + public RenderItem getItemRenderer() { + return itemRender; + } + + @Override + public void addToolTip(GT_GuiTooltip toolTip) { + mTooltipManager.addToolTip(toolTip); + } + + @Override + public boolean removeToolTip(GT_GuiTooltip toolTip) { + return mTooltipManager.removeToolTip(toolTip); + } + + @Override + protected void onMouseWheel(int mx, int my, int delta) { + if (mContainer.mTileEntity.getMetaTileEntity() instanceof IConfigurationCircuitSupport ccs) { + Slot slotCircuit = mContainer.getSlot(ccs.getCircuitGUISlot()); + if (slotCircuit != null + && func_146978_c(slotCircuit.xDisplayPosition, slotCircuit.yDisplayPosition, 16, 16, mx, my)) { + // emulate click + handleMouseClick(slotCircuit, -1, delta > 0 ? 1 : 0, 0); + return; + } + } + super.onMouseWheel(mx, my, delta); + } + + private void openSelectCircuitDialog() { + IMetaTileEntity machine = mContainer.mTileEntity.getMetaTileEntity(); + IConfigurationCircuitSupport ccs = (IConfigurationCircuitSupport) machine; + List<ItemStack> circuits = ccs.getConfigurationCircuits(); + mc.displayGuiScreen( + new GT_GUIDialogSelectItem( + StatCollector.translateToLocal("GT5U.machines.select_circuit"), + machine.getStackForm(0), + this, + this::onCircuitSelected, + circuits, + GT_Utility.findMatchingStackInList(circuits, machine.getStackInSlot(ccs.getCircuitSlot())))); + } + + private void onCircuitSelected(ItemStack selected) { + GT_Values.NW.sendToServer(new GT_Packet_SetConfigurationCircuit(mContainer.mTileEntity, selected)); + // we will not do any validation on client side + // it doesn't get to actually decide what inventory contains anyway + IConfigurationCircuitSupport ccs = (IConfigurationCircuitSupport) mContainer.mTileEntity.getMetaTileEntity(); + mContainer.mTileEntity.setInventorySlotContents(ccs.getCircuitSlot(), selected); + } +} diff --git a/src/main/java/gregtech/api/gui/GT_GUIContainer_1by1.java b/src/main/java/gregtech/api/gui/GT_GUIContainer_1by1.java new file mode 100644 index 0000000000..5bd44668c5 --- /dev/null +++ b/src/main/java/gregtech/api/gui/GT_GUIContainer_1by1.java @@ -0,0 +1,42 @@ +package gregtech.api.gui; + +import static gregtech.api.enums.Mods.GregTech; + +import net.minecraft.entity.player.InventoryPlayer; + +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; + +@Deprecated +public class GT_GUIContainer_1by1 extends GT_GUIContainerMetaTile_Machine { + + private final String mName; + private final int textColor = this.getTextColorOrDefault("title", 0x404040); + + public GT_GUIContainer_1by1(InventoryPlayer aInventoryPlayer, IGregTechTileEntity aTileEntity, String aName) { + super( + new GT_Container_1by1(aInventoryPlayer, aTileEntity), + GregTech.getResourcePath("textures", "gui", "1by1.png")); + mName = aName; + } + + public GT_GUIContainer_1by1(InventoryPlayer aInventoryPlayer, IGregTechTileEntity aTileEntity, String aName, + String aBackground) { + super( + new GT_Container_1by1(aInventoryPlayer, aTileEntity), + GregTech.getResourcePath("textures", "gui", aBackground + "1by1.png")); + mName = aName; + } + + @Override + protected void drawGuiContainerForegroundLayer(int mouseX, int mouseY) { + fontRendererObj.drawString(mName, 8, 4, textColor); + } + + @Override + protected void drawGuiContainerBackgroundLayer(float parTicks, int mouseX, int mouseY) { + super.drawGuiContainerBackgroundLayer(parTicks, mouseX, mouseY); + int x = (width - xSize) / 2; + int y = (height - ySize) / 2; + drawTexturedModalRect(x, y, 0, 0, xSize, ySize); + } +} diff --git a/src/main/java/gregtech/api/gui/GT_GUIContainer_2by2.java b/src/main/java/gregtech/api/gui/GT_GUIContainer_2by2.java new file mode 100644 index 0000000000..107bcc3859 --- /dev/null +++ b/src/main/java/gregtech/api/gui/GT_GUIContainer_2by2.java @@ -0,0 +1,42 @@ +package gregtech.api.gui; + +import static gregtech.api.enums.Mods.GregTech; + +import net.minecraft.entity.player.InventoryPlayer; + +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; + +@Deprecated +public class GT_GUIContainer_2by2 extends GT_GUIContainerMetaTile_Machine { + + private final String mName; + private final int textColor = this.getTextColorOrDefault("title", 0x404040); + + public GT_GUIContainer_2by2(InventoryPlayer aInventoryPlayer, IGregTechTileEntity aTileEntity, String aName) { + super( + new GT_Container_2by2(aInventoryPlayer, aTileEntity), + GregTech.getResourcePath("textures", "gui", "2by2.png")); + mName = aName; + } + + public GT_GUIContainer_2by2(InventoryPlayer aInventoryPlayer, IGregTechTileEntity aTileEntity, String aName, + String aBackground) { + super( + new GT_Container_2by2(aInventoryPlayer, aTileEntity), + GregTech.getResourcePath("textures", "gui", aBackground + "2by2.png")); + mName = aName; + } + + @Override + protected void drawGuiContainerForegroundLayer(int mouseX, int mouseY) { + fontRendererObj.drawString(mName, 8, 4, textColor); + } + + @Override + protected void drawGuiContainerBackgroundLayer(float parTicks, int mouseX, int mouseY) { + super.drawGuiContainerBackgroundLayer(parTicks, mouseX, mouseY); + int x = (width - xSize) / 2; + int y = (height - ySize) / 2; + drawTexturedModalRect(x, y, 0, 0, xSize, ySize); + } +} diff --git a/src/main/java/gregtech/api/gui/GT_GUIContainer_3by3.java b/src/main/java/gregtech/api/gui/GT_GUIContainer_3by3.java new file mode 100644 index 0000000000..0c8b63664a --- /dev/null +++ b/src/main/java/gregtech/api/gui/GT_GUIContainer_3by3.java @@ -0,0 +1,42 @@ +package gregtech.api.gui; + +import static gregtech.api.enums.Mods.GregTech; + +import net.minecraft.entity.player.InventoryPlayer; + +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; + +@Deprecated +public class GT_GUIContainer_3by3 extends GT_GUIContainerMetaTile_Machine { + + private final String mName; + private final int textColor = this.getTextColorOrDefault("title", 0x404040); + + public GT_GUIContainer_3by3(InventoryPlayer aInventoryPlayer, IGregTechTileEntity aTileEntity, String aName) { + super( + new GT_Container_3by3(aInventoryPlayer, aTileEntity), + GregTech.getResourcePath("textures", "gui", "3by3.png")); + mName = aName; + } + + public GT_GUIContainer_3by3(InventoryPlayer aInventoryPlayer, IGregTechTileEntity aTileEntity, String aName, + String aBackground) { + super( + new GT_Container_3by3(aInventoryPlayer, aTileEntity), + GregTech.getResourcePath("textures", "gui", aBackground + "3by3.png")); + mName = aName; + } + + @Override + protected void drawGuiContainerForegroundLayer(int mouseX, int mouseY) { + fontRendererObj.drawString(mName, 8, 4, textColor); + } + + @Override + protected void drawGuiContainerBackgroundLayer(float parTicks, int mouseX, int mouseY) { + super.drawGuiContainerBackgroundLayer(parTicks, mouseX, mouseY); + int x = (width - xSize) / 2; + int y = (height - ySize) / 2; + drawTexturedModalRect(x, y, 0, 0, xSize, ySize); + } +} diff --git a/src/main/java/gregtech/api/gui/GT_GUIContainer_4by4.java b/src/main/java/gregtech/api/gui/GT_GUIContainer_4by4.java new file mode 100644 index 0000000000..9e5d7f7155 --- /dev/null +++ b/src/main/java/gregtech/api/gui/GT_GUIContainer_4by4.java @@ -0,0 +1,42 @@ +package gregtech.api.gui; + +import static gregtech.api.enums.Mods.GregTech; + +import net.minecraft.entity.player.InventoryPlayer; + +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; + +@Deprecated +public class GT_GUIContainer_4by4 extends GT_GUIContainerMetaTile_Machine { + + private final String mName; + private final int textColor = this.getTextColorOrDefault("title", 0x404040); + + public GT_GUIContainer_4by4(InventoryPlayer aInventoryPlayer, IGregTechTileEntity aTileEntity, String aName) { + super( + new GT_Container_4by4(aInventoryPlayer, aTileEntity), + GregTech.getResourcePath("textures", "gui", "4by4.png")); + mName = aName; + } + + public GT_GUIContainer_4by4(InventoryPlayer aInventoryPlayer, IGregTechTileEntity aTileEntity, String aName, + String aBackground) { + super( + new GT_Container_4by4(aInventoryPlayer, aTileEntity), + GregTech.getResourcePath("textures", "gui", aBackground + "4by4.png")); + mName = aName; + } + + @Override + protected void drawGuiContainerForegroundLayer(int mouseX, int mouseY) { + fontRendererObj.drawString(mName, 8, 4, textColor); + } + + @Override + protected void drawGuiContainerBackgroundLayer(float parTicks, int mouseX, int mouseY) { + super.drawGuiContainerBackgroundLayer(parTicks, mouseX, mouseY); + int x = (width - xSize) / 2; + int y = (height - ySize) / 2; + drawTexturedModalRect(x, y, 0, 0, xSize, ySize); + } +} diff --git a/src/main/java/gregtech/api/gui/GT_GUIContainer_BasicTank.java b/src/main/java/gregtech/api/gui/GT_GUIContainer_BasicTank.java new file mode 100644 index 0000000000..54aa42d2a3 --- /dev/null +++ b/src/main/java/gregtech/api/gui/GT_GUIContainer_BasicTank.java @@ -0,0 +1,47 @@ +package gregtech.api.gui; + +import static gregtech.api.enums.Mods.GregTech; + +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.util.StatCollector; + +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.util.GT_Utility; + +public class GT_GUIContainer_BasicTank extends GT_GUIContainerMetaTile_Machine { + + private final String mName; + private final int textColor = this.getTextColorOrDefault("text", 0xFAFAFF), + textColorTitle = this.getTextColorOrDefault("title", 0x404040), + textColorValue = this.getTextColorOrDefault("value", 0xFAFAFF); + + public GT_GUIContainer_BasicTank(InventoryPlayer aInventoryPlayer, IGregTechTileEntity aTileEntity, String aName) { + super( + new GT_Container_BasicTank(aInventoryPlayer, aTileEntity), + GregTech.getResourcePath("textures", "gui", "BasicTank.png")); + mName = aName; + } + + @Override + protected void drawGuiContainerForegroundLayer(int mouseX, int mouseY) { + fontRendererObj + .drawString(StatCollector.translateToLocal("container.inventory"), 8, ySize - 96 + 2, textColorTitle); + fontRendererObj.drawString(mName, 8, 6, textColorTitle); + if (mContainer != null) { + fontRendererObj.drawString("Liquid Amount", 10, 20, textColor); + fontRendererObj.drawString( + GT_Utility.parseNumberToString(((GT_Container_BasicTank) mContainer).mContent), + 10, + 30, + textColorValue); + } + } + + @Override + protected void drawGuiContainerBackgroundLayer(float parTicks, int mouseX, int mouseY) { + super.drawGuiContainerBackgroundLayer(parTicks, mouseX, mouseY); + int x = (width - xSize) / 2; + int y = (height - ySize) / 2; + drawTexturedModalRect(x, y, 0, 0, xSize, ySize); + } +} diff --git a/src/main/java/gregtech/api/gui/GT_GUIContainer_MultiMachine.java b/src/main/java/gregtech/api/gui/GT_GUIContainer_MultiMachine.java new file mode 100644 index 0000000000..3d9515b19a --- /dev/null +++ b/src/main/java/gregtech/api/gui/GT_GUIContainer_MultiMachine.java @@ -0,0 +1,173 @@ +package gregtech.api.gui; + +import static gregtech.api.enums.Mods.GregTech; + +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.item.ItemStack; + +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.util.GT_ModHandler; +import gregtech.api.util.GT_Utility; +import gregtech.common.items.GT_MetaGenerated_Tool_01; +import gregtech.common.tileentities.machines.multi.GT_MetaTileEntity_DrillerBase; +import gregtech.common.tileentities.machines.multi.GT_MetaTileEntity_LargeTurbine; + +/** + * NEVER INCLUDE THIS FILE IN YOUR MOD!!! + * <p/> + * The GUI-Container I use for all my Basic Machines + */ +@Deprecated +public class GT_GUIContainer_MultiMachine extends GT_GUIContainerMetaTile_Machine { + + final String mName; + private final int textColor = this.getTextColorOrDefault("text", 0xFAFAFF), + textColorTitle = this.getTextColorOrDefault("title", 0xFAFAFF); + + public GT_GUIContainer_MultiMachine(InventoryPlayer aInventoryPlayer, IGregTechTileEntity aTileEntity, String aName, + String aTextureFile) { + super( + new GT_Container_MultiMachine(aInventoryPlayer, aTileEntity), + GregTech.getResourcePath( + "textures", + "gui", + "multimachines", + aTextureFile == null ? "MultiblockDisplay" : aTextureFile)); + mName = aName; + } + + @Override + protected void drawGuiContainerForegroundLayer(int mouseX, int mouseY) { + + // If text is drawn iterate down GUI 8 pixels (height of characters). + int line_counter = 7; + int max_chars_per_line = 26; + + if (mName.length() > 26) { + + // Split the machine name into an array, so we can try fit it on one line but if not use more. + String[] split = mName.split(" "); + + int total_line_length = 0; + StringBuilder current_line = new StringBuilder(); + + int index = 0; + + for (String str : split) { + + total_line_length += str.length(); + + if (total_line_length > max_chars_per_line) { + fontRendererObj.drawString(current_line.toString(), 10, line_counter, textColorTitle); + line_counter += 8; + current_line = new StringBuilder(); + index = 0; + total_line_length = str.length(); + } + + if (index == 0) { + current_line.append(str); + } else { + current_line.append(" ") + .append(str); + } + index++; + } + fontRendererObj.drawString(current_line.toString(), 10, line_counter, textColorTitle); + } else { + fontRendererObj.drawString(mName, 10, line_counter, textColorTitle); + } + line_counter += 8; + + if (mContainer != null) { // (mWrench ? 0 : 1) | (mScrewdriver ? 0 : 2) | (mSoftHammer ? 0 : 4) | (mHardHammer ? + // 0 : 8) + // | (mSolderingTool ? 0 : 16) | (mCrowbar ? 0 : 32) | (mMachine ? 0 : 64)); + if ((mContainer.mDisplayErrorCode & 1) != 0) { + fontRendererObj.drawString(GT_Utility.trans("132", "Pipe is loose."), 10, line_counter, textColor); + line_counter += 8; + } + + if ((mContainer.mDisplayErrorCode & 2) != 0) { + fontRendererObj.drawString(GT_Utility.trans("133", "Screws are loose."), 10, line_counter, textColor); + line_counter += 8; + } + + if ((mContainer.mDisplayErrorCode & 4) != 0) { + fontRendererObj.drawString(GT_Utility.trans("134", "Something is stuck."), 10, line_counter, textColor); + line_counter += 8; + } + + if ((mContainer.mDisplayErrorCode & 8) != 0) { + fontRendererObj + .drawString(GT_Utility.trans("135", "Platings are dented."), 10, line_counter, textColor); + line_counter += 8; + } + + if ((mContainer.mDisplayErrorCode & 16) != 0) { + fontRendererObj + .drawString(GT_Utility.trans("136", "Circuitry burned out."), 10, line_counter, textColor); + line_counter += 8; + } + + if ((mContainer.mDisplayErrorCode & 32) != 0) { + fontRendererObj + .drawString(GT_Utility.trans("137", "That doesn't belong there."), 10, line_counter, textColor); + line_counter += 8; + } + + if ((mContainer.mDisplayErrorCode & 64) != 0) { + fontRendererObj + .drawString(GT_Utility.trans("138", "Incomplete Structure."), 10, line_counter, textColor); + line_counter += 8; + } + + if (mContainer.mDisplayErrorCode == 0) { + if (mContainer.mActive == 0) { + fontRendererObj + .drawString(GT_Utility.trans("139", "Hit with Soft Mallet"), 10, line_counter, textColor); + line_counter += 8; + fontRendererObj + .drawString(GT_Utility.trans("140", "to (re-)start the Machine"), 10, line_counter, textColor); + line_counter += 8; + fontRendererObj + .drawString(GT_Utility.trans("141", "if it doesn't start."), 10, line_counter, textColor); + } else { + fontRendererObj + .drawString(GT_Utility.trans("142", "Running perfectly."), 10, line_counter, textColor); + } + line_counter += 8; + if (mContainer.mTileEntity.getMetaTileEntity() instanceof GT_MetaTileEntity_DrillerBase) { + ItemStack tItem = mContainer.mTileEntity.getMetaTileEntity() + .getStackInSlot(1); + if (tItem == null + || !GT_Utility.areStacksEqual(tItem, GT_ModHandler.getIC2Item("miningPipe", 1L))) { + fontRendererObj + .drawString(GT_Utility.trans("143", "Missing Mining Pipe"), 10, line_counter, textColor); + } + } else if (mContainer.mTileEntity.getMetaTileEntity() instanceof GT_MetaTileEntity_LargeTurbine) { + ItemStack tItem = mContainer.mTileEntity.getMetaTileEntity() + .getStackInSlot(1); + if (tItem == null + || !(tItem.getItem() == GT_MetaGenerated_Tool_01.INSTANCE && tItem.getItemDamage() >= 170 + && tItem.getItemDamage() <= 177)) { + fontRendererObj + .drawString(GT_Utility.trans("144", "Missing Turbine Rotor"), 10, line_counter, textColor); + } + } + } + } + } + + @Deprecated + public String trans(String aKey, String aEnglish) { + return GT_Utility.trans(aKey, aEnglish); + } + + @Override + protected void drawGuiContainerBackgroundLayer(float parTicks, int mouseX, int mouseY) { + super.drawGuiContainerBackgroundLayer(parTicks, mouseX, mouseY); + int x = (width - xSize) / 2; + int y = (height - ySize) / 2; + drawTexturedModalRect(x, y, 0, 0, xSize, ySize); + } +} diff --git a/src/main/java/gregtech/api/gui/GT_GUICover.java b/src/main/java/gregtech/api/gui/GT_GUICover.java new file mode 100644 index 0000000000..5729ada685 --- /dev/null +++ b/src/main/java/gregtech/api/gui/GT_GUICover.java @@ -0,0 +1,55 @@ +package gregtech.api.gui; + +import net.minecraft.item.ItemStack; + +import gregtech.api.enums.GT_Values; +import gregtech.api.interfaces.tileentity.ICoverable; +import gregtech.api.net.GT_Packet_GtTileEntityGuiRequest; + +@Deprecated +public abstract class GT_GUICover extends GT_GUIScreen { + + public final ICoverable tile; + public int parentGuiId = -1; + + public GT_GUICover(ICoverable tile, int width, int height, ItemStack cover) { + super(width, height, cover == null ? "" : cover.getDisplayName()); + this.tile = tile; + headerIcon.setItem(cover); + } + + @Override + public void updateScreen() { + super.updateScreen(); + if (!tile.isUseableByPlayer(mc.thePlayer)) { + closeScreen(); + } + } + + /** + * The parent GUI to exit to. -1 is ignored. + * + * @param parentGuiId parent GUI ID + */ + public void setParentGuiId(int parentGuiId) { + this.parentGuiId = parentGuiId; + } + + @Override + public void closeScreen() { + // If this cover was given a guiId, tell the server to open it for us when this GUI closes. + if (parentGuiId != -1 && tile.isUseableByPlayer(mc.thePlayer)) { + GT_Values.NW.sendToServer( + new GT_Packet_GtTileEntityGuiRequest( + tile.getXCoord(), + tile.getYCoord(), + tile.getZCoord(), + parentGuiId, + tile.getWorld().provider.dimensionId, + mc.thePlayer.getEntityId())); + } else { + this.mc.displayGuiScreen(null); + this.mc.setIngameFocus(); + } + } +} diff --git a/src/main/java/gregtech/api/gui/GT_GUIDialogSelectItem.java b/src/main/java/gregtech/api/gui/GT_GUIDialogSelectItem.java new file mode 100644 index 0000000000..03a6fb2a70 --- /dev/null +++ b/src/main/java/gregtech/api/gui/GT_GUIDialogSelectItem.java @@ -0,0 +1,229 @@ +package gregtech.api.gui; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.item.ItemStack; +import net.minecraft.util.StatCollector; + +import gregtech.api.gui.widgets.GT_GuiFakeItemButton; +import gregtech.api.gui.widgets.GT_GuiIcon; +import gregtech.api.gui.widgets.GT_GuiIconButton; +import gregtech.api.util.GT_Utility; + +@Deprecated +public class GT_GUIDialogSelectItem extends GT_GUIScreen { + + public static final int UNSELECTED = -1; + private static final int cols = 9; + private static final int rows = 3; + private final int textColor = this.getTextColorOrDefault("text", 0xff555555); + private final GuiScreen parent; + private final Consumer<ItemStack> selectedCallback; + // passed in stack + private final List<ItemStack> stacks; + // all slots not including btnCurrent + private final List<GT_GuiFakeItemButton> slots = new ArrayList<>(); + // the currently selected slot content + private final GT_GuiFakeItemButton btnCurrent = new GT_GuiFakeItemButton(this, 8, 25, GT_GuiIcon.SLOT_DARKGRAY) + .setMimicSlot(true); + private final boolean noDeselect; + private int selected; + private int scroll = 0; + private GT_GuiIconButton btnUp; + private GT_GuiIconButton btnDown; + + public GT_GUIDialogSelectItem(String header, ItemStack headerItem, GuiScreen parent, + Consumer<ItemStack> selectedCallback, List<ItemStack> stacks) { + this(header, headerItem, parent, selectedCallback, stacks, UNSELECTED); + } + + public GT_GUIDialogSelectItem(String header, ItemStack headerItem, GuiScreen parent, + Consumer<ItemStack> selectedCallback, List<ItemStack> stacks, int selected) { + this(header, headerItem, parent, selectedCallback, stacks, selected, false); + } + + /** + * Open a dialog to select an item from given list. Given callback may be called zero or more times depending on + * user action. + * + * @param header Header text + * @param headerItem ItemStack to use as Dialog icon + * @param parent open which GUIScreen when this dialog is closed. use null if it has no parent. + * @param selectedCallback callback upon selected + * @param stacks list to choose from + * @param selected preselected item. Use {@link #UNSELECTED} for unselected. Invalid selected will be + * clamped to 0 or highest index + * @param noDeselect true if player cannot deselect, false otherwise. If this is set to true, selectedCallback + * is guaranteed to be called with a nonnull stack + */ + public GT_GUIDialogSelectItem(String header, ItemStack headerItem, GuiScreen parent, + Consumer<ItemStack> selectedCallback, List<ItemStack> stacks, int selected, boolean noDeselect) { + super(176, 107, header); + this.noDeselect = noDeselect; + if (headerItem != null) this.headerIcon.setItem(headerItem); + this.parent = parent; + this.selectedCallback = selectedCallback; + this.stacks = stacks; + + if (stacks.size() > rows * cols) { + btnUp = new GT_GuiIconButton(this, 0, 134, 25, GT_GuiIcon.GREEN_ARROW_UP); + btnDown = new GT_GuiIconButton(this, 1, 152, 25, GT_GuiIcon.GREEN_ARROW_DOWN); + } + + for (int i = 0; i < rows; i++) { + for (int j = 0; j < cols; j++) { + slots.add( + new GT_GuiFakeItemButton(this, 8 + 18 * j, 44 + 18 * i, GT_GuiIcon.SLOT_GRAY).setMimicSlot(true)); + } + } + + setSelected(noDeselect ? Math.max(0, selected) : selected); + ensureSelectedDisplayed(); + } + + @Override + protected void onInitGui(int guiLeft, int guiTop, int gui_width, int gui_height) { + btnCurrent + .setX(8 + 2 + fontRendererObj.getStringWidth(StatCollector.translateToLocal("GT5U.gui.select.current"))); + } + + @Override + public void closeScreen() { + selectedCallback.accept(getCandidate(getSelected())); + mc.displayGuiScreen(parent); + if (parent == null) mc.setIngameFocus(); + } + + @Override + public void buttonClicked(GuiButton button) { + switch (button.id) { + case 0 -> { + setScroll(scroll - 1); + return; + } + case 1 -> { + setScroll(scroll + 1); + return; + } + } + super.buttonClicked(button); + } + + @Override + public void drawExtras(int mouseX, int mouseY, float parTicks) { + int y = 25 + (18 - getFontRenderer().FONT_HEIGHT) / 2; + getFontRenderer().drawString(StatCollector.translateToLocal("GT5U.gui.select.current"), 8, y, textColor); + super.drawExtras(mouseX, mouseY, parTicks); + } + + @Override + public void mouseClicked(int x, int y, int button) { + int mx = x - guiLeft, my = y - guiTop; + if (button == 0) { + if (btnCurrent.getBounds() + .contains(mx, my)) { + ensureSelectedDisplayed(); + return; + } + + for (int i = 0, slotsSize = slots.size(); i < slotsSize; i++) { + GT_GuiFakeItemButton slot = slots.get(i); + if (slot.getBounds() + .contains(mx, my)) { + setSelected(slotIndexToListIndex(i)); + return; + } + } + } else if (button == 1 && getSelected() >= 0) { + if (btnCurrent.getBounds() + .contains(mx, my)) { + setSelected(UNSELECTED); + return; + } + GT_GuiFakeItemButton slot = getSlot(listIndexToSlotIndex(getSelected())); + if (slot != null && slot.getBounds() + .contains(mx, my)) { + setSelected(UNSELECTED); + } + } + super.mouseClicked(x, y, button); + } + + @Override + public void onMouseWheel(int x, int y, int delta) { + if (delta < 0) setScroll(scroll + 1); + else if (delta > 0) setScroll(scroll - 1); + } + + private void fillSlots() { + for (int i = 0, j = scroll * cols; i < slots.size(); i++, j++) { + slots.get(i) + .setItem(getCandidate(j)) + .setBgIcon(j == getSelected() ? GT_GuiIcon.SLOT_DARKGRAY : GT_GuiIcon.SLOT_GRAY); + } + } + + private void ensureSelectedDisplayed() { + if (getSelected() < scroll * cols) { + setScroll(getSelected() / cols); + } else if (getSelected() > (scroll + rows) * cols) { + setScroll((getSelected() - (rows - 1) * cols) / cols); + } else { + // called nonetheless to update button enabled states + setScroll(scroll); + } + } + + private int slotIndexToListIndex(int index) { + int mapped = scroll * cols + index; + return mapped >= stacks.size() ? UNSELECTED : mapped; + } + + private int listIndexToSlotIndex(int index) { + return index - scroll * cols; + } + + public int getSelected() { + return selected; + } + + public void setSelected(int selected) { + if (selected == this.selected) return; + int newSelected = GT_Utility.clamp(selected, UNSELECTED, stacks.size() - 1); + + if (noDeselect && newSelected == UNSELECTED) return; + + GT_GuiFakeItemButton selectedSlot = getSlot(this.selected); + if (selectedSlot != null) selectedSlot.setBgIcon(GT_GuiIcon.SLOT_GRAY); + + this.selected = newSelected; + + btnCurrent.setItem(getCandidate(this.selected)); + + selectedSlot = getSlot(this.selected); + if (selectedSlot != null) selectedSlot.setBgIcon(GT_GuiIcon.SLOT_DARKGRAY); + } + + private void setScroll(int scroll) { + if (stacks.size() > rows * cols) { + int lo = 0; + int hi = (stacks.size() - rows * cols) / cols + 1; + this.scroll = GT_Utility.clamp(scroll, lo, hi); + btnUp.enabled = this.scroll != lo; + btnDown.enabled = this.scroll != hi; + } + fillSlots(); + } + + private ItemStack getCandidate(int listIndex) { + return listIndex < 0 || listIndex >= stacks.size() ? null : stacks.get(listIndex); + } + + private GT_GuiFakeItemButton getSlot(int slotIndex) { + return slotIndex < 0 || slotIndex >= slots.size() ? null : slots.get(slotIndex); + } +} diff --git a/src/main/java/gregtech/api/gui/GT_GUIScreen.java b/src/main/java/gregtech/api/gui/GT_GUIScreen.java new file mode 100644 index 0000000000..2ff2973792 --- /dev/null +++ b/src/main/java/gregtech/api/gui/GT_GUIScreen.java @@ -0,0 +1,327 @@ +package gregtech.api.gui; + +import java.util.ArrayList; +import java.util.List; + +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.gui.GuiTextField; +import net.minecraft.client.renderer.RenderHelper; +import net.minecraft.client.renderer.entity.RenderItem; +import net.minecraft.util.ResourceLocation; + +import org.lwjgl.input.Mouse; +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL12; + +import gregtech.api.enums.Dyes; +import gregtech.api.gui.widgets.GT_GuiFakeItemButton; +import gregtech.api.gui.widgets.GT_GuiIntegerTextBox; +import gregtech.api.gui.widgets.GT_GuiTooltip; +import gregtech.api.gui.widgets.GT_GuiTooltipManager; +import gregtech.api.gui.widgets.GT_GuiTooltipManager.GT_IToolTipRenderer; +import gregtech.api.interfaces.IGuiScreen; + +@Deprecated +public abstract class GT_GUIScreen extends GuiScreen implements GT_IToolTipRenderer, IGuiScreen { + + protected final GT_GuiTooltipManager ttManager = new GT_GuiTooltipManager(); + + protected int gui_width = 176; + protected int gui_height = 107; + protected int guiTop, guiLeft; + protected final boolean drawButtons = true; + protected final ResourceLocation mGUIbackgroundLocation; + + private GuiButton selectedButton; + private final GT_GUIColorOverride colorOverride; + private final int textColor; + private static final String guiTexturePath = "gregtech:textures/gui/GuiCover.png"; + + public String header; + public GT_GuiFakeItemButton headerIcon; + + protected final List<IGuiElement> elements = new ArrayList<>(); + protected final List<GT_GuiIntegerTextBox> textBoxes = new ArrayList<>(); + + public GT_GUIScreen(int width, int height, String header) { + this.gui_width = width; + this.gui_height = height; + this.header = header; + this.headerIcon = new GT_GuiFakeItemButton(this, 5, 5, null); + this.mGUIbackgroundLocation = new ResourceLocation(guiTexturePath); + this.colorOverride = GT_GUIColorOverride.get(guiTexturePath); + this.textColor = getTextColorOrDefault("title", 0xFF222222); + } + + @Override + public void initGui() { + guiLeft = (this.width - this.gui_width) / 2; + guiTop = (this.height - this.gui_height) / 2; + + for (IGuiElement element : elements) { + if (element instanceof GuiButton button) buttonList.add(button); + if (element instanceof GT_GuiIntegerTextBox) textBoxes.add((GT_GuiIntegerTextBox) element); + } + + onInitGui(guiLeft, guiTop, gui_width, gui_height); + + for (IGuiElement element : elements) { + element.onInit(); + } + super.initGui(); + } + + protected abstract void onInitGui(int guiLeft, int guiTop, int gui_width, int gui_height); + + protected int getTextColorOrDefault(String textType, int defaultColor) { + return colorOverride.getTextColorOrDefault(textType, defaultColor); + } + + public void onMouseWheel(int x, int y, int delta) {} + + @Override + public void handleMouseInput() { + int delta = Mouse.getEventDWheel(); + if (delta != 0) { + int i = Mouse.getEventX() * this.width / this.mc.displayWidth; + int j = this.height - Mouse.getEventY() * this.height / this.mc.displayHeight - 1; + onMouseWheel(i, j, delta); + } + super.handleMouseInput(); + } + + @Override + public void drawScreen(int mouseX, int mouseY, float parTicks) { + drawDefaultBackground(); + + drawBackground(mouseX, mouseY, parTicks); + + RenderHelper.disableStandardItemLighting(); + GL11.glDisable(GL11.GL_LIGHTING); + GL11.glDisable(GL11.GL_DEPTH_TEST); + GL11.glDisable(GL12.GL_RESCALE_NORMAL); + if (drawButtons) { + RenderHelper.enableGUIStandardItemLighting(); + for (IGuiElement e : elements) e.draw(mouseX, mouseY, parTicks); + } + GL11.glEnable(GL12.GL_RESCALE_NORMAL); + + GL11.glPushMatrix(); + GL11.glTranslatef(guiLeft, guiTop, 0.0F); + GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); + + GL11.glDisable(GL11.GL_LIGHTING); + drawForegroundLayer(mouseX, mouseY, parTicks); + GL11.glEnable(GL11.GL_LIGHTING); + + GL11.glPopMatrix(); + + GL11.glEnable(GL11.GL_LIGHTING); + GL11.glEnable(GL11.GL_DEPTH_TEST); + RenderHelper.enableStandardItemLighting(); + } + + public void drawForegroundLayer(int mouseX, int mouseY, float parTicks) { + drawExtras(mouseX, mouseY, parTicks); + ttManager.onTick(this, mouseX, mouseY); + } + + public void drawBackground(int mouseX, int mouseY, float parTicks) { + short[] color = Dyes.MACHINE_METAL.getRGBA(); + GL11.glColor3ub((byte) color[0], (byte) color[1], (byte) color[2]); + this.mc.renderEngine.bindTexture(mGUIbackgroundLocation); + drawTexturedModalRect(guiLeft, guiTop, 0, 0, gui_width, gui_height); + } + + public void drawExtras(int mouseX, int mouseY, float parTicks) { + this.fontRendererObj.drawString(header, 25, 9, textColor); + } + + @Override + public boolean doesGuiPauseGame() { + return false; + } + + public void closeScreen() { + this.mc.displayGuiScreen(null); + this.mc.setIngameFocus(); + } + + @Override + public void updateScreen() { + super.updateScreen(); + for (GuiTextField f : textBoxes) { + f.updateCursorCounter(); + } + } + + @Override + public void mouseClicked(int x, int y, int button) { + for (GT_GuiIntegerTextBox tBox : textBoxes) { + boolean hadFocus = tBox.isFocused(); + if (tBox.isEnabled() || hadFocus) tBox.mouseClicked(x, y, button); + + if (tBox.isFocused() && button == 1 && tBox.isEnabled()) // rightclick -> lcear it + tBox.setText("0"); + else if (hadFocus && !tBox.isFocused()) applyTextBox(tBox); + } + super.mouseClicked(x, y, button); + } + + @Override + public void keyTyped(char c, int key) { + GT_GuiIntegerTextBox focusedTextBox = null; + for (GT_GuiIntegerTextBox textBox : textBoxes) { + if (textBox.isFocused()) focusedTextBox = textBox; + } + + if (key == 1) { // esc + if (focusedTextBox != null) { + resetTextBox(focusedTextBox); + setFocusedTextBox(null); + } else { + closeScreen(); + // don't fall through to parent + } + return; + } + + if (c == '\t') { // tab + for (int i = 0; i < textBoxes.size(); i++) { + GT_GuiIntegerTextBox box = textBoxes.get(i); + if (box.isFocused()) { + applyTextBox(box); + setFocusedTextBox(((i + 1) < textBoxes.size()) ? textBoxes.get(i + 1) : null); + return; + } + } + if (!textBoxes.isEmpty()) setFocusedTextBox(textBoxes.get(0)); + return; + } + + if (focusedTextBox != null && focusedTextBox.textboxKeyTyped(c, key)) { + return; + } + + if (key == 28 && focusedTextBox != null) { // enter + applyTextBox(focusedTextBox); + setFocusedTextBox(null); + return; + } + + if (key == this.mc.gameSettings.keyBindInventory.getKeyCode()) { + if (focusedTextBox != null) { + applyTextBox(focusedTextBox); + setFocusedTextBox(null); + return; + } + closeScreen(); + return; + } + super.keyTyped(c, key); + } + + /** + * Button + */ + @Override + public void actionPerformed(GuiButton button) { + selectedButton = button; + } + + @Override + public void clearSelectedButton() { + selectedButton = null; + } + + @Override + public GuiButton getSelectedButton() { + return selectedButton; + } + + @Override + public void buttonClicked(GuiButton button) {} + + /** + * TextBoxes + */ + private void setFocusedTextBox(GT_GuiIntegerTextBox boxToFocus) { + for (GT_GuiIntegerTextBox textBox : textBoxes) { + textBox.setFocused(textBox.equals(boxToFocus) && textBox.isEnabled()); + } + } + + /** + * Given textbox's value might have changed. + */ + public void applyTextBox(GT_GuiIntegerTextBox box) {} + + /** + * Reset the given textbox to the last valid value, <b>NOT</b> 0. + */ + public void resetTextBox(GT_GuiIntegerTextBox box) {} + + /** + * GT_IToolTipRenderer + */ + @Override + public void drawHoveringText(List<String> text, int mouseX, int mouseY, FontRenderer render) { + super.drawHoveringText(text, mouseX, mouseY, render); + } + + @Override + public FontRenderer getFontRenderer() { + return super.fontRendererObj; + } + + @Override + public void addToolTip(GT_GuiTooltip toolTip) { + ttManager.addToolTip(toolTip); + } + + @Override + public boolean removeToolTip(GT_GuiTooltip toolTip) { + return ttManager.removeToolTip(toolTip); + } + + /** + * Junk + */ + @Override + public int getGuiTop() { + return guiTop; + } + + @Override + public int getGuiLeft() { + return guiLeft; + } + + @Override + public int getXSize() { + return gui_width; + } + + @Override + public int getYSize() { + return gui_height; + } + + @Override + public RenderItem getItemRenderer() { + return itemRender; + } + + @Override + public void addElement(IGuiElement element) { + if (elements.contains(element)) return; + elements.add(element); + } + + @Override + public boolean removeElement(IGuiElement element) { + return elements.remove(element); + } +} diff --git a/src/main/java/gregtech/api/gui/GT_Slot_Armor.java b/src/main/java/gregtech/api/gui/GT_Slot_Armor.java new file mode 100644 index 0000000000..1c48b01430 --- /dev/null +++ b/src/main/java/gregtech/api/gui/GT_Slot_Armor.java @@ -0,0 +1,30 @@ +package gregtech.api.gui; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.IInventory; +import net.minecraft.inventory.Slot; +import net.minecraft.item.ItemStack; + +public class GT_Slot_Armor extends Slot { + + final int mArmorType; + final EntityPlayer mPlayer; + + public GT_Slot_Armor(IInventory inventory, int slotIndex, int xPos, int yPos, int armorType, EntityPlayer aPlayer) { + super(inventory, slotIndex, xPos, yPos); + mArmorType = armorType; + mPlayer = aPlayer; + } + + @Override + public int getSlotStackLimit() { + return 1; + } + + @Override + public boolean isItemValid(ItemStack aStack) { + return aStack != null && aStack.getItem() != null + && aStack.getItem() + .isValidArmor(aStack, mArmorType, mPlayer); + } +} diff --git a/src/main/java/gregtech/api/gui/GT_Slot_DataOrb.java b/src/main/java/gregtech/api/gui/GT_Slot_DataOrb.java new file mode 100644 index 0000000000..115b50ddb8 --- /dev/null +++ b/src/main/java/gregtech/api/gui/GT_Slot_DataOrb.java @@ -0,0 +1,19 @@ +package gregtech.api.gui; + +import net.minecraft.inventory.IInventory; +import net.minecraft.inventory.Slot; +import net.minecraft.item.ItemStack; + +import gregtech.api.enums.ItemList; + +public class GT_Slot_DataOrb extends Slot { + + public GT_Slot_DataOrb(IInventory inventory, int slotIndex, int xPos, int yPos) { + super(inventory, slotIndex, xPos, yPos); + } + + @Override + public boolean isItemValid(ItemStack aStack) { + return ItemList.Tool_DataOrb.isStackEqual(aStack, false, true); + } +} diff --git a/src/main/java/gregtech/api/gui/GT_Slot_Holo.java b/src/main/java/gregtech/api/gui/GT_Slot_Holo.java new file mode 100644 index 0000000000..9b7b75f0b2 --- /dev/null +++ b/src/main/java/gregtech/api/gui/GT_Slot_Holo.java @@ -0,0 +1,77 @@ +package gregtech.api.gui; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.IInventory; +import net.minecraft.inventory.Slot; +import net.minecraft.item.ItemStack; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; + +public class GT_Slot_Holo extends Slot { + + public final int mSlotIndex; + public boolean mEnabled = true; + public boolean mCanInsertItem, mCanStackItem; + public int mMaxStacksize = 127; + + public GT_Slot_Holo(IInventory inventory, int slotIndex, int xPos, int yPos, boolean aCanInsertItem, + boolean aCanStackItem, int aMaxStacksize) { + super(inventory, slotIndex, xPos, yPos); + mCanInsertItem = aCanInsertItem; + mCanStackItem = aCanStackItem; + mMaxStacksize = aMaxStacksize; + mSlotIndex = slotIndex; + } + + @Override + public boolean isItemValid(ItemStack itemStack) { + return mCanInsertItem; + } + + @Override + public int getSlotStackLimit() { + return mMaxStacksize; + } + + @Override + public boolean getHasStack() { + return false; + } + + @Override + public ItemStack decrStackSize(int amount) { + if (!mCanStackItem) return null; + return super.decrStackSize(amount); + } + + @Override + public boolean canTakeStack(EntityPlayer player) { + return false; + } + + /** + * Sets if this slot should be ignored in event-processing. For example, highlight the slot on mouseOver. + * + * @param enabled if the slot should be enabled + */ + public void setEnabled(boolean enabled) { + mEnabled = enabled; + } + + /** + * Use this value to determine whether to ignore this slot in event processing + */ + public boolean isEnabled() { + return mEnabled; + } + + /** + * This function controls whether to highlight the slot on mouseOver. + */ + @Override + @SideOnly(Side.CLIENT) + public boolean func_111238_b() { + return isEnabled(); + } +} diff --git a/src/main/java/gregtech/api/gui/GT_Slot_Output.java b/src/main/java/gregtech/api/gui/GT_Slot_Output.java new file mode 100644 index 0000000000..7c883ea2d1 --- /dev/null +++ b/src/main/java/gregtech/api/gui/GT_Slot_Output.java @@ -0,0 +1,17 @@ +package gregtech.api.gui; + +import net.minecraft.inventory.IInventory; +import net.minecraft.inventory.Slot; +import net.minecraft.item.ItemStack; + +public class GT_Slot_Output extends Slot { + + public GT_Slot_Output(IInventory inventory, int slotIndex, int xPos, int yPos) { + super(inventory, slotIndex, xPos, yPos); + } + + @Override + public boolean isItemValid(ItemStack itemStack) { + return false; + } +} diff --git a/src/main/java/gregtech/api/gui/GT_Slot_Render.java b/src/main/java/gregtech/api/gui/GT_Slot_Render.java new file mode 100644 index 0000000000..ae03ce83ea --- /dev/null +++ b/src/main/java/gregtech/api/gui/GT_Slot_Render.java @@ -0,0 +1,24 @@ +package gregtech.api.gui; + +import net.minecraft.inventory.IInventory; +import net.minecraft.item.ItemStack; +import net.minecraft.tileentity.TileEntity; + +public class GT_Slot_Render extends GT_Slot_Holo { + + public GT_Slot_Render(IInventory inventory, int slotIndex, int xPos, int yPos) { + super(inventory, slotIndex, xPos, yPos, false, false, 0); + } + + /** + * NEI has a nice and "useful" Delete-All Function, which would delete the Content of this Slot. This is here to + * prevent that. + */ + @Override + public void putStack(ItemStack aStack) { + if (inventory instanceof TileEntity && ((TileEntity) inventory).getWorldObj().isRemote) { + inventory.setInventorySlotContents(getSlotIndex(), aStack); + } + onSlotChanged(); + } +} diff --git a/src/main/java/gregtech/api/gui/GUIHost.java b/src/main/java/gregtech/api/gui/GUIHost.java new file mode 100644 index 0000000000..bbb94317c4 --- /dev/null +++ b/src/main/java/gregtech/api/gui/GUIHost.java @@ -0,0 +1,56 @@ +package gregtech.api.gui; + +import java.util.Objects; + +import javax.annotation.Nonnull; + +import net.minecraft.item.ItemStack; + +import com.gtnewhorizons.modularui.api.screen.ITileWithModularUI; +import com.gtnewhorizons.modularui.api.screen.ModularWindow; +import com.gtnewhorizons.modularui.api.screen.UIBuildContext; + +public interface GUIHost extends ITileWithModularUI { + + @Nonnull + @Override + default ModularWindow createWindow(UIBuildContext uiContext) { + Objects.requireNonNull(uiContext); + GUIProvider<?> gui = getGUI(uiContext); + return gui.openGUI(uiContext); + } + + /** + * Width of the GUI when its being displayed + */ + default int getWidth() { + return 170; + } + + default int getHeight() { + return 192; + } + + @Nonnull + GUIProvider<?> getGUI(@Nonnull UIBuildContext uiContext); + + ItemStack getAsItem(); + + String getMachineName(); + + default boolean hasItemInput() { + return true; + } + + default boolean hasItemOutput() { + return true; + } + + default boolean hasFluidInput() { + return true; + } + + default boolean hasFluidOutput() { + return true; + } +} diff --git a/src/main/java/gregtech/api/gui/GUIProvider.java b/src/main/java/gregtech/api/gui/GUIProvider.java new file mode 100644 index 0000000000..6fec4aa52a --- /dev/null +++ b/src/main/java/gregtech/api/gui/GUIProvider.java @@ -0,0 +1,38 @@ +package gregtech.api.gui; + +import java.util.Objects; + +import javax.annotation.Nonnull; + +import com.gtnewhorizons.modularui.api.screen.ModularWindow; +import com.gtnewhorizons.modularui.api.screen.ModularWindow.Builder; +import com.gtnewhorizons.modularui.api.screen.UIBuildContext; + +public abstract class GUIProvider<T extends GUIHost> { + + @Nonnull + protected final T host; + + public GUIProvider(@Nonnull T host) { + this.host = host; + } + + @Nonnull + public ModularWindow openGUI(@Nonnull UIBuildContext uiContext) { + Builder builder = Objects.requireNonNull(ModularWindow.builder(host.getWidth(), host.getHeight())); + if (shouldBindPlayerInventory()) { + builder.bindPlayerInventory(uiContext.getPlayer()); + } + attachSynchHandlers(builder, uiContext); + addWidgets(builder, uiContext); + return Objects.requireNonNull(builder.build()); + } + + protected abstract void attachSynchHandlers(@Nonnull Builder builder, @Nonnull UIBuildContext uiContext); + + protected abstract void addWidgets(@Nonnull Builder builder, @Nonnull UIBuildContext uiContext); + + protected boolean shouldBindPlayerInventory() { + return true; + } +} diff --git a/src/main/java/gregtech/api/gui/modularui/FallbackableSteamTexture.java b/src/main/java/gregtech/api/gui/modularui/FallbackableSteamTexture.java new file mode 100644 index 0000000000..8de4bc4536 --- /dev/null +++ b/src/main/java/gregtech/api/gui/modularui/FallbackableSteamTexture.java @@ -0,0 +1,89 @@ +package gregtech.api.gui.modularui; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import net.minecraft.client.Minecraft; + +import com.gtnewhorizons.modularui.api.drawable.UITexture; +import com.gtnewhorizons.modularui.common.internal.network.NetworkUtils; + +import gregtech.api.enums.SteamVariant; + +public class FallbackableSteamTexture { + + private final SteamTexture candidate; + private final Object fallback; + private final Map<SteamVariant, Boolean> useFallbackMap = new HashMap<>(); + + private static final List<FallbackableSteamTexture> ALL_INSTANCES = new ArrayList<>(); + + public FallbackableSteamTexture(SteamTexture candidate, SteamTexture fallback) { + this(candidate, (Object) fallback); + } + + public FallbackableSteamTexture(SteamTexture candidate, FallbackableSteamTexture fallback) { + this(candidate, (Object) fallback); + } + + public FallbackableSteamTexture(SteamTexture fallback) { + this(null, fallback); + } + + private FallbackableSteamTexture(SteamTexture candidate, Object fallback) { + this.candidate = candidate; + this.fallback = fallback; + ALL_INSTANCES.add(this); + } + + public UITexture get(SteamVariant steamVariant) { + verifyCandidate(steamVariant); + if (useFallbackMap.get(steamVariant)) { + return castFallback(steamVariant); + } else { + return candidate.get(steamVariant); + } + } + + private void verifyCandidate(SteamVariant steamVariant) { + if (useFallbackMap.get(steamVariant) == null) { + boolean useFallback; + if (NetworkUtils.isDedicatedClient()) { + if (candidate == null) { + useFallback = true; + } else { + try { + Minecraft.getMinecraft() + .getResourceManager() + .getResource(candidate.get(steamVariant).location); + useFallback = false; + } catch (IOException e) { + useFallback = true; + } + } + } else { + useFallback = true; + } + useFallbackMap.put(steamVariant, useFallback); + } + } + + private UITexture castFallback(SteamVariant steamVariant) { + if (fallback instanceof SteamTexture) { + return ((SteamTexture) fallback).get(steamVariant); + } else if (fallback instanceof FallbackableSteamTexture) { + return ((FallbackableSteamTexture) fallback).get(steamVariant); + } else { + throw new RuntimeException("Unexpected type found for fallback: " + fallback.getClass()); + } + } + + public static void reload() { + for (FallbackableSteamTexture t : ALL_INSTANCES) { + t.useFallbackMap.clear(); + } + } +} diff --git a/src/main/java/gregtech/api/gui/modularui/GT_CoverUIBuildContext.java b/src/main/java/gregtech/api/gui/modularui/GT_CoverUIBuildContext.java new file mode 100644 index 0000000000..f98d6099fc --- /dev/null +++ b/src/main/java/gregtech/api/gui/modularui/GT_CoverUIBuildContext.java @@ -0,0 +1,74 @@ +package gregtech.api.gui.modularui; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraftforge.common.util.ForgeDirection; + +import com.gtnewhorizons.modularui.api.screen.UIBuildContext; + +import gregtech.api.interfaces.tileentity.ICoverable; + +public class GT_CoverUIBuildContext extends UIBuildContext { + + // cover data is not synced to client, while ID is + private final int coverID; + private final ForgeDirection side; + private final ICoverable tile; + private final boolean anotherWindow; + private final int guiColorization; + + /** + * @param player Player opened this UI + * @param coverID See {@link ICoverable#getCoverIDAtSide} + * @param side Side this cover is attached to + * @param tile Tile this cover is attached to + * @param anotherWindow If cover UI is shown on top of another window + * @param guiColorization The color used to render machine's GUI + */ + public GT_CoverUIBuildContext(EntityPlayer player, int coverID, ForgeDirection side, ICoverable tile, + boolean anotherWindow, int guiColorization) { + super(player); + this.coverID = coverID; + this.side = side; + this.tile = tile; + this.anotherWindow = anotherWindow; + this.guiColorization = guiColorization; + } + + /** + * @param player Player opened this UI + * @param coverID See {@link ICoverable#getCoverIDAtSide} + * @param side Side this cover is attached to + * @param tile Tile this cover is attached to + * @param anotherWindow If cover GUI is shown in opened on top of another window + */ + public GT_CoverUIBuildContext(EntityPlayer player, int coverID, ForgeDirection side, ICoverable tile, + boolean anotherWindow) { + this(player, coverID, side, tile, anotherWindow, tile.getGUIColorization()); + } + + public int getCoverID() { + return coverID; + } + + public ForgeDirection getCoverSide() { + return side; + } + + /** + * Note that this will return different object between client v.s. server side on SP. + */ + public ICoverable getTile() { + return tile; + } + + /** + * If cover GUI is shown in opened on top of another window. + */ + public boolean isAnotherWindow() { + return anotherWindow; + } + + public int getGuiColorization() { + return guiColorization; + } +} diff --git a/src/main/java/gregtech/api/gui/modularui/GT_UIInfos.java b/src/main/java/gregtech/api/gui/modularui/GT_UIInfos.java new file mode 100644 index 0000000000..89a0835f13 --- /dev/null +++ b/src/main/java/gregtech/api/gui/modularui/GT_UIInfos.java @@ -0,0 +1,188 @@ +package gregtech.api.gui.modularui; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.tileentity.TileEntity; +import net.minecraftforge.common.util.ForgeDirection; + +import com.gtnewhorizons.modularui.api.UIInfos; +import com.gtnewhorizons.modularui.api.screen.ITileWithModularUI; +import com.gtnewhorizons.modularui.api.screen.ModularUIContext; +import com.gtnewhorizons.modularui.api.screen.ModularWindow; +import com.gtnewhorizons.modularui.api.screen.UIBuildContext; +import com.gtnewhorizons.modularui.common.builder.UIBuilder; +import com.gtnewhorizons.modularui.common.builder.UIInfo; +import com.gtnewhorizons.modularui.common.internal.network.NetworkUtils; +import com.gtnewhorizons.modularui.common.internal.wrapper.ModularGui; +import com.gtnewhorizons.modularui.common.internal.wrapper.ModularUIContainer; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import gregtech.api.enums.GT_Values; +import gregtech.api.interfaces.tileentity.ICoverable; +import gregtech.api.interfaces.tileentity.IHasWorldObjectAndCoords; +import gregtech.api.net.GT_Packet_SendCoverData; +import gregtech.api.util.GT_CoverBehaviorBase; + +public class GT_UIInfos { + + public static void init() {} + + /** + * Generator for {@link UIInfo} which is responsible for registering and opening UIs. Unlike + * {@link com.gtnewhorizons.modularui.api.UIInfos#TILE_MODULAR_UI}, this accepts custom constructors for UI. <br> + * Do NOT run {@link UIBuilder#build} on-the-fly, otherwise MP client won't register UIs. Instead, store to static + * field, just like {@link #GTTileEntityDefaultUI}. Such mistake can be easily overlooked by testing only SP. + */ + public static final Function<ContainerConstructor, UIInfo<?, ?>> GTTileEntityUIFactory = containerConstructor -> UIBuilder + .of() + .container((player, world, x, y, z) -> { + TileEntity te = world.getTileEntity(x, y, z); + if (te instanceof ITileWithModularUI mui) { + return createTileEntityContainer(player, mui::createWindow, te::markDirty, containerConstructor); + } + return null; + }) + .gui(((player, world, x, y, z) -> { + if (!world.isRemote) return null; + TileEntity te = world.getTileEntity(x, y, z); + if (te instanceof ITileWithModularUI mui) { + return createTileEntityGuiContainer(player, mui::createWindow, containerConstructor); + } + return null; + })) + .build(); + + private static final UIInfo<?, ?> GTTileEntityDefaultUI = GTTileEntityUIFactory.apply(ModularUIContainer::new); + + private static final Map<ForgeDirection, UIInfo<?, ?>> coverUI = new HashMap<>(); + + static { + for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) { + coverUI.put( + side, + UIBuilder.of() + .container((player, world, x, y, z) -> { + final TileEntity te = world.getTileEntity(x, y, z); + if (!(te instanceof ICoverable gtTileEntity)) return null; + final GT_CoverBehaviorBase<?> cover = gtTileEntity.getCoverBehaviorAtSideNew(side); + return createCoverContainer( + player, + cover::createWindow, + te::markDirty, + gtTileEntity.getCoverIDAtSide(side), + side, + gtTileEntity); + }) + .gui((player, world, x, y, z) -> { + if (!world.isRemote) return null; + final TileEntity te = world.getTileEntity(x, y, z); + if (!(te instanceof ICoverable gtTileEntity)) return null; + final GT_CoverBehaviorBase<?> cover = gtTileEntity.getCoverBehaviorAtSideNew(side); + return createCoverGuiContainer( + player, + cover::createWindow, + gtTileEntity.getCoverIDAtSide(side), + side, + gtTileEntity); + }) + .build()); + } + } + + /** + * Opens TileEntity UI, created by {@link ITileWithModularUI#createWindow}. + */ + public static void openGTTileEntityUI(IHasWorldObjectAndCoords aTileEntity, EntityPlayer aPlayer) { + if (aTileEntity.isClientSide()) return; + GTTileEntityDefaultUI.open( + aPlayer, + aTileEntity.getWorld(), + aTileEntity.getXCoord(), + aTileEntity.getYCoord(), + aTileEntity.getZCoord()); + } + + /** + * Opens cover UI, created by {@link GT_CoverBehaviorBase#createWindow}. + */ + public static void openCoverUI(ICoverable tileEntity, EntityPlayer player, ForgeDirection side) { + if (tileEntity.isClientSide()) return; + + GT_Values.NW.sendToPlayer( + new GT_Packet_SendCoverData( + side, + tileEntity.getCoverIDAtSide(side), + tileEntity.getComplexCoverDataAtSide(side), + tileEntity), + (EntityPlayerMP) player); + + coverUI.get(side) + .open( + player, + tileEntity.getWorld(), + tileEntity.getXCoord(), + tileEntity.getYCoord(), + tileEntity.getZCoord()); + } + + /** + * Opens UI for player's item, created by + * {@link com.gtnewhorizons.modularui.api.screen.IItemWithModularUI#createWindow}. + */ + public static void openPlayerHeldItemUI(EntityPlayer player) { + if (NetworkUtils.isClient()) return; + UIInfos.PLAYER_HELD_ITEM_UI.open(player); + } + + private static ModularUIContainer createTileEntityContainer(EntityPlayer player, + Function<UIBuildContext, ModularWindow> windowCreator, Runnable onWidgetUpdate, + ContainerConstructor containerCreator) { + final UIBuildContext buildContext = new UIBuildContext(player); + final ModularWindow window = windowCreator.apply(buildContext); + if (window == null) return null; + return containerCreator.of(new ModularUIContext(buildContext, onWidgetUpdate), window); + } + + @SideOnly(Side.CLIENT) + private static ModularGui createTileEntityGuiContainer(EntityPlayer player, + Function<UIBuildContext, ModularWindow> windowCreator, ContainerConstructor containerConstructor) { + final ModularUIContainer container = createTileEntityContainer( + player, + windowCreator, + null, + containerConstructor); + if (container == null) return null; + return new ModularGui(container); + } + + private static ModularUIContainer createCoverContainer(EntityPlayer player, + Function<GT_CoverUIBuildContext, ModularWindow> windowCreator, Runnable onWidgetUpdate, int coverID, + ForgeDirection side, ICoverable tile) { + final GT_CoverUIBuildContext buildContext = new GT_CoverUIBuildContext(player, coverID, side, tile, false); + final ModularWindow window = windowCreator.apply(buildContext); + if (window == null) return null; + return new ModularUIContainer(new ModularUIContext(buildContext, onWidgetUpdate), window); + } + + @SideOnly(Side.CLIENT) + private static ModularGui createCoverGuiContainer(EntityPlayer player, + Function<GT_CoverUIBuildContext, ModularWindow> windowCreator, int coverID, ForgeDirection side, + ICoverable tile) { + final ModularUIContainer container = createCoverContainer(player, windowCreator, null, coverID, side, tile); + if (container == null) { + return null; + } + return new ModularGui(container); + } + + @FunctionalInterface + public interface ContainerConstructor { + + ModularUIContainer of(ModularUIContext context, ModularWindow mainWindow); + } +} diff --git a/src/main/java/gregtech/api/gui/modularui/GT_UITextures.java b/src/main/java/gregtech/api/gui/modularui/GT_UITextures.java new file mode 100644 index 0000000000..b200e16d47 --- /dev/null +++ b/src/main/java/gregtech/api/gui/modularui/GT_UITextures.java @@ -0,0 +1,488 @@ +package gregtech.api.gui.modularui; + +import static gregtech.api.enums.Mods.GregTech; + +import java.util.function.BiFunction; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import com.gtnewhorizons.modularui.api.drawable.AdaptableUITexture; +import com.gtnewhorizons.modularui.api.drawable.FallbackableUITexture; +import com.gtnewhorizons.modularui.api.drawable.UITexture; + +public class GT_UITextures { + + public static final UITexture TRANSPARENT = UITexture.fullImage(GregTech.ID, "gui/picture/transparent"); + + public static final AdaptableUITexture BACKGROUND_SINGLEBLOCK_DEFAULT = AdaptableUITexture + .of(GregTech.ID, "gui/background/singleblock_default", 176, 166, 4); + public static final SteamTexture BACKGROUND_STEAM = SteamTexture + .adaptableTexture(GregTech.ID, "gui/background/%s", 176, 166, 4); + public static final UITexture BACKGROUND_FUSION_COMPUTER = UITexture + .fullImage(GregTech.ID, "gui/background/fusion_computer"); + public static final AdaptableUITexture BACKGROUND_TEXT_FIELD = AdaptableUITexture + .of(GregTech.ID, "gui/background/text_field", 142, 28, 1); + public static final AdaptableUITexture BACKGROUND_TEXT_FIELD_LIGHT_GRAY = AdaptableUITexture + .of(GregTech.ID, "gui/background/text_field_light_gray", 61, 12, 1); + public static final AdaptableUITexture BACKGROUND_NEI_SINGLE_RECIPE = AdaptableUITexture + .of(GregTech.ID, "gui/background/nei_single_recipe.png", 64, 64, 2); + + public static final SteamTexture SLOT_ITEM_STEAM = SteamTexture.fullImage(GregTech.ID, "gui/slot/item_%s"); + public static final SteamTexture SLOT_FLUID_STEAM = SteamTexture.fullImage(GregTech.ID, "gui/slot/fluid_%s"); + public static final AdaptableUITexture SLOT_DARK_GRAY = AdaptableUITexture + .of(GregTech.ID, "gui/slot/dark_gray", 18, 18, 1); + public static final AdaptableUITexture SLOT_MAINTENANCE = AdaptableUITexture + .of(GregTech.ID, "gui/slot/maintenance", 20, 20, 1); + public static final AdaptableUITexture SLOT_UPLIFTED = AdaptableUITexture + .of(GregTech.ID, "gui/slot/uplifted", 18, 18, 1); + + public static final UITexture OVERLAY_SLOT_ARROW_ME = UITexture.fullImage(GregTech.ID, "gui/overlay_slot/arrow_me"); + public static final UITexture OVERLAY_SLOT_PATTERN_ME = UITexture + .fullImage(GregTech.ID, "gui/overlay_slot/pattern_me"); + + public static final UITexture OVERLAY_SLOT_BEAKER_1 = UITexture.fullImage(GregTech.ID, "gui/overlay_slot/beaker_1"); + public static final UITexture OVERLAY_SLOT_BEAKER_2 = UITexture.fullImage(GregTech.ID, "gui/overlay_slot/beaker_2"); + public static final UITexture OVERLAY_SLOT_BEE_DRONE = UITexture + .fullImage(GregTech.ID, "gui/overlay_slot/bee_drone"); + public static final UITexture OVERLAY_SLOT_BEE_QUEEN = UITexture + .fullImage(GregTech.ID, "gui/overlay_slot/bee_queen"); + public static final UITexture OVERLAY_SLOT_BENDER = UITexture.fullImage(GregTech.ID, "gui/overlay_slot/bender"); + public static final UITexture OVERLAY_SLOT_BOX = UITexture.fullImage(GregTech.ID, "gui/overlay_slot/box"); + public static final UITexture OVERLAY_SLOT_BOXED = UITexture.fullImage(GregTech.ID, "gui/overlay_slot/boxed"); + public static final UITexture OVERLAY_SLOT_CANISTER = UITexture.fullImage(GregTech.ID, "gui/overlay_slot/canister"); + public static final SteamTexture OVERLAY_SLOT_CANISTER_STEAM = SteamTexture + .fullImage(GregTech.ID, "gui/overlay_slot/canister_%s"); + public static final UITexture OVERLAY_SLOT_CANNER = UITexture.fullImage(GregTech.ID, "gui/overlay_slot/canner"); + public static final UITexture OVERLAY_SLOT_CAULDRON = UITexture.fullImage(GregTech.ID, "gui/overlay_slot/cauldron"); + public static final UITexture OVERLAY_SLOT_CENTRIFUGE = UITexture + .fullImage(GregTech.ID, "gui/overlay_slot/centrifuge"); + public static final UITexture OVERLAY_SLOT_CENTRIFUGE_FLUID = UITexture + .fullImage(GregTech.ID, "gui/overlay_slot/centrifuge_fluid"); + public static final SteamTexture OVERLAY_SLOT_CENTRIFUGE_STEAM = SteamTexture + .fullImage(GregTech.ID, "gui/overlay_slot/centrifuge_%s"); + public static final UITexture OVERLAY_SLOT_CHARGER = UITexture.fullImage(GregTech.ID, "gui/overlay_slot/charger"); + public static final UITexture OVERLAY_SLOT_CHARGER_FLUID = UITexture + .fullImage(GregTech.ID, "gui/overlay_slot/charger_fluid"); + public static final UITexture OVERLAY_SLOT_CIRCUIT = UITexture.fullImage(GregTech.ID, "gui/overlay_slot/circuit"); + public static final SteamTexture OVERLAY_SLOT_COAL_STEAM = SteamTexture + .fullImage(GregTech.ID, "gui/overlay_slot/coal_%s"); + public static final UITexture OVERLAY_SLOT_COMPRESSOR = UITexture + .fullImage(GregTech.ID, "gui/overlay_slot/compressor"); + public static final SteamTexture OVERLAY_SLOT_COMPRESSOR_STEAM = SteamTexture + .fullImage(GregTech.ID, "gui/overlay_slot/compressor_%s"); + public static final UITexture OVERLAY_SLOT_CRUSHED_ORE = UITexture + .fullImage(GregTech.ID, "gui/overlay_slot/crushed_ore"); + public static final SteamTexture OVERLAY_SLOT_CRUSHED_ORE_STEAM = SteamTexture + .fullImage(GregTech.ID, "gui/overlay_slot/crushed_ore_%s"); + public static final UITexture OVERLAY_SLOT_CUTTER_SLICED = UITexture + .fullImage(GregTech.ID, "gui/overlay_slot/cutter_sliced"); + public static final UITexture OVERLAY_SLOT_DATA_ORB = UITexture.fullImage(GregTech.ID, "gui/overlay_slot/data_orb"); + public static final UITexture OVERLAY_SLOT_DATA_STICK = UITexture + .fullImage(GregTech.ID, "gui/overlay_slot/data_stick"); + public static final UITexture OVERLAY_SLOT_DUST = UITexture.fullImage(GregTech.ID, "gui/overlay_slot/dust"); + public static final SteamTexture OVERLAY_SLOT_DUST_STEAM = SteamTexture + .fullImage(GregTech.ID, "gui/overlay_slot/dust_%s"); + public static final SteamTexture OVERLAY_SLOT_BLOCK_STEAM = SteamTexture + .fullImage(GregTech.ID, "gui/overlay_slot/block_%s"); + public static final UITexture OVERLAY_SLOT_EXPLOSIVE = UITexture + .fullImage(GregTech.ID, "gui/overlay_slot/explosive"); + public static final UITexture OVERLAY_SLOT_EXTRUDER_SHAPE = UITexture + .fullImage(GregTech.ID, "gui/overlay_slot/extruder_shape"); + public static final UITexture OVERLAY_SLOT_FURNACE = UITexture.fullImage(GregTech.ID, "gui/overlay_slot/furnace"); + public static final SteamTexture OVERLAY_SLOT_FURNACE_STEAM = SteamTexture + .fullImage(GregTech.ID, "gui/overlay_slot/furnace_%s"); + public static final UITexture OVERLAY_SLOT_GEM = UITexture.fullImage(GregTech.ID, "gui/overlay_slot/gem"); + public static final UITexture OVERLAY_SLOT_HAMMER = UITexture.fullImage(GregTech.ID, "gui/overlay_slot/hammer"); + public static final SteamTexture OVERLAY_SLOT_HAMMER_STEAM = SteamTexture + .fullImage(GregTech.ID, "gui/overlay_slot/hammer_%s"); + public static final UITexture OVERLAY_SLOT_HEATER_1 = UITexture.fullImage(GregTech.ID, "gui/overlay_slot/heater_1"); + public static final UITexture OVERLAY_SLOT_HEATER_2 = UITexture.fullImage(GregTech.ID, "gui/overlay_slot/heater_2"); + public static final UITexture OVERLAY_SLOT_IMPLOSION = UITexture + .fullImage(GregTech.ID, "gui/overlay_slot/implosion"); + public static final UITexture OVERLAY_SLOT_IN = UITexture.fullImage(GregTech.ID, "gui/overlay_slot/in"); + public static final SteamTexture OVERLAY_SLOT_IN_STEAM = SteamTexture + .fullImage(GregTech.ID, "gui/overlay_slot/in_%s"); + public static final SteamTexture OVERLAY_SLOT_INGOT_STEAM = SteamTexture + .fullImage(GregTech.ID, "gui/overlay_slot/ingot_%s"); + public static final UITexture OVERLAY_SLOT_INT_CIRCUIT = UITexture + .fullImage(GregTech.ID, "gui/overlay_slot/int_circuit"); + public static final UITexture OVERLAY_SLOT_LENS = UITexture.fullImage(GregTech.ID, "gui/overlay_slot/lens"); + public static final UITexture OVERLAY_SLOT_MICROSCOPE = UITexture + .fullImage(GregTech.ID, "gui/overlay_slot/microscope"); + public static final UITexture OVERLAY_SLOT_MINING_PIPE = UITexture + .fullImage(GregTech.ID, "gui/overlay_slot/mining_pipe"); + public static final UITexture OVERLAY_SLOT_MOLD = UITexture.fullImage(GregTech.ID, "gui/overlay_slot/mold"); + public static final UITexture OVERLAY_SLOT_MOLECULAR_1 = UITexture + .fullImage(GregTech.ID, "gui/overlay_slot/molecular_1"); + public static final UITexture OVERLAY_SLOT_MOLECULAR_2 = UITexture + .fullImage(GregTech.ID, "gui/overlay_slot/molecular_2"); + public static final UITexture OVERLAY_SLOT_MOLECULAR_3 = UITexture + .fullImage(GregTech.ID, "gui/overlay_slot/molecular_3"); + public static final UITexture OVERLAY_SLOT_OUT = UITexture.fullImage(GregTech.ID, "gui/overlay_slot/out"); + public static final SteamTexture OVERLAY_SLOT_OUT_STEAM = SteamTexture + .fullImage(GregTech.ID, "gui/overlay_slot/out_%s"); + public static final UITexture OVERLAY_SLOT_PAGE_BLANK = UITexture + .fullImage(GregTech.ID, "gui/overlay_slot/page_blank"); + public static final UITexture OVERLAY_SLOT_PAGE_PRINTED = UITexture + .fullImage(GregTech.ID, "gui/overlay_slot/page_printed"); + public static final UITexture OVERLAY_SLOT_PRESS_1 = UITexture.fullImage(GregTech.ID, "gui/overlay_slot/press_1"); + public static final UITexture OVERLAY_SLOT_PRESS_2 = UITexture.fullImage(GregTech.ID, "gui/overlay_slot/press_2"); + public static final UITexture OVERLAY_SLOT_PRESS_3 = UITexture.fullImage(GregTech.ID, "gui/overlay_slot/press_3"); + public static final UITexture OVERLAY_SLOT_RECYCLE = UITexture.fullImage(GregTech.ID, "gui/overlay_slot/recycle"); + public static final UITexture OVERLAY_SLOT_ROD_1 = UITexture.fullImage(GregTech.ID, "gui/overlay_slot/rod_1"); + public static final UITexture OVERLAY_SLOT_ROD_2 = UITexture.fullImage(GregTech.ID, "gui/overlay_slot/rod_2"); + public static final UITexture OVERLAY_SLOT_SLICE_SHAPE = UITexture + .fullImage(GregTech.ID, "gui/overlay_slot/slice_shape"); + public static final UITexture OVERLAY_SLOT_SLICER_SLICED = UITexture + .fullImage(GregTech.ID, "gui/overlay_slot/slicer_sliced"); + public static final UITexture OVERLAY_SLOT_SQUARE = UITexture.fullImage(GregTech.ID, "gui/overlay_slot/square"); + public static final UITexture OVERLAY_SLOT_UUA = UITexture.fullImage(GregTech.ID, "gui/overlay_slot/uua"); + public static final UITexture OVERLAY_SLOT_UUM = UITexture.fullImage(GregTech.ID, "gui/overlay_slot/uum"); + public static final UITexture OVERLAY_SLOT_VIAL_1 = UITexture.fullImage(GregTech.ID, "gui/overlay_slot/vial_1"); + public static final UITexture OVERLAY_SLOT_VIAL_2 = UITexture.fullImage(GregTech.ID, "gui/overlay_slot/vial_2"); + public static final UITexture OVERLAY_SLOT_WIREMILL = UITexture.fullImage(GregTech.ID, "gui/overlay_slot/wiremill"); + public static final UITexture OVERLAY_SLOT_WRENCH = UITexture.fullImage(GregTech.ID, "gui/overlay_slot/wrench"); + public static final UITexture[] OVERLAY_SLOTS_NUMBER = IntStream.range(0, 12) + .mapToObj(i -> UITexture.fullImage(GregTech.ID, "gui/overlay_slot/number_" + i)) + .collect(Collectors.toList()) + .toArray(new UITexture[0]); + + public static final UITexture PROGRESSBAR_ARROW = UITexture.fullImage(GregTech.ID, "gui/progressbar/arrow"); + public static final SteamTexture PROGRESSBAR_ARROW_STEAM = SteamTexture + .fullImage(GregTech.ID, "gui/progressbar/arrow_%s"); + public static final SteamTexture PROGRESSBAR_ARROW_2_STEAM = SteamTexture + .fullImage(GregTech.ID, "gui/progressbar/arrow_2_%s"); + public static final UITexture PROGRESSBAR_ARROW_MULTIPLE = UITexture + .fullImage(GregTech.ID, "gui/progressbar/arrow_multiple"); + public static final UITexture PROGRESSBAR_ASSEMBLE = UITexture.fullImage(GregTech.ID, "gui/progressbar/assemble"); + public static final UITexture PROGRESSBAR_ASSEMBLY_LINE_1 = UITexture + .fullImage(GregTech.ID, "gui/progressbar/assemblyline_1"); + public static final UITexture PROGRESSBAR_ASSEMBLY_LINE_2 = UITexture + .fullImage(GregTech.ID, "gui/progressbar/assemblyline_2"); + public static final UITexture PROGRESSBAR_ASSEMBLY_LINE_3 = UITexture + .fullImage(GregTech.ID, "gui/progressbar/assemblyline_3"); + public static final UITexture PROGRESSBAR_BATH = UITexture.fullImage(GregTech.ID, "gui/progressbar/bath"); + public static final UITexture PROGRESSBAR_BENDING = UITexture.fullImage(GregTech.ID, "gui/progressbar/bending"); + public static final SteamTexture PROGRESSBAR_BOILER_EMPTY_STEAM = SteamTexture + .fullImage(GregTech.ID, "gui/progressbar/boiler_empty_%s"); + public static final UITexture PROGRESSBAR_BOILER_HEAT = UITexture + .fullImage(GregTech.ID, "gui/progressbar/boiler_heat"); + public static final UITexture PROGRESSBAR_BOILER_STEAM = UITexture + .fullImage(GregTech.ID, "gui/progressbar/boiler_steam"); + public static final UITexture PROGRESSBAR_BOILER_WATER = UITexture + .fullImage(GregTech.ID, "gui/progressbar/boiler_water"); + public static final UITexture PROGRESSBAR_CANNER = UITexture.fullImage(GregTech.ID, "gui/progressbar/canner"); + public static final UITexture PROGRESSBAR_CIRCUIT_ASSEMBLER = UITexture + .fullImage(GregTech.ID, "gui/progressbar/circuit_assembler"); + public static final UITexture PROGRESSBAR_COMPRESS = UITexture.fullImage(GregTech.ID, "gui/progressbar/compress"); + public static final SteamTexture PROGRESSBAR_COMPRESS_STEAM = SteamTexture + .fullImage(GregTech.ID, "gui/progressbar/compress_%s"); + public static final UITexture PROGRESSBAR_CUT = UITexture.fullImage(GregTech.ID, "gui/progressbar/cut"); + public static final UITexture PROGRESSBAR_EXTRACT = UITexture.fullImage(GregTech.ID, "gui/progressbar/extract"); + public static final SteamTexture PROGRESSBAR_EXTRACT_STEAM = SteamTexture + .fullImage(GregTech.ID, "gui/progressbar/extract_%s"); + public static final UITexture PROGRESSBAR_EXTRUDE = UITexture.fullImage(GregTech.ID, "gui/progressbar/extrude"); + public static final SteamTexture PROGRESSBAR_FUEL_STEAM = SteamTexture + .fullImage(GregTech.ID, "gui/progressbar/fuel_%s"); + public static final UITexture PROGRESSBAR_HAMMER = UITexture.fullImage(GregTech.ID, "gui/progressbar/hammer"); + public static final UITexture PROGRESSBAR_HAMMER_BASE = UITexture + .fullImage(GregTech.ID, "gui/progressbar/hammer_base"); + public static final SteamTexture PROGRESSBAR_HAMMER_STEAM = SteamTexture + .fullImage(GregTech.ID, "gui/progressbar/hammer_%s"); + public static final SteamTexture PROGRESSBAR_HAMMER_BASE_STEAM = SteamTexture + .fullImage(GregTech.ID, "gui/progressbar/hammer_base_%s"); + public static final UITexture PROGRESSBAR_LATHE = UITexture.fullImage(GregTech.ID, "gui/progressbar/lathe"); + public static final UITexture PROGRESSBAR_LATHE_BASE = UITexture + .fullImage(GregTech.ID, "gui/progressbar/lathe_base"); + public static final UITexture PROGRESSBAR_MACERATE = UITexture.fullImage(GregTech.ID, "gui/progressbar/macerate"); + public static final SteamTexture PROGRESSBAR_MACERATE_STEAM = SteamTexture + .fullImage(GregTech.ID, "gui/progressbar/macerate_%s"); + public static final UITexture PROGRESSBAR_MAGNET = UITexture.fullImage(GregTech.ID, "gui/progressbar/magnet"); + public static final UITexture PROGRESSBAR_MIXER = UITexture.fullImage(GregTech.ID, "gui/progressbar/mixer"); + public static final UITexture PROGRESSBAR_RECYCLE = UITexture.fullImage(GregTech.ID, "gui/progressbar/recycle"); + public static final UITexture PROGRESSBAR_SIFT = UITexture.fullImage(GregTech.ID, "gui/progressbar/sift"); + public static final UITexture PROGRESSBAR_SLICE = UITexture.fullImage(GregTech.ID, "gui/progressbar/slice"); + public static final UITexture PROGRESSBAR_STORED_EU = UITexture.fullImage(GregTech.ID, "gui/progressbar/stored_eu"); + public static final UITexture PROGRESSBAR_WIREMILL = UITexture.fullImage(GregTech.ID, "gui/progressbar/wiremill"); + + public static FallbackableUITexture fallbackableProgressbar(String name, UITexture fallback) { + return new FallbackableUITexture(UITexture.fullImage(GregTech.ID, "gui/progressbar/" + name), fallback); + } + + public static final UITexture TAB_COVER_NORMAL = UITexture.fullImage(GregTech.ID, "gui/tab/cover_normal"); + public static final UITexture TAB_COVER_HIGHLIGHT = UITexture.fullImage(GregTech.ID, "gui/tab/cover_highlight"); + public static final UITexture TAB_COVER_DISABLED = UITexture.fullImage(GregTech.ID, "gui/tab/cover_disabled"); + public static final SteamTexture TAB_COVER_STEAM_NORMAL = SteamTexture + .fullImage(GregTech.ID, "gui/tab/cover_%s_normal"); + public static final SteamTexture TAB_COVER_STEAM_HIGHLIGHT = SteamTexture + .fullImage(GregTech.ID, "gui/tab/cover_%s_highlight"); + public static final SteamTexture TAB_COVER_STEAM_DISABLED = SteamTexture + .fullImage(GregTech.ID, "gui/tab/cover_%s_disabled"); + public static final AdaptableUITexture TAB_TITLE = AdaptableUITexture.of(GregTech.ID, "gui/tab/title", 28, 28, 4); + public static final AdaptableUITexture TAB_TITLE_DARK = AdaptableUITexture + .of(GregTech.ID, "gui/tab/title_dark", 28, 28, 4); + public static final SteamTexture TAB_TITLE_STEAM = SteamTexture + .adaptableTexture(GregTech.ID, "gui/tab/title_%s", 28, 28, 4); + public static final SteamTexture TAB_TITLE_DARK_STEAM = SteamTexture + .adaptableTexture(GregTech.ID, "gui/tab/title_dark_%s", 28, 28, 4); + public static final AdaptableUITexture TAB_TITLE_ANGULAR = AdaptableUITexture + .of(GregTech.ID, "gui/tab/title_angular", 28, 28, 4); + public static final SteamTexture TAB_TITLE_ANGULAR_STEAM = SteamTexture + .adaptableTexture(GregTech.ID, "gui/tab/title_angular_%s", 28, 28, 4); + + public static final UITexture BUTTON_STANDARD = AdaptableUITexture + .of(GregTech.ID, "gui/button/standard", 18, 18, 1); + public static final UITexture BUTTON_STANDARD_PRESSED = AdaptableUITexture + .of(GregTech.ID, "gui/button/standard_pressed", 18, 18, 1); + public static final UITexture BUTTON_STANDARD_DISABLED = AdaptableUITexture + .of(GregTech.ID, "gui/button/standard_disabled", 18, 18, 1); + public static final UITexture BUTTON_STANDARD_TOGGLE = AdaptableUITexture + .of(GregTech.ID, "gui/button/standard_toggle", 18, 18, 1); + public static final UITexture BUTTON_STANDARD_TOGGLE_DISABLED = AdaptableUITexture + .of(GregTech.ID, "gui/button/standard_toggle_disabled", 18, 18, 1); + public static final UITexture BUTTON_COVER_NORMAL = UITexture.fullImage(GregTech.ID, "gui/button/cover_normal"); + public static final UITexture BUTTON_COVER_NORMAL_HOVERED = UITexture + .fullImage(GregTech.ID, "gui/button/cover_normal_hovered"); + public static final UITexture BUTTON_COVER_NORMAL_DISABLED = UITexture + .fullImage(GregTech.ID, "gui/button/cover_normal_disabled"); + + public static final UITexture OVERLAY_BUTTON_DISABLE = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/disable"); + public static final UITexture OVERLAY_BUTTON_REDSTONE_OFF = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/redstone_off"); + public static final UITexture OVERLAY_BUTTON_REDSTONE_ON = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/redstone_on"); + public static final UITexture OVERLAY_BUTTON_POWER_SWITCH_ON = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/power_switch_on"); + public static final UITexture OVERLAY_BUTTON_POWER_SWITCH_OFF = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/power_switch_off"); + public static final UITexture OVERLAY_BUTTON_VOID_EXCESS_NONE = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/void_excess_none"); + public static final UITexture OVERLAY_BUTTON_VOID_EXCESS_ITEM = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/void_excess_item"); + public static final UITexture OVERLAY_BUTTON_VOID_EXCESS_FLUID = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/void_excess_fluid"); + public static final UITexture OVERLAY_BUTTON_VOID_EXCESS_ALL = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/void_excess_all"); + public static final UITexture OVERLAY_BUTTON_INPUT_SEPARATION_ON = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/input_separation_on"); + public static final UITexture OVERLAY_BUTTON_INPUT_SEPARATION_ON_DISABLED = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/input_separation_on_disabled"); + public static final UITexture OVERLAY_BUTTON_INPUT_SEPARATION_OFF = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/input_separation_off"); + public static final UITexture OVERLAY_BUTTON_INPUT_SEPARATION_OFF_DISABLED = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/input_separation_off_disabled"); + public static final UITexture OVERLAY_BUTTON_RECIPE_LOCKED = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/recipe_locked"); + public static final UITexture OVERLAY_BUTTON_RECIPE_LOCKED_DISABLED = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/recipe_locked_disabled"); + public static final UITexture OVERLAY_BUTTON_RECIPE_UNLOCKED = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/recipe_unlocked"); + public static final UITexture OVERLAY_BUTTON_RECIPE_UNLOCKED_DISABLED = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/recipe_unlocked_disabled"); + public static final UITexture OVERLAY_BUTTON_BATCH_MODE_ON = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/batch_mode_on"); + public static final UITexture OVERLAY_BUTTON_BATCH_MODE_ON_DISABLED = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/batch_mode_on_disabled"); + public static final UITexture OVERLAY_BUTTON_BATCH_MODE_OFF = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/batch_mode_off"); + public static final UITexture OVERLAY_BUTTON_BATCH_MODE_OFF_DISABLED = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/batch_mode_off_disabled"); + public static final UITexture OVERLAY_BUTTON_FORBIDDEN = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/forbidden"); + public static final UITexture OVERLAY_BUTTON_LOCKED = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/lock_small"); + public static final UITexture OVERLAY_BUTTON_DOWN_TIERING_ON = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/down_tiering_on"); + public static final UITexture OVERLAY_BUTTON_DOWN_TIERING_OFF = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/down_tiering_off"); + public static final UITexture OVERLAY_BUTTON_CHECKMARK = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/checkmark"); + public static final UITexture OVERLAY_BUTTON_CROSS = UITexture.fullImage(GregTech.ID, "gui/overlay_button/cross"); + public static final UITexture OVERLAY_BUTTON_WHITELIST = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/whitelist"); + public static final UITexture OVERLAY_BUTTON_BLACKLIST = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/blacklist"); + public static final UITexture OVERLAY_BUTTON_PROGRESS = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/progress"); + public static final UITexture OVERLAY_BUTTON_EXPORT = UITexture.fullImage(GregTech.ID, "gui/overlay_button/export"); + public static final UITexture OVERLAY_BUTTON_IMPORT = UITexture.fullImage(GregTech.ID, "gui/overlay_button/import"); + public static final UITexture OVERLAY_BUTTON_AUTOOUTPUT_ITEM = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/autooutput_item"); + public static final UITexture OVERLAY_BUTTON_AUTOOUTPUT_FLUID = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/autooutput_fluid"); + public static final UITexture OVERLAY_BUTTON_ALLOW_INPUT = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/allow_input"); + public static final UITexture OVERLAY_BUTTON_ALLOW_OUTPUT = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/allow_output"); + public static final UITexture OVERLAY_BUTTON_AUTOPULL_ME = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/auto_pull_me"); + public static final UITexture OVERLAY_BUTTON_AUTOPULL_ME_DISABLED = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/auto_pull_me_disabled"); + public static final UITexture OVERLAY_BUTTON_BLOCK_INPUT = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/block_input"); + public static final UITexture OVERLAY_BUTTON_BLOCK_OUTPUT = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/block_output"); + public static final UITexture OVERLAY_BUTTON_ARROW_GREEN_UP = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/arrow_green_up"); + public static final UITexture OVERLAY_BUTTON_ARROW_GREEN_DOWN = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/arrow_green_down"); + public static final UITexture OVERLAY_BUTTON_CYCLIC = UITexture.fullImage(GregTech.ID, "gui/overlay_button/cyclic"); + public static final UITexture OVERLAY_BUTTON_EMIT_ENERGY = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/emit_energy"); + public static final UITexture OVERLAY_BUTTON_EMIT_REDSTONE = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/emit_redstone"); + public static final UITexture OVERLAY_BUTTON_INVERT_REDSTONE = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/invert_redstone"); + public static final UITexture OVERLAY_BUTTON_STOCKING_MODE = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/stocking_mode"); + public static final UITexture OVERLAY_BUTTON_INVERT_FILTER = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/invert_filter"); + public static final UITexture OVERLAY_BUTTON_NBT = UITexture.fullImage(GregTech.ID, "gui/overlay_button/nbt"); + public static final UITexture OVERLAY_BUTTON_PRINT = UITexture.fullImage(GregTech.ID, "gui/overlay_button/print"); + public static final UITexture OVERLAY_BUTTON_TRANSPOSE = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/transpose"); + public static final UITexture OVERLAY_BUTTON_SORTING_MODE = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/sorting_mode"); + public static final UITexture OVERLAY_BUTTON_ONE_STACK_LIMIT = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/one_stack_limit"); + public static final UITexture OVERLAY_BUTTON_BOUNDING_BOX = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/bounding_box"); + public static final UITexture OVERLAY_BUTTON_MINUS_SMALL = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/minus_small"); + public static final UITexture OVERLAY_BUTTON_MINUS_LARGE = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/minus_large"); + public static final UITexture OVERLAY_BUTTON_PLUS_SMALL = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/plus_small"); + public static final UITexture OVERLAY_BUTTON_PLUS_LARGE = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/plus_large"); + public static final UITexture OVERLAY_BUTTON_GATE_AND = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/gate_and"); + public static final UITexture OVERLAY_BUTTON_GATE_NAND = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/gate_nand"); + public static final UITexture OVERLAY_BUTTON_GATE_OR = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/gate_or"); + public static final UITexture OVERLAY_BUTTON_GATE_NOR = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/gate_nor"); + public static final UITexture OVERLAY_BUTTON_ANALOG = UITexture.fullImage(GregTech.ID, "gui/overlay_button/analog"); + public static final UITexture OVERLAY_BUTTON_LOCK = UITexture.fullImage(GregTech.ID, "gui/overlay_button/lock"); + public static final UITexture OVERLAY_BUTTON_INPUT_FROM_OUTPUT_SIDE = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/input_from_output_side"); + public static final UITexture OVERLAY_BUTTON_TANK_VOID_EXCESS = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/tank_void_excess"); + public static final UITexture OVERLAY_BUTTON_TANK_VOID_ALL = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/tank_void_all"); + public static final UITexture OVERLAY_BUTTON_NEI = UITexture.fullImage(GregTech.ID, "gui/overlay_button/nei"); + public static final UITexture OVERLAY_BUTTON_USE_PROCESSING_STATE = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/use_processing_state.png"); + public static final UITexture OVERLAY_BUTTON_USE_INVERTED_PROCESSING_STATE = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/use_inverted_processing_state.png"); + public static final UITexture OVERLAY_BUTTON_CHUNK_LOADING = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/chunkloading"); + public static final UITexture OVERLAY_BUTTON_CHUNK_LOADING_OFF = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/chunkloading_off"); + public static final UITexture OVERLAY_BUTTON_WORK_AREA = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/work_area"); + public static final UITexture OVERLAY_BUTTON_REPLACE_COBBLE_ON = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/replace_cobble_on"); + public static final UITexture OVERLAY_BUTTON_REPLACE_COBBLE_OFF = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/replace_cobble_off"); + public static final UITexture OVERLAY_BUTTON_RETRACT_PIPE = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/retract_pipes"); + public static final UITexture OVERLAY_BUTTON_HOURGLASS = UITexture + .fullImage(GregTech.ID, "gui/overlay_button/hourglass"); + + /** + * Can adjust size as needed. + */ + public static final AdaptableUITexture PICTURE_SCREEN_BLACK = AdaptableUITexture + .of(GregTech.ID, "gui/picture/screen_black", 16, 16, 2); + + public static final UITexture PICTURE_RADIATION_WARNING = UITexture + .fullImage(GregTech.ID, "gui/picture/radiation_warning"); + public static final UITexture PICTURE_GT_LOGO_17x17_TRANSPARENT = UITexture + .fullImage(GregTech.ID, "gui/picture/gt_logo_17x17_transparent"); + public static final UITexture PICTURE_GT_LOGO_17x17_TRANSPARENT_GRAY = UITexture + .fullImage(GregTech.ID, "gui/picture/gt_logo_17x17_transparent_gray"); + public static final SteamTexture PICTURE_GT_LOGO_17x17_TRANSPARENT_STEAM = SteamTexture + .fullImage(GregTech.ID, "gui/picture/gt_logo_17x17_transparent_%s"); + public static final UITexture PICTURE_GT_LOGO_18x18 = UITexture.fullImage(GregTech.ID, "gui/picture/gt_logo_18x18"); + public static final UITexture PICTURE_GT_LOGO_19x19 = UITexture.fullImage(GregTech.ID, "gui/picture/gt_logo_19x19"); + public static final UITexture PICTURE_INFORMATION = UITexture.fullImage(GregTech.ID, "gui/picture/information"); + public static final UITexture PICTURE_STALLED_ELECTRICITY = UITexture + .fullImage(GregTech.ID, "gui/picture/stalled_electricity"); + public static final UITexture PICTURE_STALLED_STEAM = UITexture.fullImage(GregTech.ID, "gui/picture/stalled_steam"); + public static final BiFunction<Integer, Boolean, UITexture> PICTURE_ARROW_22_RED = (width, fromRight) -> UITexture + .partly( + GregTech.ID, + "gui/picture/arrow_22_red", + 87, + 22, + fromRight ? 87 - width : 0, + 0, + fromRight ? 87 : width, + 22); + public static final BiFunction<Integer, Boolean, UITexture> PICTURE_ARROW_22_BLUE = (width, fromRight) -> UITexture + .partly( + GregTech.ID, + "gui/picture/arrow_22_blue", + 87, + 22, + fromRight ? 87 - width : 0, + 0, + fromRight ? 87 : width, + 22); + public static final BiFunction<Integer, Boolean, UITexture> PICTURE_ARROW_22_WHITE = (width, fromRight) -> UITexture + .partly( + GregTech.ID, + "gui/picture/arrow_22_white", + 87, + 22, + fromRight ? 87 - width : 0, + 0, + fromRight ? 87 : width, + 22); + public static final BiFunction<Integer, Boolean, UITexture> PICTURE_ARROW_24_RED = (width, fromRight) -> UITexture + .partly( + GregTech.ID, + "gui/picture/arrow_24_red", + 69, + 24, + fromRight ? 69 - width : 0, + 0, + fromRight ? 69 : width, + 24); + public static final BiFunction<Integer, Boolean, UITexture> PICTURE_ARROW_24_BLUE = (width, fromRight) -> UITexture + .partly( + GregTech.ID, + "gui/picture/arrow_24_blue", + 69, + 24, + fromRight ? 69 - width : 0, + 0, + fromRight ? 69 : width, + 24); + public static final BiFunction<Integer, Boolean, UITexture> PICTURE_ARROW_24_WHITE = (width, fromRight) -> UITexture + .partly( + GregTech.ID, + "gui/picture/arrow_24_white", + 69, + 24, + fromRight ? 69 - width : 0, + 0, + fromRight ? 69 : width, + 24); + public static final UITexture PICTURE_FLUID_WINDOW = UITexture.fullImage(GregTech.ID, "gui/picture/fluid_window"); + public static final UITexture PICTURE_FLUID_TANK = UITexture.fullImage(GregTech.ID, "gui/picture/fluid_tank"); + public static final UITexture PICTURE_SLOTS_HOLO_3BY3 = UITexture + .fullImage(GregTech.ID, "gui/picture/slots_holo_3by3"); + public static final UITexture PICTURE_ARROW_DOUBLE = UITexture.fullImage(GregTech.ID, "gui/picture/arrow_double"); + public static final UITexture PICTURE_SUPER_BUFFER = UITexture.fullImage(GregTech.ID, "gui/picture/super_buffer"); + public static final UITexture PICTURE_SQUARE_LIGHT_GRAY = UITexture + .fullImage(GregTech.ID, "gui/picture/square_light_gray"); + public static final UITexture PICTURE_GAUGE = UITexture.fullImage(GregTech.ID, "gui/picture/gauge"); + public static final UITexture PICTURE_ITEM_IN = UITexture.fullImage(GregTech.ID, "gui/picture/item_in"); + public static final UITexture PICTURE_ITEM_OUT = UITexture.fullImage(GregTech.ID, "gui/picture/item_out"); + public static final UITexture PICTURE_FLUID_IN = UITexture.fullImage(GregTech.ID, "gui/picture/fluid_in"); + public static final UITexture PICTURE_FLUID_OUT = UITexture.fullImage(GregTech.ID, "gui/picture/fluid_out"); +} diff --git a/src/main/java/gregtech/api/gui/modularui/GUITextureSet.java b/src/main/java/gregtech/api/gui/modularui/GUITextureSet.java new file mode 100644 index 0000000000..18d7741421 --- /dev/null +++ b/src/main/java/gregtech/api/gui/modularui/GUITextureSet.java @@ -0,0 +1,156 @@ +package gregtech.api.gui.modularui; + +import java.util.function.Function; + +import com.gtnewhorizons.modularui.api.ModularUITextures; +import com.gtnewhorizons.modularui.api.drawable.AdaptableUITexture; +import com.gtnewhorizons.modularui.api.drawable.UITexture; + +import gregtech.api.enums.SteamVariant; + +/** + * Set of textures that is commonly used for GUI but can vary depending on "style" of machines, e.g. bronze steam or + * steel steam. <br> + * This has builder pattern; Textures you didn't specify will fall back to default ones. + */ +public class GUITextureSet { + + private UITexture mainBackground; + private UITexture itemSlot; + private UITexture fluidSlot; + private UITexture coverTabNormal; + private UITexture coverTabHighlight; + private UITexture coverTabDisabled; + private UITexture coverTabNormalFlipped; + private UITexture coverTabHighlightFlipped; + private UITexture coverTabDisabledFlipped; + private AdaptableUITexture titleTabNormal; + private AdaptableUITexture titleTabDark; + private AdaptableUITexture titleTabAngular; + private UITexture gregtechLogo; + + public static final GUITextureSet DEFAULT = new GUITextureSet() + .setMainBackground(GT_UITextures.BACKGROUND_SINGLEBLOCK_DEFAULT) + .setItemSlot(ModularUITextures.ITEM_SLOT) + .setFluidSlot(ModularUITextures.FLUID_SLOT) + .setCoverTab( + GT_UITextures.TAB_COVER_NORMAL, + GT_UITextures.TAB_COVER_HIGHLIGHT, + GT_UITextures.TAB_COVER_DISABLED) + .setTitleTab(GT_UITextures.TAB_TITLE, GT_UITextures.TAB_TITLE_DARK, GT_UITextures.TAB_TITLE_ANGULAR) + .setGregTechLogo(GT_UITextures.PICTURE_GT_LOGO_17x17_TRANSPARENT); + + public static final Function<SteamVariant, GUITextureSet> STEAM = steamVariant -> new GUITextureSet() + .setMainBackground(GT_UITextures.BACKGROUND_STEAM.get(steamVariant)) + .setItemSlot(GT_UITextures.SLOT_ITEM_STEAM.get(steamVariant)) + .setFluidSlot(GT_UITextures.SLOT_FLUID_STEAM.get(steamVariant)) + .setCoverTab( + GT_UITextures.TAB_COVER_STEAM_NORMAL.get(steamVariant), + GT_UITextures.TAB_COVER_STEAM_HIGHLIGHT.get(steamVariant), + GT_UITextures.TAB_COVER_STEAM_DISABLED.get(steamVariant)) + .setTitleTab( + GT_UITextures.TAB_TITLE_STEAM.getAdaptable(steamVariant), + GT_UITextures.TAB_TITLE_DARK_STEAM.getAdaptable(steamVariant), + GT_UITextures.TAB_TITLE_ANGULAR_STEAM.getAdaptable(steamVariant)) + .setGregTechLogo(GT_UITextures.PICTURE_GT_LOGO_17x17_TRANSPARENT_STEAM.get(steamVariant)); + + public GUITextureSet() {} + + // region setters + + public GUITextureSet setMainBackground(UITexture mainBackground) { + this.mainBackground = mainBackground; + return this; + } + + public GUITextureSet setItemSlot(UITexture itemSlot) { + this.itemSlot = itemSlot; + return this; + } + + public GUITextureSet setFluidSlot(UITexture fluidSlot) { + this.fluidSlot = fluidSlot; + return this; + } + + public GUITextureSet setCoverTab(UITexture coverNormal, UITexture coverHighlight, UITexture coverDisabled) { + this.coverTabNormal = coverNormal; + this.coverTabHighlight = coverHighlight; + this.coverTabDisabled = coverDisabled; + this.coverTabNormalFlipped = coverNormal.getFlipped(true, false); + this.coverTabHighlightFlipped = coverHighlight.getFlipped(true, false); + this.coverTabDisabledFlipped = coverDisabled.getFlipped(true, false); + return this; + } + + public GUITextureSet setTitleTab(AdaptableUITexture titleNormal, AdaptableUITexture titleDark, + AdaptableUITexture titleTabAngular) { + this.titleTabNormal = titleNormal; + this.titleTabDark = titleDark; + this.titleTabAngular = titleTabAngular; + return this; + } + + public GUITextureSet setGregTechLogo(UITexture gregtechLogo) { + this.gregtechLogo = gregtechLogo; + return this; + } + + // endregion + + // region getters + + public UITexture getMainBackground() { + return mainBackground != null ? mainBackground : DEFAULT.mainBackground; + } + + public UITexture getItemSlot() { + return itemSlot != null ? itemSlot : DEFAULT.itemSlot; + } + + public UITexture getFluidSlot() { + return fluidSlot != null ? fluidSlot : DEFAULT.fluidSlot; + } + + public UITexture getCoverTabNormal() { + return coverTabNormal != null ? coverTabNormal : DEFAULT.coverTabNormal; + } + + public UITexture getCoverTabHighlight() { + return coverTabHighlight != null ? coverTabHighlight : DEFAULT.coverTabHighlight; + } + + public UITexture getCoverTabDisabled() { + return coverTabDisabled != null ? coverTabDisabled : DEFAULT.coverTabDisabled; + } + + public UITexture getCoverTabNormalFlipped() { + return coverTabNormalFlipped != null ? coverTabNormalFlipped : DEFAULT.coverTabNormalFlipped; + } + + public UITexture getCoverTabHighlightFlipped() { + return coverTabHighlightFlipped != null ? coverTabHighlightFlipped : DEFAULT.coverTabHighlightFlipped; + } + + public UITexture getCoverTabDisabledFlipped() { + return coverTabDisabledFlipped != null ? coverTabDisabledFlipped : DEFAULT.coverTabDisabledFlipped; + } + + public AdaptableUITexture getTitleTabNormal() { + return titleTabNormal != null ? titleTabNormal : DEFAULT.titleTabNormal; + } + + public AdaptableUITexture getTitleTabDark() { + return titleTabDark != null ? titleTabDark : DEFAULT.titleTabDark; + } + + public AdaptableUITexture getTitleTabAngular() { + return titleTabAngular != null ? titleTabAngular : DEFAULT.titleTabAngular; + } + + public UITexture getGregTechLogo() { + return gregtechLogo != null ? gregtechLogo : DEFAULT.gregtechLogo; + } + + // endregion +} diff --git a/src/main/java/gregtech/api/gui/modularui/IDataFollowerWidget.java b/src/main/java/gregtech/api/gui/modularui/IDataFollowerWidget.java new file mode 100644 index 0000000000..393b8431e2 --- /dev/null +++ b/src/main/java/gregtech/api/gui/modularui/IDataFollowerWidget.java @@ -0,0 +1,50 @@ +package gregtech.api.gui.modularui; + +import java.util.function.Consumer; +import java.util.function.Function; + +import com.gtnewhorizons.modularui.api.widget.Widget; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import gregtech.api.util.ISerializableObject; +import gregtech.common.gui.modularui.widget.DataControllerWidget; + +/** + * Widget whose state is controlled by specific data. Data can be anything, e.g. {@link ISerializableObject} or machine + * recipe mode. <br> + * No widgets implementing this interface should not sync; Instead, {@link DataControllerWidget} will sync data, either + * when this widget triggers update on client or data update is detected on server. + * + * @param <T> Data type stored in the parent widget + * @param <U> State type stored in this widget + * @see DataControllerWidget + */ +@SuppressWarnings("UnusedReturnValue") +public interface IDataFollowerWidget<T, U> { + + /** + * Sets function to get widget state from provided data. This function will be called when client receives data from + * server and {@link DataControllerWidget} updates all children, including this widget. + */ + Widget setDataToStateGetter(Function<T, U> dataToStateGetter); + + /** + * Sets setter called when this widget gets action from player. Basically the same functionality with widgets that + * have getter/setter. + */ + Widget setStateSetter(Consumer<U> setter); + + /** + * Updates state of this widget with provided data. On server {@link DataControllerWidget} won't propagate data + * update to this widget, so this method is client-only. + */ + @SideOnly(Side.CLIENT) + void updateState(T data); + + /** + * Called on {@link Widget#onPostInit}. + */ + @SuppressWarnings("OverrideOnly") // So IntelliJ doesn't warn about the Widget#onPostInit link in the javadoc + default void onPostInit() {} +} diff --git a/src/main/java/gregtech/api/gui/modularui/SteamTexture.java b/src/main/java/gregtech/api/gui/modularui/SteamTexture.java new file mode 100644 index 0000000000..301671afe8 --- /dev/null +++ b/src/main/java/gregtech/api/gui/modularui/SteamTexture.java @@ -0,0 +1,62 @@ +package gregtech.api.gui.modularui; + +import com.gtnewhorizons.modularui.api.drawable.AdaptableUITexture; +import com.gtnewhorizons.modularui.api.drawable.UITexture; + +import gregtech.api.enums.SteamVariant; + +/** + * Wrapper for {@link UITexture}s used to ease in choosing between Bronze, Steel and Primitive textures. + */ +public class SteamTexture { + + private final UITexture bronzeTexture; + private final UITexture steelTexture; + private final UITexture primitiveTexture; + + private SteamTexture(UITexture bronzeTexture, UITexture steelTexture, UITexture primitiveTexture) { + this.bronzeTexture = bronzeTexture; + this.steelTexture = steelTexture; + this.primitiveTexture = primitiveTexture; + } + + public static SteamTexture fullImage(String mod, String location) { + return new SteamTexture( + UITexture.fullImage(mod, String.format(location, SteamVariant.BRONZE)), + UITexture.fullImage(mod, String.format(location, SteamVariant.STEEL)), + UITexture.fullImage(mod, String.format(location, SteamVariant.PRIMITIVE))); + } + + public static SteamTexture adaptableTexture(String mod, String location, int imageWidth, int imageHeight, + int borderWidthPixel) { + return new SteamTexture( + AdaptableUITexture + .of(mod, String.format(location, SteamVariant.BRONZE), imageWidth, imageHeight, borderWidthPixel), + AdaptableUITexture + .of(mod, String.format(location, SteamVariant.STEEL), imageWidth, imageHeight, borderWidthPixel), + AdaptableUITexture + .of(mod, String.format(location, SteamVariant.PRIMITIVE), imageWidth, imageHeight, borderWidthPixel)); + } + + public UITexture get(SteamVariant variant) { + return switch (variant) { + case BRONZE -> bronzeTexture; + case STEEL -> steelTexture; + case PRIMITIVE -> primitiveTexture; + default -> null; + }; + } + + public AdaptableUITexture getAdaptable(SteamVariant variant) { + return switch (variant) { + case BRONZE -> (AdaptableUITexture) bronzeTexture; + case STEEL -> (AdaptableUITexture) steelTexture; + case PRIMITIVE -> (AdaptableUITexture) primitiveTexture; + default -> null; + }; + } + + public UITexture get(boolean isHighPressure) { + return isHighPressure ? steelTexture : bronzeTexture; + } +} diff --git a/src/main/java/gregtech/api/gui/widgets/GT_CoverTickRateButton.java b/src/main/java/gregtech/api/gui/widgets/GT_CoverTickRateButton.java new file mode 100644 index 0000000000..883ffb4079 --- /dev/null +++ b/src/main/java/gregtech/api/gui/widgets/GT_CoverTickRateButton.java @@ -0,0 +1,82 @@ +package gregtech.api.gui.widgets; + +import static gregtech.api.gui.modularui.GT_UITextures.OVERLAY_BUTTON_HOURGLASS; +import static gregtech.common.covers.CoverInfo.MAX_TICK_RATE_ADDITION; + +import java.util.List; + +import net.minecraft.util.StatCollector; + +import org.jetbrains.annotations.NotNull; + +import com.google.common.collect.ImmutableList; +import com.gtnewhorizons.modularui.api.drawable.UITexture; +import com.gtnewhorizons.modularui.api.widget.IWidgetBuilder; +import com.gtnewhorizons.modularui.api.widget.Widget; +import com.gtnewhorizons.modularui.common.widget.ButtonWidget; +import com.gtnewhorizons.modularui.common.widget.FakeSyncWidget; + +import gregtech.api.gui.modularui.GT_UITextures; +import gregtech.common.covers.CoverInfo; + +public class GT_CoverTickRateButton extends ButtonWidget { + + private static final UITexture BACKGROUND = GT_UITextures.BUTTON_COVER_NORMAL.getSubArea(0, 0, 1, 0.5f); + + private final CoverInfo coverInfo; + private int clientTickRate; + private int tickRateAddition; + + public GT_CoverTickRateButton(@NotNull CoverInfo coverInfo, @NotNull IWidgetBuilder<?> builder) { + this.coverInfo = coverInfo; + this.clientTickRate = coverInfo.getTickRate(); + this.tickRateAddition = coverInfo.getTickRateAddition(); + + super.setBackground(BACKGROUND, OVERLAY_BUTTON_HOURGLASS); + super.setOnClick(this::onClick); + super.dynamicTooltip(this::dynamicTooltip); + super.attachSyncer( + new FakeSyncWidget.IntegerSyncer(this.coverInfo::getTickRate, integer -> clientTickRate = integer), + builder, + (widget, aInt) -> notifyTooltipChange()) + .attachSyncer( + new FakeSyncWidget.IntegerSyncer( + this.coverInfo::getTickRateAddition, + integer -> tickRateAddition = integer), + builder); + + } + + private void onClick(@NotNull ClickData clickData, @NotNull Widget widget) { + final int iterations = clickData.ctrl ? 5 : 1; + final boolean isDecreasing = clickData.mouseButton == 1; + + // Do five operations at once if Ctrl is held down. Since the actual increase granted by each invocation can be + // different on each call, just call the method several times rather than trying to do a bunch of weird math. + for (int i = 0; i < iterations; i++) { + coverInfo.adjustTickRateMultiplier(isDecreasing); + } + } + + private List<String> dynamicTooltip() { + final String boundsNotification; + + if (tickRateAddition == 0) { + boundsNotification = StatCollector.translateToLocal("gt.cover.info.button.bounds_notification.minimum"); + } else if (tickRateAddition >= MAX_TICK_RATE_ADDITION - 1) { + // Clamping can make tickRateAddition approach but never actually equal MAX_ADDITION, so we need this + // adjustment. + boundsNotification = StatCollector.translateToLocal("gt.cover.info.button.bounds_notification.maximum"); + } else { + boundsNotification = ""; + } + + return ImmutableList.of( + StatCollector.translateToLocalFormatted( + "gt.cover.info.button.tick_rate.1", + new CoverInfo.ClientTickRateFormatter(clientTickRate), + boundsNotification), + StatCollector.translateToLocal("gt.cover.info.button.tick_rate.2"), + StatCollector.translateToLocal("gt.cover.info.button.tick_rate.3")); + } +} diff --git a/src/main/java/gregtech/api/gui/widgets/GT_GuiCoverTabLine.java b/src/main/java/gregtech/api/gui/widgets/GT_GuiCoverTabLine.java new file mode 100644 index 0000000000..6f4eb0e2c2 --- /dev/null +++ b/src/main/java/gregtech/api/gui/widgets/GT_GuiCoverTabLine.java @@ -0,0 +1,179 @@ +package gregtech.api.gui.widgets; + +import java.awt.Rectangle; +import java.util.List; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.inventory.GuiContainer; +import net.minecraft.item.ItemStack; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.StatCollector; +import net.minecraftforge.common.util.ForgeDirection; + +import org.lwjgl.opengl.GL11; + +import codechicken.nei.api.API; +import codechicken.nei.api.INEIGuiAdapter; +import gregtech.api.enums.GT_Values; +import gregtech.api.gui.GT_GUIContainerMetaTile_Machine; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.net.GT_Packet_GtTileEntityGuiRequest; +import gregtech.common.GT_Proxy; + +/** + * Let's you access a GregTech IGregTechTileEntity's covers as tabs on the GUI's sides + */ +public class GT_GuiCoverTabLine extends GT_GuiTabLine { + + // Names of the block a cover could be on + private static final String[] SIDES = new String[] { "GT5U.interface.coverTabs.down", "GT5U.interface.coverTabs.up", + "GT5U.interface.coverTabs.north", "GT5U.interface.coverTabs.south", "GT5U.interface.coverTabs.west", + "GT5U.interface.coverTabs.east" }; + + // Not sure if there's a point in JIT translation but that's what this is + private final String[] translatedSides; + private final IGregTechTileEntity tile; + private final int colorization; + + /** + * Let's you access an IGregTechTileEntity's covers as tabs on the GUI's sides + * + * @param gui GT_ITabRenderer gui which this tab line attaches to + * @param tabLineLeft left position of the tab line in relation to the gui + * @param tabLineTop top position of the tab line in relation to the gui + * @param tabHeight height of a tab + * @param tabWidth width of a tab + * @param tabSpacing pixels between each tab + * @param xDir whether to extend the line horizontally to the right (NORMAL), the left (INVERSE) or not at + * all (NONE) + * @param yDir whether to extend the line vertically down (NORMAL), up (INVERSE) or not at all (NONE) + * @param displayMode whether to display on the left side of the GT_ITabRenderer (NORMAL), on it's right side + * (INVERSE) or not at all (NONE) + * @param tabBackground the set of textures used to draw this tab line's tab backgrounds + * @param tile The IGregTechTileEntity the covers of which we are accessing + * @param colorization The colorization of the GUI we are adding tabs to + */ + public GT_GuiCoverTabLine(GT_GUIContainerMetaTile_Machine gui, int tabLineLeft, int tabLineTop, int tabHeight, + int tabWidth, int tabSpacing, DisplayStyle xDir, DisplayStyle yDir, DisplayStyle displayMode, + GT_GuiTabIconSet tabBackground, IGregTechTileEntity tile, int colorization) { + super(gui, 6, tabLineLeft, tabLineTop, tabHeight, tabWidth, tabSpacing, xDir, yDir, displayMode, tabBackground); + this.tile = tile; + this.colorization = colorization; + this.translatedSides = new String[6]; + setupTabs(); + } + + /** + * Add a tab for each existing cover on this IGregTechTileEntity at creation time + */ + private void setupTabs() { + for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) { + final ItemStack cover = tile.getCoverItemAtSide(side); + if (cover != null) { + addCoverToTabs(side, cover); + } + } + } + + @Override + protected void drawBackground(float parTicks, int mouseX, int mouseY) { + // Apply this tile's coloration to draw the background + GL11.glColor3ub( + (byte) ((colorization >> 16) & 0xFF), + (byte) ((colorization >> 8) & 0xFF), + (byte) (colorization & 0xFF)); + super.drawBackground(parTicks, mouseX, mouseY); + } + + @Override + protected void tabClicked(int tabId, int mouseButton) { + if (mouseButton == 0 && mTabs[tabId].enabled) { + GT_Values.NW.sendToServer( + new GT_Packet_GtTileEntityGuiRequest( + this.tile.getXCoord(), + this.tile.getYCoord(), + this.tile.getZCoord(), + tabId + GT_Proxy.GUI_ID_COVER_SIDE_BASE, + this.tile.getWorld().provider.dimensionId, + Minecraft.getMinecraft().thePlayer.getEntityId(), + 0)); + } + } + + /** + * Add the cover on this side of the IGregTechTileEntity to the tabs + * + * @param side side to apply the cover to + * @param cover cover to add + */ + private void addCoverToTabs(ForgeDirection side, ItemStack cover) { + final boolean enabled = this.tile.getCoverBehaviorAtSideNew(side) + .hasCoverGUI(); + final int ordinalSide = side.ordinal(); + this.setTab(ordinalSide, cover, null, getTooltipForCoverTab(side, cover, enabled)); + this.setTabEnabled(ordinalSide, enabled); + } + + /** + * Decorate the cover's tooltips according to the side it's on and on whether the tab is enabled or not + * + * @param side side + * @param cover cover which tooltip to decorate + * @param enabled if the tab is enabled + * @return This cover tab's tooltip + */ + private String[] getTooltipForCoverTab(ForgeDirection side, ItemStack cover, boolean enabled) { + final List<String> tooltip = cover.getTooltip(Minecraft.getMinecraft().thePlayer, true); + tooltip.set( + 0, + (enabled ? EnumChatFormatting.UNDERLINE : EnumChatFormatting.DARK_GRAY) + getSideDescription(side) + + (enabled ? EnumChatFormatting.RESET + ": " : ": " + EnumChatFormatting.RESET) + + tooltip.get(0)); + return tooltip.toArray(new String[0]); + } + + /** + * Get the translated name for a side of the IGregTechTileEntity + * + * @param side side of the entity + * @return translated name for a side of the IGregTechTileEntity + */ + private String getSideDescription(ForgeDirection side) { + final int ordinalSide = side.ordinal(); + if (ordinalSide < SIDES.length) { + if (this.translatedSides[ordinalSide] == null) { + this.translatedSides[ordinalSide] = StatCollector.translateToLocal(SIDES[ordinalSide]); + } + return this.translatedSides[ordinalSide]; + } + return null; + } + + /** + * Hide any NEI slots that would intersect with a cover tab + */ + static class CoverTabLineNEIHandler extends INEIGuiAdapter { + + @Override + public boolean hideItemPanelSlot(GuiContainer gui, int x, int y, int w, int h) { + final Rectangle neiSlotArea = new Rectangle(x, y, w, h); + if (gui instanceof GT_GUIContainerMetaTile_Machine) { + final GT_GuiTabLine tabLine = ((GT_GUIContainerMetaTile_Machine) gui).coverTabs; + if (!tabLine.visible) { + return false; + } + for (int i = 0; i < tabLine.mTabs.length; i++) { + if (tabLine.mTabs[i] != null && tabLine.mTabs[i].getBounds() + .intersects(neiSlotArea)) { + return true; + } + } + } + return false; + } + } + + static { + API.registerNEIGuiHandler(new CoverTabLineNEIHandler()); + } +} diff --git a/src/main/java/gregtech/api/gui/widgets/GT_GuiFakeItemButton.java b/src/main/java/gregtech/api/gui/widgets/GT_GuiFakeItemButton.java new file mode 100644 index 0000000000..9f4287a65b --- /dev/null +++ b/src/main/java/gregtech/api/gui/widgets/GT_GuiFakeItemButton.java @@ -0,0 +1,162 @@ +package gregtech.api.gui.widgets; + +import java.awt.Rectangle; +import java.util.List; + +import net.minecraft.client.Minecraft; +import net.minecraft.item.ItemBlock; +import net.minecraft.item.ItemStack; + +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL12; + +import codechicken.lib.gui.GuiDraw; +import gregtech.api.interfaces.IGuiScreen; +import gregtech.api.util.GT_UtilityClient; + +public class GT_GuiFakeItemButton implements IGuiScreen.IGuiElement { + + private GT_GuiIcon bgIcon; + private ItemStack item; + private final IGuiScreen gui; + private int xPosition, yPosition; + private List<String> itemTooltips; + private final GT_GuiTooltip tooltip = new GT_GuiTooltip(null) { + + @Override + public List<String> getToolTipText() { + return itemTooltips; + } + + @Override + public boolean isDelayed() { + return false; + } + + @Override + public Rectangle getBounds() { + return GT_GuiFakeItemButton.this.getBounds(); + } + }; + private final Rectangle rectangle; + private boolean mimicSlot; + + public GT_GuiFakeItemButton(IGuiScreen gui, int x, int y, GT_GuiIcon bgIcon) { + this.gui = gui; + this.bgIcon = bgIcon; + item = null; + rectangle = new Rectangle(x, y, 18, 18); + gui.addElement(this); + } + + public GT_GuiFakeItemButton setItem(ItemStack i) { + item = i; + if (getMimicSlot()) updateTooltip(); + return this; + } + + private void updateTooltip() { + itemTooltips = item == null ? null : GT_UtilityClient.getTooltip(item, true); + } + + public ItemStack getItem() { + return item; + } + + public GT_GuiFakeItemButton setMimicSlot(boolean mimicSlot) { + if (mimicSlot != this.mimicSlot) { + if (mimicSlot) { + updateTooltip(); + gui.addToolTip(tooltip); + } else { + gui.removeToolTip(tooltip); + } + this.mimicSlot = mimicSlot; + } + return this; + } + + public boolean getMimicSlot() { + return mimicSlot; + } + + public GT_GuiIcon getBgIcon() { + return bgIcon; + } + + public GT_GuiFakeItemButton setBgIcon(GT_GuiIcon bgIcon) { + this.bgIcon = bgIcon; + return this; + } + + @Override + public void onInit() { + xPosition = rectangle.x + gui.getGuiLeft(); + yPosition = rectangle.y + gui.getGuiTop(); + } + + @Override + public void onRemoved() { + if (mimicSlot) gui.removeToolTip(tooltip); + } + + @Override + public void draw(int mouseX, int mouseY, float parTicks) { + GL11.glColor4f(1, 1, 1, 1); + GL11.glPushAttrib(GL11.GL_ENABLE_BIT); + GL11.glEnable(GL11.GL_BLEND); + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + + if (bgIcon != null) { + GT_GuiIcon.render(bgIcon, xPosition - 1, yPosition - 1, 18, 18, 0, true); + } + + if (item != null) { + if (item.getItem() instanceof ItemBlock) { + GL11.glPushAttrib(GL11.GL_ENABLE_BIT); + GL11.glEnable(GL12.GL_RESCALE_NORMAL); + } + gui.getItemRenderer() + .renderItemAndEffectIntoGUI( + gui.getFontRenderer(), + Minecraft.getMinecraft() + .getTextureManager(), + item, + xPosition, + yPosition); + + if (item.getItem() instanceof ItemBlock) GL11.glPopAttrib(); + } + + if (getMimicSlot()) if (getBounds().contains(mouseX - gui.getGuiLeft(), mouseY - gui.getGuiTop())) { + GL11.glDisable(GL11.GL_LIGHTING); + GL11.glDisable(GL11.GL_DEPTH_TEST); + GL11.glColorMask(true, true, true, false); + GuiDraw.drawGradientRect(xPosition, yPosition, 16, 16, 0x80ffffff, 0x80ffffff); + GL11.glColorMask(true, true, true, true); + // no glEnable, state will be recovered by glPopAttrib + } + + GL11.glPopAttrib(); + } + + public Rectangle getBounds() { + return rectangle; + } + + public void setX(int x) { + rectangle.x = x; + } + + public void setY(int y) { + rectangle.y = y; + } + + public void setWidth(int width) { + rectangle.width = width; + } + + public void setHeight(int height) { + rectangle.height = height; + } +} diff --git a/src/main/java/gregtech/api/gui/widgets/GT_GuiIcon.java b/src/main/java/gregtech/api/gui/widgets/GT_GuiIcon.java new file mode 100644 index 0000000000..66ab27356e --- /dev/null +++ b/src/main/java/gregtech/api/gui/widgets/GT_GuiIcon.java @@ -0,0 +1,157 @@ +package gregtech.api.gui.widgets; + +import static gregtech.api.enums.Mods.GregTech; + +import java.util.Arrays; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.util.ResourceLocation; + +import gregtech.api.interfaces.IGuiIcon; + +public enum GT_GuiIcon implements IGuiIcon { + + BUTTON_NORMAL(0, 0, 0), + BUTTON_DOWN(0, 32, 0), + BUTTON_HIGHLIGHT(0, 32 * 2, 0), + BUTTON_HIGHLIGHT_DOWN(0, 32 * 3, 0), + BUTTON_DISABLED(0, 32 * 4, 0), + + DISABLE(0, 0, 32), + REDSTONE_OFF(0, 32, 32), + REDSTONE_ON(0, 32 * 2, 32), + CHECKMARK(0, 32 * 3, 32), + CROSS(0, 32 * 4, 32), + WHITELIST(0, 32 * 5, 32), + BLACKLIST(0, 32 * 6, 32), + PROGRESS(0, 32 * 7, 32), + + EXPORT(0, 0, 32 * 2), + IMPORT(0, 32, 32 * 2), + ALLOW_INPUT(0, 32 * 2, 32 * 2), + BLOCK_INPUT(0, 32 * 3, 32 * 2), + GREEN_ARROW_UP(0, 32 * 4, 32 * 2), + GREEN_ARROW_DOWN(0, 32 * 5, 32 * 2), + CYCLIC(0, 32 * 6, 32 * 2), + + AND_GATE(0, 0, 32 * 3), + NAND_GATE(0, 32, 32 * 3), + OR_GATE(0, 32 * 2, 32 * 3), + NOR_GATE(0, 32 * 3, 32 * 3), + ANALOG_MODE(0, 32 * 4, 32 * 3), + + SLOT_DARKGRAY(1, 176, 0, 18, 18), + SLOT_GRAY(1, 176, 18, 18, 18), + + TAB_NORMAL(2, 0, 0, 18, 20), + TAB_HIGHLIGHT(2, 18, 0, 18, 20), + TAB_DISABLED(2, 18 * 2, 0, 18, 20), + TAB_NORMAL_BRONZE(2, 0, 20, 18, 20), + TAB_HIGHLIGHT_BRONZE(2, 18, 20, 18, 20), + TAB_DISABLED_BRONZE(2, 18 * 2, 20, 18, 20), + TAB_NORMAL_STEEL(2, 0, 2 * 20, 18, 20), + TAB_HIGHLIGHT_STEEL(2, 18, 2 * 20, 18, 20), + TAB_DISABLED_STEEL(2, 18 * 2, 2 * 20, 18, 20), + TAB_NORMAL_BRICK(2, 0, 3 * 20, 18, 20), + TAB_HIGHLIGHT_BRICK(2, 18, 3 * 20, 18, 20), + TAB_DISABLED_BRICK(2, 18 * 2, 3 * 20, 18, 20), + TAB_INFO_GRAY(2, 220, 0, 18, 20), + TAB_INFO_BLUE(2, 220 + 18, 0, 18, 20),; + + private static final int T_SIZE = 256; + private static ResourceLocation[] TEXTURES = { new ResourceLocation(GregTech.ID, "textures/gui/GuiButtons.png"), + new ResourceLocation(GregTech.ID, "textures/gui/GuiCover.png"), + new ResourceLocation(GregTech.ID, "textures/gui/GuiTabs.png"), }; + + public final int x, y, width, height; + public final IGuiIcon overlay; + private final int texID; + + GT_GuiIcon(int texID, int x, int y, int width, int height, IGuiIcon overlay) { + this.x = x; + this.y = y; + this.width = width; + this.height = height; + this.overlay = overlay; + this.texID = texID; + } + + GT_GuiIcon(int texID, int x, int y) { + this(texID, x, y, 32, 32, null); + } + + GT_GuiIcon(int texID, int x, int y, int width, int height) { + this(texID, x, y, width, height, null); + } + + public static void render(IGuiIcon icon, double x, double y, double width, double height, double zLevel, + boolean doDraw) { + render(icon, x, y, width, height, zLevel, doDraw, false); + } + + public static void render(IGuiIcon icon, double x, double y, double width, double height, double zLevel, + boolean doDraw, boolean flipHoritontally) { + Tessellator tess = Tessellator.instance; + if (doDraw) { + Minecraft.getMinecraft().renderEngine.bindTexture(TEXTURES[icon.getTexId()]); + tess.startDrawingQuads(); + } + double minU = (double) (icon.getX() + (flipHoritontally ? icon.getWidth() : 0)) / T_SIZE; + double maxU = (double) (icon.getX() + (flipHoritontally ? 0 : icon.getWidth())) / T_SIZE; + double minV = (double) icon.getY() / T_SIZE; + double maxV = (double) (icon.getY() + icon.getHeight()) / T_SIZE; + tess.addVertexWithUV(x, y + height, zLevel, minU, maxV); + tess.addVertexWithUV(x + width, y + height, zLevel, maxU, maxV); + tess.addVertexWithUV(x + width, y + 0, zLevel, maxU, minV); + tess.addVertexWithUV(x, y + 0, zLevel, minU, minV); + + if (icon.getOverlay() != null) render(icon.getOverlay(), x, y, width, height, zLevel, false); + + if (doDraw) tess.draw(); + } + + /** + * This is intended to enable addon mods to register additional textures. They can then add to this enum using + * EnumHelper.addEnum or by creating their enum that implements IGuiIcon (still requires adding a texture here) + * + * @param location location of the texture to add + */ + public static void addTextures(ResourceLocation... location) { + if (location == null || location.length == 0) return; + + int startIndex = TEXTURES.length; + TEXTURES = Arrays.copyOf(TEXTURES, location.length); + System.arraycopy(location, 0, TEXTURES, startIndex, location.length); + } + + @Override + public int getX() { + return this.x; + } + + @Override + public int getY() { + return this.y; + } + + @Override + public int getWidth() { + return this.width; + } + + @Override + public int getHeight() { + return this.height; + } + + @Override + public int getTexId() { + return this.texID; + } + + @Override + public IGuiIcon getOverlay() { + return this.overlay; + } +} diff --git a/src/main/java/gregtech/api/gui/widgets/GT_GuiIconButton.java b/src/main/java/gregtech/api/gui/widgets/GT_GuiIconButton.java new file mode 100644 index 0000000000..d4bfe31404 --- /dev/null +++ b/src/main/java/gregtech/api/gui/widgets/GT_GuiIconButton.java @@ -0,0 +1,114 @@ +package gregtech.api.gui.widgets; + +import java.awt.Rectangle; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiButton; + +import org.lwjgl.opengl.GL11; + +import gregtech.api.interfaces.IGuiScreen; + +public class GT_GuiIconButton extends GuiButton implements IGuiScreen.IGuiElement { + + public static final int DEFAULT_WIDTH = 16; + public static final int DEFAULT_HEIGHT = 16; + + protected GT_GuiIcon icon; + private final int x0; + private final int y0; + protected final IGuiScreen gui; + + private GT_GuiTooltip tooltip; + + public GT_GuiIconButton(IGuiScreen gui, int id, int x, int y, GT_GuiIcon icon) { + super(id, x, y, DEFAULT_WIDTH, DEFAULT_HEIGHT, ""); + this.gui = gui; + this.icon = icon; + this.x0 = x; + this.y0 = y; + gui.addElement(this); + } + + @Override + public void onInit() { + if (tooltip != null) gui.addToolTip(tooltip); + xPosition = x0 + gui.getGuiLeft(); + yPosition = y0 + gui.getGuiTop(); + } + + @Override + public void draw(int mouseX, int mouseY, float parTicks) { + drawButton(Minecraft.getMinecraft(), mouseX, mouseY); + } + + @Override + public void drawButton(Minecraft mc, int mouseX, int mouseY) { + if (this.tooltip != null) this.tooltip.enabled = true; + + if (this.visible) { + // moused over + this.field_146123_n = mouseX >= this.xPosition && mouseY >= this.yPosition + && mouseX < this.xPosition + width + && mouseY < this.yPosition + height; + + mouseDragged(mc, mouseX, mouseY); + + GL11.glPushAttrib(GL11.GL_ENABLE_BIT); + GL11.glEnable(GL11.GL_BLEND); + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + + int x = xPosition; + int y = yPosition; + if (!this.field_146123_n) { + // GL11.glColor4f(200F/255F, 210F/255F, 1, 1); + } else GL11.glColor4f(1, 1, 1, 1); + + GT_GuiIcon.render(getButtonTexture(this.field_146123_n), x, y, width, height, 0, true); + + GL11.glColor4f(1, 1, 1, 1); + if (icon != null) { + GT_GuiIcon.render(icon, x, y, width, height, 0, true); + } + + GL11.glPopAttrib(); + } + } + + @Override + public void mouseReleased(int mouseX, int mouseY) { + this.gui.clearSelectedButton(); + if (mousePressed(Minecraft.getMinecraft(), mouseX, mouseY)) this.gui.buttonClicked(this); + } + + public GT_GuiIcon getButtonTexture(boolean mouseOver) { + if (!enabled) return GT_GuiIcon.BUTTON_DISABLED; + if (this.equals(this.gui.getSelectedButton())) + return mouseOver ? GT_GuiIcon.BUTTON_HIGHLIGHT_DOWN : GT_GuiIcon.BUTTON_DOWN; + + return mouseOver ? GT_GuiIcon.BUTTON_HIGHLIGHT : GT_GuiIcon.BUTTON_NORMAL; + } + + public GT_GuiIcon getIcon() { + return icon; + } + + public GT_GuiIconButton setIcon(GT_GuiIcon icon) { + this.icon = icon; + return this; + } + + public GT_GuiTooltip getTooltip() { + return tooltip; + } + + public GT_GuiIconButton setTooltipText(String... text) { + if (tooltip == null) tooltip = new GT_GuiTooltip(getBounds(), text); + else tooltip.setToolTipText(text); + return this; + } + + public Rectangle getBounds() { + return new Rectangle(x0, y0, width, height); + } +} diff --git a/src/main/java/gregtech/api/gui/widgets/GT_GuiIconCheckButton.java b/src/main/java/gregtech/api/gui/widgets/GT_GuiIconCheckButton.java new file mode 100644 index 0000000000..5b5007fef3 --- /dev/null +++ b/src/main/java/gregtech/api/gui/widgets/GT_GuiIconCheckButton.java @@ -0,0 +1,43 @@ +package gregtech.api.gui.widgets; + +import gregtech.api.interfaces.IGuiScreen; + +public class GT_GuiIconCheckButton extends GT_GuiIconButton { + + private final GT_GuiIcon checkedIcon; + private final GT_GuiIcon normalIcon; + private final String checkedTooltip; + private final String normalTooltip; + private boolean checked = false; + + public GT_GuiIconCheckButton(IGuiScreen gui, int id, int x, int y, GT_GuiIcon checkedIcon, GT_GuiIcon normalIcon) { + this(gui, id, x, y, checkedIcon, normalIcon, null, null); + } + + public GT_GuiIconCheckButton(IGuiScreen gui, int id, int x, int y, GT_GuiIcon checkedIcon, GT_GuiIcon normalIcon, + String checkedTooltip, String normalTooltip) { + super(gui, id, x, y, normalIcon); + this.checkedIcon = checkedIcon; + this.normalIcon = normalIcon; + this.checkedTooltip = checkedTooltip; + this.normalTooltip = normalTooltip; + } + + @Override + public GT_GuiIcon getButtonTexture(boolean mouseOver) { + if (!enabled) return GT_GuiIcon.BUTTON_DISABLED; + if (this.equals(super.gui.getSelectedButton())) + return mouseOver ? GT_GuiIcon.BUTTON_HIGHLIGHT_DOWN : GT_GuiIcon.BUTTON_DOWN; + return mouseOver ? GT_GuiIcon.BUTTON_HIGHLIGHT : GT_GuiIcon.BUTTON_NORMAL; + } + + public boolean isChecked() { + return checked; + } + + public void setChecked(boolean checked) { + super.setIcon(checked ? checkedIcon : normalIcon); + super.setTooltipText(checked ? checkedTooltip : normalTooltip); + this.checked = checked; + } +} diff --git a/src/main/java/gregtech/api/gui/widgets/GT_GuiIntegerTextBox.java b/src/main/java/gregtech/api/gui/widgets/GT_GuiIntegerTextBox.java new file mode 100644 index 0000000000..2d3c7374bd --- /dev/null +++ b/src/main/java/gregtech/api/gui/widgets/GT_GuiIntegerTextBox.java @@ -0,0 +1,73 @@ +package gregtech.api.gui.widgets; + +import java.awt.Rectangle; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiTextField; + +import gregtech.api.interfaces.IGuiScreen; + +public class GT_GuiIntegerTextBox extends GuiTextField implements IGuiScreen.IGuiElement { + + private final int x0, y0; + private final IGuiScreen gui; + public final int id; + private boolean enabled; + + public GT_GuiIntegerTextBox(IGuiScreen gui, int id, int x, int y, int width, int height) { + super(Minecraft.getMinecraft().fontRenderer, x, y, width, height); + super.setText(""); + this.id = id; + x0 = x; + y0 = y; + this.gui = gui; + enabled = true; + gui.addElement(this); + } + + @Override + public void onInit() { + xPosition = x0 + gui.getGuiLeft(); + yPosition = y0 + gui.getGuiTop(); + } + + @Override + public void draw(int mouseX, int mouseY, float parTicks) { + super.drawTextBox(); + } + + public Rectangle getBounds() { + return new Rectangle(x0, y0, width, height); + } + + public boolean validChar(char c, int key) { + return Character.isDigit(c); + } + + @Override + public boolean textboxKeyTyped(char c, int key) { + if (validChar(c, key) || c == 1 + || c == 3 + || c == 22 + || c == 24 + || key == 14 + || key == 199 + || key == 203 + || key == 205 + || key == 207 + || key == 211) { + return super.textboxKeyTyped(c, key); + } + return false; + } + + @Override + public void setEnabled(boolean p_146184_1_) { + super.setEnabled(p_146184_1_); + enabled = p_146184_1_; + } + + public boolean isEnabled() { + return enabled; + } +} diff --git a/src/main/java/gregtech/api/gui/widgets/GT_GuiSlotTooltip.java b/src/main/java/gregtech/api/gui/widgets/GT_GuiSlotTooltip.java new file mode 100644 index 0000000000..015c8c7697 --- /dev/null +++ b/src/main/java/gregtech/api/gui/widgets/GT_GuiSlotTooltip.java @@ -0,0 +1,24 @@ +package gregtech.api.gui.widgets; + +import java.awt.Rectangle; + +import net.minecraft.inventory.Slot; + +import gregtech.api.util.GT_TooltipDataCache.TooltipData; + +public class GT_GuiSlotTooltip extends GT_GuiTooltip { + + private final Slot slot; + + public GT_GuiSlotTooltip(Slot slot, TooltipData data) { + super(new Rectangle(slot.xDisplayPosition - 1, slot.yDisplayPosition - 1, 18, 18), data); + this.slot = slot; + } + + @Override + protected void onTick() { + super.onTick(); + // If disabled by super, stay disabled. + this.enabled = this.enabled && this.slot.getStack() == null; + } +} diff --git a/src/main/java/gregtech/api/gui/widgets/GT_GuiSmartTooltip.java b/src/main/java/gregtech/api/gui/widgets/GT_GuiSmartTooltip.java new file mode 100644 index 0000000000..ffae5c30e6 --- /dev/null +++ b/src/main/java/gregtech/api/gui/widgets/GT_GuiSmartTooltip.java @@ -0,0 +1,27 @@ +package gregtech.api.gui.widgets; + +import java.awt.Rectangle; + +import gregtech.api.util.GT_TooltipDataCache.TooltipData; + +public class GT_GuiSmartTooltip extends GT_GuiTooltip { + + public interface TooltipVisibilityProvider { + + boolean shouldShowTooltip(); + } + + private final TooltipVisibilityProvider visibilityProvider; + + public GT_GuiSmartTooltip(Rectangle bounds, TooltipVisibilityProvider visibilityProvider, TooltipData data) { + super(bounds, data); + this.visibilityProvider = visibilityProvider; + } + + @Override + protected void onTick() { + super.onTick(); + // If disabled by super, stay disabled. + this.enabled = this.enabled && this.visibilityProvider.shouldShowTooltip(); + } +} diff --git a/src/main/java/gregtech/api/gui/widgets/GT_GuiTab.java b/src/main/java/gregtech/api/gui/widgets/GT_GuiTab.java new file mode 100644 index 0000000000..d06c2bd2eb --- /dev/null +++ b/src/main/java/gregtech/api/gui/widgets/GT_GuiTab.java @@ -0,0 +1,174 @@ +package gregtech.api.gui.widgets; + +import java.awt.Rectangle; + +import net.minecraft.client.Minecraft; +import net.minecraft.item.ItemBlock; +import net.minecraft.item.ItemStack; + +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL12; + +import gregtech.api.gui.widgets.GT_GuiTabLine.GT_GuiTabIconSet; +import gregtech.api.gui.widgets.GT_GuiTabLine.GT_ITabRenderer; +import gregtech.api.interfaces.IGuiIcon; + +/** + * A tab to be attached to a tab line + */ +public class GT_GuiTab { + + private static final int SLOT_SIZE = 18; + + public boolean visible = true, mousedOver, enabled = true; + + private Rectangle bounds; + private final GT_GuiTabIconSet tabBackground; + private final ItemStack item; + private final GT_ITabRenderer gui; + private GT_GuiTooltip tooltip; + private final IGuiIcon overlay; + private final boolean flipHorizontally; + + /** + * A tab to be attached to a tab line + * + * @param gui IGregTechTileEntity the tab line this tab belongs to is attached to + * @param id both the ID and position in the tab line of this tab. Not used, kept for compatibility. + * @param bounds bounds of this tab + * @param tabBackground set of background textures + * @param item item to draw atop the background texture, not colored + * @param overlay texture to draw atop the background texture, not colored + * @param tooltipText tooltip of this tab + * @param flipHorizontally whether to draw this tab on the right side of the IGregTechTileEntity + */ + public GT_GuiTab(GT_ITabRenderer gui, int id, Rectangle bounds, GT_GuiTabIconSet tabBackground, ItemStack item, + IGuiIcon overlay, String[] tooltipText, boolean flipHorizontally) { + this.gui = gui; + this.bounds = bounds; + this.item = item; + this.tabBackground = tabBackground; + this.overlay = overlay; + if (tooltipText != null) { + setTooltipText(tooltipText); + } + this.flipHorizontally = flipHorizontally; + } + + public GT_GuiTab(GT_ITabRenderer gui, int id, Rectangle bounds, GT_GuiTabIconSet tabBackground) { + this(gui, id, bounds, tabBackground, null, null, null, false); + } + + /** + * Set this tab's tooltip text + * + * @param text text to set + * @return This tab for chaining + */ + public GT_GuiTab setTooltipText(String... text) { + if (tooltip == null) { + tooltip = new GT_GuiTooltip(bounds, text); + gui.addToolTip(tooltip); + } else { + tooltip.setToolTipText(text); + } + return this; + } + + /** + * @return This tab's tooltip object + */ + public GT_GuiTooltip getTooltip() { + return tooltip; + } + + /** + * Draw the background texture for this tab + * + * @param mouseX not used, likely kept for backward compatibility + * @param mouseY not used, likely kept for backward compatibility + * @param parTicks not used, likely kept for backward compatibility + */ + public void drawBackground(int mouseX, int mouseY, float parTicks) { + if (this.visible) { + GT_GuiIcon.render( + getBackgroundTexture(), + bounds.x, + bounds.y, + bounds.width, + bounds.height, + 1, + true, + this.flipHorizontally); + } + } + + /** + * Draw overlay textures and items atop the background texture + * + * @param mouseX X mouse coordinate + * @param mouseY Y mouse coordinate + * @param parTicks not used, likely kept for backward compatibility + */ + public void drawOverlays(int mouseX, int mouseY, float parTicks) { + this.mousedOver = bounds.contains(mouseX, mouseY); + + if (this.tooltip != null) { + this.tooltip.enabled = this.visible; + } + + if (this.visible) { + if (overlay != null) { + GL11.glColor4f(1, 1, 1, 1); + GT_GuiIcon.render(overlay, bounds.x, bounds.y, bounds.width, bounds.height, 1, true); + } + if (item != null) { + GL11.glPushAttrib(GL11.GL_ENABLE_BIT); + + if (item.getItem() instanceof ItemBlock) { + GL11.glPushAttrib(GL11.GL_ENABLE_BIT); + GL11.glEnable(GL12.GL_RESCALE_NORMAL); + } + int margin = (bounds.height - SLOT_SIZE); + gui.getItemRenderer() + .renderItemAndEffectIntoGUI( + gui.getFontRenderer(), + Minecraft.getMinecraft() + .getTextureManager(), + item, + bounds.x + (this.flipHorizontally ? 0 : margin), + bounds.y + margin); + + if (item.getItem() instanceof ItemBlock) GL11.glPopAttrib(); + + GL11.glPopAttrib(); + } + } + } + + /** + * @return the texture this tab should currently use as it's background + */ + protected IGuiIcon getBackgroundTexture() { + if (!enabled) return tabBackground.disabled; + + return mousedOver ? tabBackground.highlight : tabBackground.normal; + } + + /** + * @return the screen space occupied by this tab + */ + public Rectangle getBounds() { + return this.bounds; + } + + /** + * Reposition this tab on the screen + * + * @param xPos X tab coordinate + * @param yPos Y tab coordinate + */ + public void setPosition(int xPos, int yPos) { + this.bounds = new Rectangle(xPos, yPos, bounds.width, bounds.height); + } +} diff --git a/src/main/java/gregtech/api/gui/widgets/GT_GuiTabLine.java b/src/main/java/gregtech/api/gui/widgets/GT_GuiTabLine.java new file mode 100644 index 0000000000..950478cdfa --- /dev/null +++ b/src/main/java/gregtech/api/gui/widgets/GT_GuiTabLine.java @@ -0,0 +1,274 @@ +package gregtech.api.gui.widgets; + +import java.awt.Rectangle; + +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.client.renderer.entity.RenderItem; +import net.minecraft.item.ItemStack; + +import org.lwjgl.opengl.GL11; + +import gregtech.api.interfaces.IGuiIcon; + +/** + * Draws clickable and configurable tabs on the left or right side of another GUI + */ +public class GT_GuiTabLine { + + /** + * Defines a set of textures a tab line can use to render it's tab backgrounds + */ + public static class GT_GuiTabIconSet { + + public IGuiIcon disabled; + public IGuiIcon normal; + public IGuiIcon highlight; + + public GT_GuiTabIconSet(IGuiIcon normalIcon, IGuiIcon highlightIcon, IGuiIcon disabledIcon) { + this.normal = normalIcon; + this.highlight = highlightIcon; + this.disabled = disabledIcon; + } + } + + /** + * Controls the rendering style of the tab line + */ + public enum DisplayStyle { + + NONE((byte) 0), + NORMAL((byte) 1), + INVERSE((byte) -1); + + private final byte value; + + DisplayStyle(byte value) { + this.value = value; + } + + public byte getValue() { + return value; + } + } + + /** + * A GUI should implement these methods as well as call the tab line's onMouseClicked, onInit and drawTabs for the + * tab line to attach to it properly. + */ + public interface GT_ITabRenderer { + + int getGuiLeft(); + + int getGuiTop(); + + int getXSize(); + + RenderItem getItemRenderer(); + + FontRenderer getFontRenderer(); + + void addToolTip(GT_GuiTooltip tooltip); + + boolean removeToolTip(GT_GuiTooltip tooltip); + } + + // The tabs are arranged according to their index in this array + protected final GT_GuiTab[] mTabs; + + private final int tabLineLeft; + private final int tabLineTop; + private final int tabHeight; + private final int tabWidth; + private final int tabSpacing; + + // In which direction to draw the tab line + private final DisplayStyle xDir; + private final DisplayStyle yDir; + + // Whether to display on the right side of the GT_ITabRenderer instead of left + protected final boolean flipHorizontally; + protected final boolean visible; + + private final GT_GuiTabIconSet tabBackground; + private final GT_ITabRenderer gui; + + /** + * Draws clickable and configurable tabs on the left or right side of a GT_ITabRenderer + * + * @param gui GT_ITabRenderer gui which this tab line attaches to + * @param numTabs number of tab positions in this tab line + * @param tabLineLeft left position of the tab line in relation to the gui + * @param tabLineTop top position of the tab line in relation to the gui + * @param tabHeight height of a tab + * @param tabWidth width of a tab + * @param tabSpacing pixels between each tab + * @param xDir whether to extend the line horizontally to the right (NORMAL), the left (INVERSE) or not at + * all (NONE) + * @param yDir whether to extend the line vertically down (NORMAL), up (INVERSE) or not at all (NONE) + * @param displayMode whether to display on the left side of the GT_ITabRenderer (NORMAL), on it's right side + * (INVERSE) or not at all (NONE) + * @param tabBackground the set of textures used to draw this tab line's tab backgrounds + */ + public GT_GuiTabLine(GT_ITabRenderer gui, int numTabs, int tabLineLeft, int tabLineTop, int tabHeight, int tabWidth, + int tabSpacing, DisplayStyle xDir, DisplayStyle yDir, DisplayStyle displayMode, + GT_GuiTabIconSet tabBackground) { + this.gui = gui; + this.mTabs = new GT_GuiTab[numTabs]; + this.tabLineLeft = tabLineLeft; + this.tabLineTop = tabLineTop; + this.tabHeight = tabHeight; + this.tabWidth = tabWidth; + this.tabSpacing = tabSpacing; + this.xDir = xDir; + this.yDir = yDir; + this.tabBackground = tabBackground; + this.flipHorizontally = displayMode == DisplayStyle.INVERSE; + this.visible = !(displayMode == DisplayStyle.NONE); + } + + /** + * Creates a new tab at the specified position with the given parameters. This class handles the positioning. + * + * @param tabId tab ID + * @param item item to draw atop the background texture, not colored + * @param overlay texture to draw atop the background texture, not colored + * @param text tooltip of this tab + */ + public void setTab(int tabId, ItemStack item, IGuiIcon overlay, String[] text) { + mTabs[tabId] = new GT_GuiTab( + this.gui, + tabId, + getBoundsForTab(tabId), + this.tabBackground, + item, + overlay, + text, + this.flipHorizontally); + } + + /** + * Get the bounds a given tab should occupy + * + * @param tabId tab ID + * @return tab bounds + */ + protected Rectangle getBoundsForTab(int tabId) { + return new Rectangle(getTabX(tabId), getTabY(tabId), this.tabWidth, this.tabHeight); + } + + /** + * Enable or disable a tab. Disabled tabs have a dark background. + * + * @param tabId tab ID + * @param enable true to enable, false to disable + */ + public void setTabEnabled(int tabId, boolean enable) { + if (mTabs[tabId] != null) { + mTabs[tabId].enabled = enable; + } + } + + /** + * Draw the tabs for this tab bar GT_ITabRenderer must call this method on drawGuiContainerBackgroundLayer or on + * drawScreen. + * + * @param parTicks + */ + public void drawTabs(float parTicks, int mouseX, int mouseY) { + if (this.visible) { + GL11.glPushAttrib(GL11.GL_ENABLE_BIT); + GL11.glEnable(GL11.GL_BLEND); + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + drawBackground(parTicks, mouseX, mouseY); + drawOverlays(parTicks, mouseX, mouseY); + GL11.glPopAttrib(); + } + } + + /** + * Draw the tab's backgrounds first + * + * @param parTicks not used, likely kept for compatibility + * @param mouseX mouse X position + * @param mouseY mouse Y position + */ + protected void drawOverlays(float parTicks, int mouseX, int mouseY) { + for (GT_GuiTab mTab : mTabs) { + if (mTab != null) { + mTab.drawOverlays(mouseX, mouseY, parTicks); + } + } + } + + /** + * Draw anything that overlays the tab's background texture + * + * @param parTicks not used, likely kept for compatibility + * @param mouseX mouse X position + * @param mouseY mouse Y position + */ + protected void drawBackground(float parTicks, int mouseX, int mouseY) { + for (GT_GuiTab mTab : mTabs) { + if (mTab != null) { + mTab.drawBackground(mouseX, mouseY, parTicks); + } + } + } + + /** + * Call tabClick for every tab that was clicked. GT_ITabRenderer must call this method on mouseClicked. + * + * @param mouseX mouse X position + * @param mouseY mouse Y position + * @param mouseButton which mouse button was used to click + */ + public void onMouseClicked(int mouseX, int mouseY, int mouseButton) { + for (int tabId = 0; tabId < mTabs.length; tabId++) { + if (mTabs[tabId] != null && mTabs[tabId].getBounds() + .contains(mouseX, mouseY)) { + tabClicked(tabId, mouseButton); + return; + } + } + } + + /** + * Act on a tab being clicked. + * + * @param tabId tab ID + * @param mouseButton which mouse button was used to click + */ + protected void tabClicked(int tabId, int mouseButton) {} + + /** + * Reposition ourselves whenever the GT_ITabRenderer does so. GT_ITabRenderer must call this method on Init. + */ + public void onInit() { + for (int i = 0; i < mTabs.length; i++) { + if (mTabs[i] != null) { + mTabs[i].setPosition(getTabX(i), getTabY(i)); + } + } + } + + /** + * Get the proper X position for a given tab + * + * @param tabId tab ID + * @return X position of the tab + */ + private int getTabX(int tabId) { + return this.gui.getGuiLeft() + (flipHorizontally ? (gui.getXSize() - tabLineLeft - tabWidth) : tabLineLeft) + + (tabId * (tabWidth + tabSpacing) * xDir.getValue()); + } + + /** + * Get the proper Y position for a given tab + * + * @param tabId tab ID + * @return Y position of the tab + */ + private int getTabY(int tabId) { + return this.gui.getGuiTop() + tabLineTop + (tabId * (tabHeight + tabSpacing) * yDir.getValue()); + } +} diff --git a/src/main/java/gregtech/api/gui/widgets/GT_GuiTooltip.java b/src/main/java/gregtech/api/gui/widgets/GT_GuiTooltip.java new file mode 100644 index 0000000000..fe20b2b57a --- /dev/null +++ b/src/main/java/gregtech/api/gui/widgets/GT_GuiTooltip.java @@ -0,0 +1,121 @@ +package gregtech.api.gui.widgets; + +import java.awt.Rectangle; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.lwjgl.input.Keyboard; + +import gregtech.api.util.GT_TooltipDataCache.TooltipData; + +public class GT_GuiTooltip { + + protected final Rectangle bounds; + protected TooltipData data; + private List<String> displayedText; + public boolean enabled = true; + + /** + * Used to create a tooltip that will appear over the specified bounds. This will initially be a "static" tooltip + * that doesn't respect verbosity levels or respond to the shift key. + * + * @param bounds tooltip bounds + * @param text tooltip text + */ + public GT_GuiTooltip(Rectangle bounds, String... text) { + this.bounds = bounds; + setToolTipText(text); + } + + /** + * Used to create a tooltip that will appear over the specified bounds. This will initially be a "dynamic" tooltip + * that respects verbosity levels and responds to the shift key. + * + * @param bounds tooltip bounds + * @param data tooltip data + */ + public GT_GuiTooltip(Rectangle bounds, TooltipData data) { + this.bounds = bounds; + // Trust that the tooltips have already been formatted and colored, just make sure it has no nulls + this.data = sanitizeTooltipData(data); + } + + private TooltipData sanitizeTooltipData(TooltipData data) { + if (data.text == null) { + data.text = Collections.emptyList(); + } + if (data.shiftText == null) { + data.shiftText = Collections.emptyList(); + } + return data; + } + + /** + * Called before the tooltip manager checks whether this tooltip is enabled + */ + protected void onTick() { + // Switch which of our 2 stored texts we're displaying now. + this.displayedText = Keyboard.isKeyDown(Keyboard.KEY_LSHIFT) ? this.data.shiftText : this.data.text; + // If this text is empty, let's not display a tooltip at all. + this.enabled = this.displayedText.size() != 0; + } + + /** + * Called once this tooltip has been determined to be enabled + */ + protected void updateText() {} + + /** + * Used to set a "static" tooltip that doesn't respect verbosity levels or respond to the shift key + * + * @param text tooltip text + */ + public void setToolTipText(String... text) { + this.data = formatTooltip(text); + this.displayedText = data.text; + } + + /** + * Used to set a "dynamic" tooltip that respects verbosity levels and responds to the shift key + * + * @param data tooltip data + */ + public void setToolTipText(TooltipData data) { + // Trust that the tooltips have already been formatted and colored, just make sure it has no nulls + this.data = sanitizeTooltipData(data); + } + + /** + * Apply tooltip colors in case the text doesn't contain them and return as tooltip data + * + * @param text text to apply the colors to + * @return colored tooltip lines as list + */ + protected TooltipData formatTooltip(String[] text) { + List<String> list; + if (text != null) { + list = new ArrayList<>(text.length); + for (String s : text) { + if (s == null) continue; + if (list.isEmpty()) list.add("\u00a7f" + s); + else list.add("\u00a77" + s); + } + } else { + list = Collections.emptyList(); + } + return new TooltipData(list, list); + } + + public List<String> getToolTipText() { + return this.displayedText; + } + + public Rectangle getBounds() { + return bounds; + } + + public boolean isDelayed() { + return true; + } +} diff --git a/src/main/java/gregtech/api/gui/widgets/GT_GuiTooltipManager.java b/src/main/java/gregtech/api/gui/widgets/GT_GuiTooltipManager.java new file mode 100644 index 0000000000..4304ca14fc --- /dev/null +++ b/src/main/java/gregtech/api/gui/widgets/GT_GuiTooltipManager.java @@ -0,0 +1,80 @@ +package gregtech.api.gui.widgets; + +import java.util.ArrayList; +import java.util.List; + +import net.minecraft.client.gui.FontRenderer; + +public class GT_GuiTooltipManager { + + public interface GT_IToolTipRenderer { + + int getGuiLeft(); + + int getGuiTop(); + + int getXSize(); + + FontRenderer getFontRenderer(); + + void drawHoveringText(List<String> text, int mouseX, int mouseY, FontRenderer font); + } + + private static final long DELAY = 5; + private int mouseStopped; + private int lastMouseX = -1; + private int lastMouseY = -1; + private final List<GT_GuiTooltip> tips = new ArrayList<>(); + + public void addToolTip(GT_GuiTooltip tip) { + if (tip != null && !tips.contains(tip)) tips.add(tip); + } + + public boolean removeToolTip(GT_GuiTooltip tip) { + return tips.remove(tip); + } + + public final void onTick(GT_IToolTipRenderer render, int mouseX, int mouseY) { + if ((Math.abs(mouseX - lastMouseX) < 2) && (Math.abs(mouseY - lastMouseY) < 2)) { + mouseStopped = Math.min(mouseStopped + 1, 50); + } else { + mouseStopped = 0; + } + + lastMouseX = mouseX; + lastMouseY = mouseY; + + mouseX -= render.getGuiLeft(); + mouseY -= render.getGuiTop(); + for (GT_GuiTooltip tip : tips) { + // Give the tooltip the opportunity to decide whether they should be enabled + tip.onTick(); + if (tip.enabled && (!tip.isDelayed() || mouseStopped > DELAY) + && tip.getBounds() + .contains(mouseX, mouseY)) { + tip.updateText(); + drawTooltip(tip, mouseX, mouseY, render); + break; + } + } + } + + private void drawTooltip(GT_GuiTooltip tip, int mouseX, int mouseY, GT_IToolTipRenderer render) { + List<String> text = tip.getToolTipText(); + if (text == null) return; + + if (mouseX > render.getGuiLeft() + render.getXSize() / 2) { + int maxWidth = 0; + for (String s : text) { + int w = render.getFontRenderer() + .getStringWidth(s); + if (w > maxWidth) { + maxWidth = w; + } + } + mouseX -= (maxWidth + 18); + } + + render.drawHoveringText(text, mouseX, mouseY, render.getFontRenderer()); + } +} diff --git a/src/main/java/gregtech/api/gui/widgets/GT_LockedWhileActiveButton.java b/src/main/java/gregtech/api/gui/widgets/GT_LockedWhileActiveButton.java new file mode 100644 index 0000000000..9a93a8fadf --- /dev/null +++ b/src/main/java/gregtech/api/gui/widgets/GT_LockedWhileActiveButton.java @@ -0,0 +1,90 @@ +package gregtech.api.gui.widgets; + +import java.util.Arrays; +import java.util.List; +import java.util.function.BiConsumer; +import java.util.function.Supplier; + +import net.minecraft.util.StatCollector; + +import org.jetbrains.annotations.NotNull; + +import com.google.common.collect.ImmutableList; +import com.gtnewhorizons.modularui.api.drawable.IDrawable; +import com.gtnewhorizons.modularui.api.screen.ModularWindow; +import com.gtnewhorizons.modularui.api.widget.Widget; +import com.gtnewhorizons.modularui.common.widget.ButtonWidget; +import com.gtnewhorizons.modularui.common.widget.FakeSyncWidget; + +import gregtech.api.gui.modularui.GT_UITextures; +import gregtech.api.interfaces.tileentity.IMachineProgress; + +public class GT_LockedWhileActiveButton extends ButtonWidget { + + @NotNull + private final IMachineProgress machine; + + public GT_LockedWhileActiveButton(@NotNull IMachineProgress machine, @NotNull ModularWindow.Builder builder) { + super(); + this.machine = machine; + + super.attachSyncer( + new FakeSyncWidget.BooleanSyncer(machine::isActive, a -> {}), + builder, + (widget, aBoolean) -> widget.notifyTooltipChange()); + + super.dynamicTooltip(this::generateTooltip); + } + + @NotNull + @Override + public ButtonWidget setOnClick(@NotNull BiConsumer<ClickData, Widget> clickAction) { + return super.setOnClick((clickData, widget) -> { + if (!machine.isActive()) { + clickAction.accept(clickData, widget); + } + }); + } + + @NotNull + @Override + public Widget setBackground(@NotNull IDrawable... drawables) { + return super.setBackground(() -> appendLockedOverlay(drawables)); + } + + @NotNull + @Override + public Widget setBackground(@NotNull Supplier<IDrawable[]> drawablesSupplier) { + return super.setBackground(() -> appendLockedOverlay(drawablesSupplier.get())); + } + + @NotNull + @Override + public Widget dynamicTooltip(@NotNull Supplier<List<String>> dynamicTooltip) { + return super.dynamicTooltip(() -> { + ImmutableList.Builder<String> tooltips = ImmutableList.<String>builder() + .addAll(dynamicTooltip.get()); + tooltips.addAll(generateTooltip()); + + return tooltips.build(); + }); + } + + @NotNull + private IDrawable[] appendLockedOverlay(@NotNull IDrawable[] drawables) { + if (machine.isActive()) { + final IDrawable[] copy = Arrays.copyOf(drawables, drawables.length + 1); + copy[drawables.length] = GT_UITextures.OVERLAY_BUTTON_LOCKED; + return copy; + } + return drawables; + } + + @NotNull + private List<String> generateTooltip() { + if (machine.isActive()) { + return ImmutableList.of(StatCollector.translateToLocal("GT5U.gui.button.forbidden_while_running")); + } + return ImmutableList.of(); + } +} diff --git a/src/main/java/gregtech/api/interfaces/IBlockContainer.java b/src/main/java/gregtech/api/interfaces/IBlockContainer.java new file mode 100644 index 0000000000..5a80655a5c --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/IBlockContainer.java @@ -0,0 +1,10 @@ +package gregtech.api.interfaces; + +import net.minecraft.block.Block; + +public interface IBlockContainer { + + Block getBlock(); + + byte getMeta(); +} diff --git a/src/main/java/gregtech/api/interfaces/IBlockOnWalkOver.java b/src/main/java/gregtech/api/interfaces/IBlockOnWalkOver.java new file mode 100644 index 0000000000..0c8fce931b --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/IBlockOnWalkOver.java @@ -0,0 +1,9 @@ +package gregtech.api.interfaces; + +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.world.World; + +public interface IBlockOnWalkOver { + + void onWalkOver(EntityLivingBase aEntity, World aWorld, int aX, int aY, int aZ); +} diff --git a/src/main/java/gregtech/api/interfaces/IChunkLoader.java b/src/main/java/gregtech/api/interfaces/IChunkLoader.java new file mode 100644 index 0000000000..b597d6a71f --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/IChunkLoader.java @@ -0,0 +1,10 @@ +package gregtech.api.interfaces; + +import net.minecraft.world.ChunkCoordIntPair; + +// This interface is implemented by the machines that actively load a working chunk +public interface IChunkLoader { + + // return a working chunk coordinates, may be null + ChunkCoordIntPair getActiveChunk(); +} diff --git a/src/main/java/gregtech/api/interfaces/ICleanroom.java b/src/main/java/gregtech/api/interfaces/ICleanroom.java new file mode 100644 index 0000000000..61e339d050 --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/ICleanroom.java @@ -0,0 +1,22 @@ +package gregtech.api.interfaces; + +/** + * Classes implementing this interface can act as cleanroom, used to satisfy environment required for some recipes. + */ +public interface ICleanroom { + + /** + * @return Current cleanness of this cleanroom. Max at 10000. + */ + int getCleanness(); + + /** + * @return If this cleanroom is valid. + */ + boolean isValidCleanroom(); + + /** + * Release pollution to this cleanroom. + */ + void pollute(); +} diff --git a/src/main/java/gregtech/api/interfaces/ICleanroomReceiver.java b/src/main/java/gregtech/api/interfaces/ICleanroomReceiver.java new file mode 100644 index 0000000000..b0d42f9aec --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/ICleanroomReceiver.java @@ -0,0 +1,18 @@ +package gregtech.api.interfaces; + +import javax.annotation.Nullable; + +import net.minecraft.tileentity.TileEntity; + +/** + * Implement this interface for TileEntities that can have association to cleanroom. + * Calling {@link gregtech.common.GT_Pollution#addPollution(TileEntity, int)} from this machine + * will pollute associated cleanroom. + */ +public interface ICleanroomReceiver { + + @Nullable + ICleanroom getCleanroom(); + + void setCleanroom(ICleanroom cleanroom); +} diff --git a/src/main/java/gregtech/api/interfaces/IColorModulationContainer.java b/src/main/java/gregtech/api/interfaces/IColorModulationContainer.java new file mode 100644 index 0000000000..55053e1d12 --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/IColorModulationContainer.java @@ -0,0 +1,6 @@ +package gregtech.api.interfaces; + +public interface IColorModulationContainer { + + short[] getRGBA(); +} diff --git a/src/main/java/gregtech/api/interfaces/ICondition.java b/src/main/java/gregtech/api/interfaces/ICondition.java new file mode 100644 index 0000000000..9c7033b3d7 --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/ICondition.java @@ -0,0 +1,116 @@ +package gregtech.api.interfaces; + +public interface ICondition<O> { + + boolean isTrue(O aObject); + + // Utility Classes for adding relations between Conditions. + + class Not<O> implements ICondition<O> { + + private final ICondition<O> mCondition; + + public Not(ICondition<O> aCondition) { + mCondition = aCondition; + } + + @Override + public boolean isTrue(O aObject) { + return !mCondition.isTrue(aObject); + } + } + + class Or<O> implements ICondition<O> { + + private final ICondition<O>[] mConditions; + + @SafeVarargs + public Or(ICondition<O>... aConditions) { + mConditions = aConditions; + } + + @Override + public boolean isTrue(O aObject) { + for (ICondition<O> tCondition : mConditions) if (tCondition.isTrue(aObject)) return true; + return false; + } + } + + class Nor<O> implements ICondition<O> { + + private final ICondition<O>[] mConditions; + + @SafeVarargs + public Nor(ICondition<O>... aConditions) { + mConditions = aConditions; + } + + @Override + public boolean isTrue(O aObject) { + for (ICondition<O> tCondition : mConditions) if (tCondition.isTrue(aObject)) return false; + return true; + } + } + + class And<O> implements ICondition<O> { + + private final ICondition<O>[] mConditions; + + @SafeVarargs + public And(ICondition<O>... aConditions) { + mConditions = aConditions; + } + + @Override + public boolean isTrue(O aObject) { + for (ICondition<O> tCondition : mConditions) if (!tCondition.isTrue(aObject)) return false; + return true; + } + } + + class Nand<O> implements ICondition<O> { + + private final ICondition<O>[] mConditions; + + @SafeVarargs + public Nand(ICondition<O>... aConditions) { + mConditions = aConditions; + } + + @Override + public boolean isTrue(O aObject) { + for (ICondition<O> tCondition : mConditions) if (!tCondition.isTrue(aObject)) return true; + return false; + } + } + + class Xor<O> implements ICondition<O> { + + private final ICondition<O> mCondition1, mCondition2; + + public Xor(ICondition<O> aCondition1, ICondition<O> aCondition2) { + mCondition1 = aCondition1; + mCondition2 = aCondition2; + } + + @Override + public boolean isTrue(O aObject) { + return mCondition1.isTrue(aObject) != mCondition2.isTrue(aObject); + } + } + + class Equal<O> implements ICondition<O> { + + private final ICondition<O> mCondition1, mCondition2; + + public Equal(ICondition<O> aCondition1, ICondition<O> aCondition2) { + mCondition1 = aCondition1; + mCondition2 = aCondition2; + } + + @Override + public boolean isTrue(O aObject) { + return mCondition1.isTrue(aObject) == mCondition2.isTrue(aObject); + } + } +} diff --git a/src/main/java/gregtech/api/interfaces/IConfigurationCircuitSupport.java b/src/main/java/gregtech/api/interfaces/IConfigurationCircuitSupport.java new file mode 100644 index 0000000000..8dde8163c8 --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/IConfigurationCircuitSupport.java @@ -0,0 +1,47 @@ +package gregtech.api.interfaces; + +import java.util.List; + +import net.minecraft.item.ItemStack; + +import gregtech.api.GregTech_API; + +/** + * Implement this interface if your tileentity (or metatileentity) supports configuration circuits to resolve recipe + * conflicts. + */ +public interface IConfigurationCircuitSupport { + + /** + * + * @return Integrated circuit slot index in the machine inventory + */ + int getCircuitSlot(); + + /** + * Return a list of possible configuration circuit this machine expects. + * <p> + * This list is unmodifiable. Its elements are not supposed to be modified in any way! + */ + default List<ItemStack> getConfigurationCircuits() { + return GregTech_API.getConfigurationCircuitList(100); + } + + /** + * + * @return True if that machine supports built-in configuration circuit + */ + boolean allowSelectCircuit(); + + /** + * + * @return Circuit slot index in GUI container + */ + default int getCircuitGUISlot() { + return getCircuitSlot(); + } + + int getCircuitSlotX(); + + int getCircuitSlotY(); +} diff --git a/src/main/java/gregtech/api/interfaces/IDamagableItem.java b/src/main/java/gregtech/api/interfaces/IDamagableItem.java new file mode 100644 index 0000000000..5f0d53b577 --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/IDamagableItem.java @@ -0,0 +1,8 @@ +package gregtech.api.interfaces; + +import net.minecraft.item.ItemStack; + +public interface IDamagableItem { + + boolean doDamageToItem(ItemStack aStack, int aVanillaDamage); +} diff --git a/src/main/java/gregtech/api/interfaces/IDebugableBlock.java b/src/main/java/gregtech/api/interfaces/IDebugableBlock.java new file mode 100644 index 0000000000..9c6ab660bd --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/IDebugableBlock.java @@ -0,0 +1,24 @@ +package gregtech.api.interfaces; + +import java.util.ArrayList; + +import net.minecraft.entity.player.EntityPlayer; + +/** + * You are allowed to include this File in your Download, as i will not change it. + */ +public interface IDebugableBlock { + + /** + * Returns a Debug Message, for a generic DebugItem Blocks have to implement this interface NOT TileEntities! + * + * @param aPlayer the Player, who rightclicked with his Debug Item + * @param aX Block-Coordinate + * @param aY Block-Coordinate + * @param aZ Block-Coordinate + * @param aLogLevel the Log Level of the Debug Item. 0 = Obvious 1 = Visible for the regular Scanner 2 = Only + * visible to more advanced Scanners 3 = Debug ONLY + * @return a String-Array containing the DebugInfo, every Index is a separate line (0 = first Line) + */ + ArrayList<String> getDebugInfo(EntityPlayer aPlayer, int aX, int aY, int aZ, int aLogLevel); +} diff --git a/src/main/java/gregtech/api/interfaces/IDescribable.java b/src/main/java/gregtech/api/interfaces/IDescribable.java new file mode 100644 index 0000000000..21bb520482 --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/IDescribable.java @@ -0,0 +1,12 @@ +package gregtech.api.interfaces; + +/** + * To get simple things like a ToolTip Description + */ +public interface IDescribable { + + /** + * The Tooltip Text + */ + String[] getDescription(); +} diff --git a/src/main/java/gregtech/api/interfaces/IDragAndDropSupport.java b/src/main/java/gregtech/api/interfaces/IDragAndDropSupport.java new file mode 100644 index 0000000000..b516db5bad --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/IDragAndDropSupport.java @@ -0,0 +1,58 @@ +package gregtech.api.interfaces; + +import java.util.Collections; +import java.util.List; + +import net.minecraft.client.gui.inventory.GuiContainer; +import net.minecraft.item.ItemStack; + +import codechicken.nei.NEIClientUtils; +import codechicken.nei.VisiblityData; +import codechicken.nei.api.INEIGuiHandler; +import codechicken.nei.api.TaggedInventoryArea; +import cpw.mods.fml.common.Optional; +import gregtech.api.enums.Mods; + +/** + * Implement this interface if your GuiContainer supports Drag-And-Drop behavior on NEI. + */ +@Optional.Interface(modid = Mods.Names.NOT_ENOUGH_ITEMS, iface = "codechicken.nei.api.INEIGuiHandler") +public interface IDragAndDropSupport extends INEIGuiHandler { + + /** + * Implement this to handle Drag-And-Drop behavior. This may be invoked on normal click too + * ({@code isGhost==false}), so be careful if your slot supports both Drag-And-Drop and other behaviors e.g. fluid + * I/O with FluidDisplay click + * + * @param gui Current gui instance. Make sure to check if it is an instance of your GuiContainer. + * @param mousex X position of the mouse + * @param mousey Y position of the mouse + * @param draggedStack ItemStack user is holding on cursor + * @param button 0 = left click, 1 = right click + * @param isGhost Whether {@code draggedStack} is dragged from ItemPanel/BookmarkPanel, or actual item player + * holds + * @return True if success + */ + boolean handleDragAndDropGT(GuiContainer gui, int mousex, int mousey, ItemStack draggedStack, int button, + boolean isGhost); + + default boolean handleDragNDrop(GuiContainer gui, int mousex, int mousey, ItemStack draggedStack, int button) { + return handleDragAndDropGT(gui, mousex, mousey, draggedStack, button, NEIClientUtils.getHeldItem() == null); + } + + default VisiblityData modifyVisiblity(GuiContainer gui, VisiblityData currentVisibility) { + return currentVisibility; + } + + default Iterable<Integer> getItemSpawnSlots(GuiContainer gui, ItemStack item) { + return Collections.emptyList(); + } + + default List<TaggedInventoryArea> getInventoryAreas(GuiContainer gui) { + return null; + } + + default boolean hideItemPanelSlot(GuiContainer gui, int x, int y, int w, int h) { + return false; + } +} diff --git a/src/main/java/gregtech/api/interfaces/IFluidAccess.java b/src/main/java/gregtech/api/interfaces/IFluidAccess.java new file mode 100644 index 0000000000..8fa9b3a3fa --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/IFluidAccess.java @@ -0,0 +1,26 @@ +package gregtech.api.interfaces; + +import net.minecraftforge.fluids.FluidStack; + +public interface IFluidAccess { + + void set(FluidStack stack); + + FluidStack get(); + + int getCapacity(); + + default int getRealCapacity() { + return getCapacity(); + } + + default void addAmount(int amount) { + if (get() != null) { + get().amount = Math.min(get().amount + amount, getRealCapacity()); + } + } + + default void verifyFluidStack() { + if (get() != null && get().amount <= 0) set(null); + } +} diff --git a/src/main/java/gregtech/api/interfaces/IFoodStat.java b/src/main/java/gregtech/api/interfaces/IFoodStat.java new file mode 100644 index 0000000000..c768c5ca1c --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/IFoodStat.java @@ -0,0 +1,37 @@ +package gregtech.api.interfaces; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumAction; +import net.minecraft.item.ItemStack; + +import gregtech.api.items.GT_MetaBase_Item; + +public interface IFoodStat { + + /** + * Warning the "aPlayer" Parameter may be null! + */ + int getFoodLevel(GT_MetaBase_Item aItem, ItemStack aStack, EntityPlayer aPlayer); + + /** + * Warning the "aPlayer" Parameter may be null! + */ + float getSaturation(GT_MetaBase_Item aItem, ItemStack aStack, EntityPlayer aPlayer); + + /** + * Warning the "aPlayer" Parameter may be null! + */ + boolean alwaysEdible(GT_MetaBase_Item aItem, ItemStack aStack, EntityPlayer aPlayer); + + /** + * Warning the "aPlayer" Parameter may be null! + */ + boolean isRotten(GT_MetaBase_Item aItem, ItemStack aStack, EntityPlayer aPlayer); + + /** + * Warning the "aPlayer" Parameter may be null! + */ + EnumAction getFoodAction(GT_MetaBase_Item aItem, ItemStack aStack); + + void onEaten(GT_MetaBase_Item aItem, ItemStack aStack, EntityPlayer aPlayer); +} diff --git a/src/main/java/gregtech/api/interfaces/IGT_ItemWithMaterialRenderer.java b/src/main/java/gregtech/api/interfaces/IGT_ItemWithMaterialRenderer.java new file mode 100644 index 0000000000..4bf0311544 --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/IGT_ItemWithMaterialRenderer.java @@ -0,0 +1,68 @@ +package gregtech.api.interfaces; + +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.util.IIcon; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import gregtech.common.render.items.GT_GeneratedMaterial_Renderer; + +public interface IGT_ItemWithMaterialRenderer { + + /** + * @return If allow using {@link gregtech.common.render.items.GT_MetaGenerated_Item_Renderer} to render item + */ + boolean shouldUseCustomRenderer(int aMetaData); + + /** + * @return Custom renderer of the Material with offset < 32000 + */ + GT_GeneratedMaterial_Renderer getMaterialRenderer(int aMetaData); + + /** + * If this returns false, renderer falls back to {@link gregtech.common.render.items.GT_GeneratedItem_Renderer} + */ + boolean allowMaterialRenderer(int aMetaData); + + /** + * @return Icon the Material is going to be rendered with + */ + IIcon getIcon(int aMetaData, int pass); + + /** + * @return Icon of the Overlay (or null if there is no Icon) + */ + IIcon getOverlayIcon(int aMetaData, int pass); + + /** + * @return Color Modulation the Material is going to be rendered with. + */ + short[] getRGBa(ItemStack aStack); + + @SideOnly(Side.CLIENT) + default int getSpriteNumber() { + if (this instanceof Item) { + return ((Item) this).getSpriteNumber(); + } else { + throw new RuntimeException(String.format("Class %s does not extend Item!", getClass())); + } + } + + @SideOnly(Side.CLIENT) + default boolean requiresMultipleRenderPasses() { + if (this instanceof Item) { + return ((Item) this).requiresMultipleRenderPasses(); + } else { + throw new RuntimeException(String.format("Class %s does not extend Item!", getClass())); + } + } + + default int getRenderPasses(int metadata) { + if (this instanceof Item) { + return ((Item) this).getRenderPasses(metadata); + } else { + throw new RuntimeException(String.format("Class %s does not extend Item!", getClass())); + } + } +} diff --git a/src/main/java/gregtech/api/interfaces/IGlobalWirelessEnergy.java b/src/main/java/gregtech/api/interfaces/IGlobalWirelessEnergy.java new file mode 100644 index 0000000000..b931549a07 --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/IGlobalWirelessEnergy.java @@ -0,0 +1,98 @@ +package gregtech.api.interfaces; + +import java.math.BigInteger; +import java.util.UUID; + +import net.minecraft.entity.player.EntityPlayer; + +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.common.misc.WirelessNetworkManager; +import gregtech.common.misc.spaceprojects.SpaceProjectManager; + +// If you are adding very late-game content feel free to tap into this interface. +// The eventual goal is to bypass laser/dynamo stuff and have energy deposited directly from ultra-endgame +// multi-blocks directly into the users network. +/** + * Use WirelessNetworkManager instead + */ +@Deprecated +public interface IGlobalWirelessEnergy { + + // Adds a user to the energy map if they do not already exist. Otherwise, do + // nothing. Will also check if the user + // has changed their username and adjust the maps accordingly. This should be + // called infrequently. Ideally on first + // tick of a machine being placed only. + + default void strongCheckOrAddUser(EntityPlayer user) { + WirelessNetworkManager.strongCheckOrAddUser(user.getUniqueID()); + } + + default void strongCheckOrAddUser(UUID user_uuid, String user_name) { + WirelessNetworkManager.strongCheckOrAddUser(user_uuid); + } + + default void strongCheckOrAddUser(String user_uuid, String user_name) { + WirelessNetworkManager.strongCheckOrAddUser(UUID.fromString(user_uuid)); + } + + // ------------------------------------------------------------------------------------ + // Add EU to the users global energy. You can enter a negative number to + // subtract it. + // If the value goes below 0 it will return false and not perform the operation. + // BigIntegers have much slower operations than longs/ints. You should call + // these methods + // as infrequently as possible and bulk store values to add to the global map. + default boolean addEUToGlobalEnergyMap(String userUUID, BigInteger EU) { + return WirelessNetworkManager.addEUToGlobalEnergyMap(UUID.fromString(userUUID), EU); + } + + default boolean addEUToGlobalEnergyMap(UUID user_uuid, BigInteger EU) { + return addEUToGlobalEnergyMap(user_uuid.toString(), EU); + } + + default boolean addEUToGlobalEnergyMap(UUID user_uuid, long EU) { + return addEUToGlobalEnergyMap(user_uuid.toString(), BigInteger.valueOf(EU)); + } + + default boolean addEUToGlobalEnergyMap(UUID user_uuid, int EU) { + return addEUToGlobalEnergyMap(user_uuid.toString(), BigInteger.valueOf(EU)); + } + + default boolean addEUToGlobalEnergyMap(String user_uuid, long EU) { + return addEUToGlobalEnergyMap(user_uuid, BigInteger.valueOf(EU)); + } + + default boolean addEUToGlobalEnergyMap(String user_uuid, int EU) { + return addEUToGlobalEnergyMap(user_uuid, BigInteger.valueOf(EU)); + } + + // ------------------------------------------------------------------------------------ + + default BigInteger getUserEU(String user_uuid) { + return WirelessNetworkManager.getUserEU(UUID.fromString(user_uuid)); + } + + // This overwrites the EU in the network. Only use this if you are absolutely + // sure you know what you are doing. + default void setUserEU(String user_uuid, BigInteger EU) { + WirelessNetworkManager.setUserEU(UUID.fromString(user_uuid), EU); + } + + default String GetUsernameFromUUID(String uuid) { + return SpaceProjectManager.getPlayerNameFromUUID(UUID.fromString(uuid)); + } + + default String getUUIDFromUsername(String username) { + return SpaceProjectManager.getPlayerUUIDFromName(username) + .toString(); + } + + static void clearGlobalEnergyInformationMaps() { + WirelessNetworkManager.clearGlobalEnergyInformationMaps(); + } + + default UUID processInitialSettings(final IGregTechTileEntity machine) { + return WirelessNetworkManager.processInitialSettings(machine); + } +} diff --git a/src/main/java/gregtech/api/interfaces/IGuiIcon.java b/src/main/java/gregtech/api/interfaces/IGuiIcon.java new file mode 100644 index 0000000000..0bc7408250 --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/IGuiIcon.java @@ -0,0 +1,19 @@ +package gregtech.api.interfaces; + +/** + * To allow addons to make use of GT_GuiIcon + */ +public interface IGuiIcon { + + int getX(); + + int getY(); + + int getWidth(); + + int getHeight(); + + int getTexId(); + + IGuiIcon getOverlay(); +} diff --git a/src/main/java/gregtech/api/interfaces/IGuiScreen.java b/src/main/java/gregtech/api/interfaces/IGuiScreen.java new file mode 100644 index 0000000000..c33838fc7f --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/IGuiScreen.java @@ -0,0 +1,45 @@ +package gregtech.api.interfaces; + +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.renderer.entity.RenderItem; + +import gregtech.api.gui.widgets.GT_GuiTooltip; + +public interface IGuiScreen { + + interface IGuiElement { + + void onInit(); + + default void onRemoved() {} + + void draw(int mouseX, int mouseY, float parTicks); + } + + void addToolTip(GT_GuiTooltip toolTip); + + boolean removeToolTip(GT_GuiTooltip toolTip); + + GuiButton getSelectedButton(); + + void clearSelectedButton(); + + void buttonClicked(GuiButton button); + + int getGuiLeft(); + + int getGuiTop(); + + int getXSize(); + + int getYSize(); + + void addElement(IGuiElement element); + + boolean removeElement(IGuiElement element); + + RenderItem getItemRenderer(); + + FontRenderer getFontRenderer(); +} diff --git a/src/main/java/gregtech/api/interfaces/IHasIndexedTexture.java b/src/main/java/gregtech/api/interfaces/IHasIndexedTexture.java new file mode 100644 index 0000000000..ed31984b6e --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/IHasIndexedTexture.java @@ -0,0 +1,17 @@ +package gregtech.api.interfaces; + +/** + * To be implemented on blocks. Usually machine casing blocks. + */ +public interface IHasIndexedTexture { + + /** + * Returns the statically mapped texture for this casing. Return + * {@link gregtech.api.enums.Textures.BlockIcons#ERROR_TEXTURE_INDEX} if meta maps to a nonexistent block, or the + * block does not have a statically mapped texture. + * + * @param aMeta block meta + * @return texture index into {@link gregtech.api.enums.Textures.BlockIcons#casingTexturePages} + */ + int getTextureIndex(int aMeta); +} diff --git a/src/main/java/gregtech/api/interfaces/IHatchElement.java b/src/main/java/gregtech/api/interfaces/IHatchElement.java new file mode 100644 index 0000000000..09f3385729 --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/IHatchElement.java @@ -0,0 +1,198 @@ +package gregtech.api.interfaces; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.function.BiPredicate; +import java.util.function.ToLongFunction; + +import net.minecraftforge.common.util.ForgeDirection; + +import com.google.common.collect.ImmutableList; +import com.gtnewhorizon.structurelib.structure.IStructureElement; + +import gregtech.api.interfaces.metatileentity.IMetaTileEntity; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.util.GT_StructureUtility; +import gregtech.api.util.IGT_HatchAdder; + +public interface IHatchElement<T> { + + List<? extends Class<? extends IMetaTileEntity>> mteClasses(); + + IGT_HatchAdder<? super T> adder(); + + String name(); + + long count(T t); + + default <T2 extends T> IHatchElement<T2> withMteClass(Class<? extends IMetaTileEntity> aClass) { + if (aClass == null) throw new IllegalArgumentException(); + return withMteClasses(Collections.singletonList(aClass)); + } + + @SuppressWarnings("unchecked") // can't set SafeVarargs :( + default <T2 extends T> IHatchElement<T2> withMteClasses(Class<? extends IMetaTileEntity>... aClasses) { + if (aClasses == null) throw new IllegalArgumentException(); + return withMteClasses(Arrays.asList(aClasses)); + } + + default <T2 extends T> IHatchElement<T2> withMteClasses(List<Class<? extends IMetaTileEntity>> aClasses) { + if (aClasses == null) throw new IllegalArgumentException(); + return new HatchElement<>(aClasses, null, null, null, this); + } + + default <T2 extends T> IHatchElement<T2> withAdder(IGT_HatchAdder<T2> aAdder) { + if (aAdder == null) throw new IllegalArgumentException(); + return new HatchElement<>(null, aAdder, null, null, this); + } + + default IHatchElement<T> withName(String aName) { + if (aName == null) throw new IllegalArgumentException(); + return new HatchElement<>(null, null, aName, null, this); + } + + default <T2 extends T> IHatchElement<T2> withCount(ToLongFunction<T2> aCount) { + if (aCount == null) throw new IllegalArgumentException(); + return new HatchElement<>(null, null, null, aCount, this); + } + + default <T2 extends T> IStructureElement<T2> newAny(int aCasingIndex, int aDot) { + if (aCasingIndex < 0 || aDot < 0) throw new IllegalArgumentException(); + return GT_StructureUtility.<T2>buildHatchAdder() + .anyOf(this) + .casingIndex(aCasingIndex) + .dot(aDot) + .continueIfSuccess() + .build(); + } + + default <T2 extends T> IStructureElement<T2> newAny(int aCasingIndex, int aDot, ForgeDirection... allowedFacings) { + if (aCasingIndex < 0 || aDot < 0) throw new IllegalArgumentException(); + return GT_StructureUtility.<T2>buildHatchAdder() + .anyOf(this) + .casingIndex(aCasingIndex) + .dot(aDot) + .continueIfSuccess() + .allowOnly(allowedFacings) + .build(); + } + + default <T2 extends T> IStructureElement<T2> newAny(int aCasingIndex, int aDot, + BiPredicate<? super T2, ? super IGregTechTileEntity> aShouldSkip) { + if (aCasingIndex < 0 || aDot < 0 || aShouldSkip == null) throw new IllegalArgumentException(); + return GT_StructureUtility.<T2>buildHatchAdder() + .anyOf(this) + .casingIndex(aCasingIndex) + .dot(aDot) + .shouldSkip(aShouldSkip) + .continueIfSuccess() + .build(); + } + + default <T2 extends T> IHatchElement<T2> or(IHatchElement<? super T2> fallback) { + return new HatchElementEither<>(this, fallback); + } +} + +class HatchElementEither<T> implements IHatchElement<T> { + + private final IHatchElement<? super T> first, second; + private ImmutableList<? extends Class<? extends IMetaTileEntity>> mMteClasses; + private String name; + + HatchElementEither(IHatchElement<? super T> first, IHatchElement<? super T> second) { + this.first = first; + this.second = second; + } + + @Override + public List<? extends Class<? extends IMetaTileEntity>> mteClasses() { + if (mMteClasses == null) mMteClasses = ImmutableList.<Class<? extends IMetaTileEntity>>builder() + .addAll(first.mteClasses()) + .addAll(second.mteClasses()) + .build(); + return mMteClasses; + } + + @Override + public IGT_HatchAdder<? super T> adder() { + return ((t, te, i) -> first.adder() + .apply(t, te, i) + || second.adder() + .apply(t, te, i)); + } + + @Override + public String name() { + if (name == null) name = first.name() + " or " + second.name(); + return name; + } + + @Override + public long count(T t) { + return first.count(t) + second.count(t); + } +} + +class HatchElement<T> implements IHatchElement<T> { + + private final List<Class<? extends IMetaTileEntity>> mClasses; + private final IGT_HatchAdder<? super T> mAdder; + private final String mName; + private final IHatchElement<? super T> mBacking; + private final ToLongFunction<? super T> mCount; + + public HatchElement(List<Class<? extends IMetaTileEntity>> aMteClasses, IGT_HatchAdder<? super T> aAdder, + String aName, ToLongFunction<? super T> aCount, IHatchElement<? super T> aBacking) { + this.mClasses = aMteClasses; + this.mAdder = aAdder; + this.mName = aName; + this.mCount = aCount; + this.mBacking = aBacking; + } + + @Override + public List<? extends Class<? extends IMetaTileEntity>> mteClasses() { + return mClasses == null ? mBacking.mteClasses() : mClasses; + } + + @Override + public IGT_HatchAdder<? super T> adder() { + return mAdder == null ? mBacking.adder() : mAdder; + } + + @Override + public String name() { + return mName == null ? mBacking.name() : mName; + } + + @Override + public long count(T t) { + return mCount == null ? mBacking.count(t) : mCount.applyAsLong(t); + } + + @Override + public <T2 extends T> IHatchElement<T2> withMteClasses(List<Class<? extends IMetaTileEntity>> aClasses) { + if (aClasses == null) throw new IllegalArgumentException(); + return new HatchElement<>(aClasses, mAdder, mName, mCount, mBacking); + } + + @Override + public <T2 extends T> IHatchElement<T2> withAdder(IGT_HatchAdder<T2> aAdder) { + if (aAdder == null) throw new IllegalArgumentException(); + return new HatchElement<>(mClasses, aAdder, mName, mCount, mBacking); + } + + @Override + public IHatchElement<T> withName(String aName) { + if (aName == null) throw new IllegalArgumentException(); + return new HatchElement<>(mClasses, mAdder, aName, mCount, mBacking); + } + + @Override + public <T2 extends T> IHatchElement<T2> withCount(ToLongFunction<T2> aCount) { + if (aCount == null) throw new IllegalArgumentException(); + return new HatchElement<>(mClasses, mAdder, mName, aCount, mBacking); + } +} diff --git a/src/main/java/gregtech/api/interfaces/IHeatingCoil.java b/src/main/java/gregtech/api/interfaces/IHeatingCoil.java new file mode 100644 index 0000000000..37f7969d56 --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/IHeatingCoil.java @@ -0,0 +1,20 @@ +package gregtech.api.interfaces; + +import java.util.function.Consumer; + +import net.minecraft.item.ItemStack; + +import gregtech.api.enums.HeatingCoilLevel; + +public interface IHeatingCoil { + + HeatingCoilLevel getCoilHeat(int meta); + + default HeatingCoilLevel getCoilHeat(ItemStack stack) { + return getCoilHeat(stack.getItemDamage()); + } + + void setOnCoilCheck(Consumer<IHeatingCoil> callback); + + Consumer<IHeatingCoil> getOnCoilCheck(); +} diff --git a/src/main/java/gregtech/api/interfaces/IIconContainer.java b/src/main/java/gregtech/api/interfaces/IIconContainer.java new file mode 100644 index 0000000000..525721bb5c --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/IIconContainer.java @@ -0,0 +1,48 @@ +package gregtech.api.interfaces; + +import static gregtech.api.enums.GT_Values.UNCOLORED_RGBA; + +import net.minecraft.util.IIcon; +import net.minecraft.util.ResourceLocation; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; + +public interface IIconContainer { + + /** + * @return A regular Icon. + */ + @SideOnly(Side.CLIENT) + IIcon getIcon(); + + /** + * @return Icon of the Overlay (or null if there is no Icon) + */ + @SideOnly(Side.CLIENT) + IIcon getOverlayIcon(); + + /** + * @return the Amount of Render Passes for this Icon. + */ + @SideOnly(Side.CLIENT) + default int getIconPasses() { + return 1; + } + + /** + * @return the Default Texture File for this Icon. + */ + @SideOnly(Side.CLIENT) + ResourceLocation getTextureFile(); + + @SideOnly(Side.CLIENT) + default short[] getIconColor(int aRenderPass) { + return UNCOLORED_RGBA; + } + + @SideOnly(Side.CLIENT) + default boolean isUsingColorModulation(int aRenderPass) { + return aRenderPass == 0; + } +} diff --git a/src/main/java/gregtech/api/interfaces/IItemBehaviour.java b/src/main/java/gregtech/api/interfaces/IItemBehaviour.java new file mode 100644 index 0000000000..67bb505c6b --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/IItemBehaviour.java @@ -0,0 +1,47 @@ +package gregtech.api.interfaces; + +import java.util.List; + +import net.minecraft.dispenser.IBlockSource; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.projectile.EntityArrow; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.world.World; +import net.minecraftforge.common.util.ForgeDirection; + +import gregtech.api.enums.SubTag; +import gregtech.api.items.GT_MetaBase_Item; + +public interface IItemBehaviour<E extends Item> { + + boolean onLeftClickEntity(E aItem, ItemStack aStack, EntityPlayer aPlayer, Entity aEntity); + + boolean onItemUse(E aItem, ItemStack aStack, EntityPlayer aPlayer, World aWorld, int aX, int aY, int aZ, + int ordinalSide, float hitX, float hitY, float hitZ); + + boolean onItemUseFirst(E aItem, ItemStack aStack, EntityPlayer aPlayer, World aWorld, int aX, int aY, int aZ, + ForgeDirection side, float hitX, float hitY, float hitZ); + + ItemStack onItemRightClick(E aItem, ItemStack aStack, World aWorld, EntityPlayer aPlayer); + + List<String> getAdditionalToolTips(E aItem, List<String> aList, ItemStack aStack); + + void onUpdate(E aItem, ItemStack aStack, World aWorld, Entity aPlayer, int aTimer, boolean aIsInHand); + + boolean isItemStackUsable(E aItem, ItemStack aStack); + + boolean canDispense(E aItem, IBlockSource aSource, ItemStack aStack); + + ItemStack onDispense(E aItem, IBlockSource aSource, ItemStack aStack); + + boolean hasProjectile(GT_MetaBase_Item aItem, SubTag aProjectileType, ItemStack aStack); + + EntityArrow getProjectile(E aItem, SubTag aProjectileType, ItemStack aStack, World aWorld, double aX, double aY, + double aZ); + + EntityArrow getProjectile(E aItem, SubTag aProjectileType, ItemStack aStack, World aWorld, EntityLivingBase aEntity, + float aSpeed); +} diff --git a/src/main/java/gregtech/api/interfaces/IItemContainer.java b/src/main/java/gregtech/api/interfaces/IItemContainer.java new file mode 100644 index 0000000000..de94606e95 --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/IItemContainer.java @@ -0,0 +1,40 @@ +package gregtech.api.interfaces; + +import net.minecraft.block.Block; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; + +public interface IItemContainer { + + Item getItem(); + + Block getBlock(); + + boolean isStackEqual(Object aStack); + + boolean isStackEqual(Object aStack, boolean aWildcard, boolean aIgnoreNBT); + + ItemStack get(long aAmount, Object... aReplacements); + + ItemStack getWildcard(long aAmount, Object... aReplacements); + + ItemStack getUndamaged(long aAmount, Object... aReplacements); + + ItemStack getAlmostBroken(long aAmount, Object... aReplacements); + + ItemStack getWithDamage(long aAmount, long aMetaValue, Object... aReplacements); + + IItemContainer set(Item aItem); + + IItemContainer set(ItemStack aStack); + + IItemContainer registerOre(Object... aOreNames); + + IItemContainer registerWildcardAsOre(Object... aOreNames); + + ItemStack getWithCharge(long aAmount, int aEnergy, Object... aReplacements); + + ItemStack getWithName(long aAmount, String aDisplayName, Object... aReplacements); + + boolean hasBeenSet(); +} diff --git a/src/main/java/gregtech/api/interfaces/IMaterialHandler.java b/src/main/java/gregtech/api/interfaces/IMaterialHandler.java new file mode 100644 index 0000000000..ddd7e832dd --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/IMaterialHandler.java @@ -0,0 +1,6 @@ +package gregtech.api.interfaces; + +public interface IMaterialHandler { + + void onMaterialsInit(); +} diff --git a/src/main/java/gregtech/api/interfaces/INetworkUpdatableItem.java b/src/main/java/gregtech/api/interfaces/INetworkUpdatableItem.java new file mode 100644 index 0000000000..1dd36d9998 --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/INetworkUpdatableItem.java @@ -0,0 +1,25 @@ +package gregtech.api.interfaces; + +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; + +/** + * Together with {@link gregtech.api.net.GT_Packet_UpdateItem} you can request server side to update item in hand with a + * NBT tag. + * <p> + * Usual NBT tag size limit applies. + */ +public interface INetworkUpdatableItem { + + /** + * Receive update from client. Runs on server thread. + * + * @param stack Stack being updated + * @param player player holding the stack + * @param tag received data + * @return true if this stack should be kept inside the player inventory. false if this stack should vanish (i.e. + * slot content set to null) + */ + boolean receive(ItemStack stack, EntityPlayerMP player, NBTTagCompound tag); +} diff --git a/src/main/java/gregtech/api/interfaces/IOreRecipeRegistrator.java b/src/main/java/gregtech/api/interfaces/IOreRecipeRegistrator.java new file mode 100644 index 0000000000..714342ae7e --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/IOreRecipeRegistrator.java @@ -0,0 +1,19 @@ +package gregtech.api.interfaces; + +import net.minecraft.item.ItemStack; + +import gregtech.api.enums.Materials; +import gregtech.api.enums.OrePrefixes; + +public interface IOreRecipeRegistrator { + + /** + * Contains a Code Fragment, used in the OrePrefix to register Recipes. Better than using a switch/case, like I did + * before. + * + * @param aPrefix always != null + * @param aMaterial always != null, and can be == _NULL if the Prefix is Self Referencing or not Material based! + * @param aStack always != null + */ + void registerOre(OrePrefixes aPrefix, Materials aMaterial, String aOreDictName, String aModName, ItemStack aStack); +} diff --git a/src/main/java/gregtech/api/interfaces/IProjectileItem.java b/src/main/java/gregtech/api/interfaces/IProjectileItem.java new file mode 100644 index 0000000000..64782bf04c --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/IProjectileItem.java @@ -0,0 +1,29 @@ +package gregtech.api.interfaces; + +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.projectile.EntityArrow; +import net.minecraft.item.ItemStack; +import net.minecraft.world.World; + +import gregtech.api.enums.SubTag; + +public interface IProjectileItem { + + /** + * @return if this Item has an Arrow Entity + */ + boolean hasProjectile(SubTag aProjectileType, ItemStack aStack); + + /** + * @return an Arrow Entity to be spawned. If null then this is not an Arrow. Note: Other Projectiles still extend + * EntityArrow + */ + EntityArrow getProjectile(SubTag aProjectileType, ItemStack aStack, World aWorld, double aX, double aY, double aZ); + + /** + * @return an Arrow Entity to be spawned. If null then this is not an Arrow. Note: Other Projectiles still extend + * EntityArrow + */ + EntityArrow getProjectile(SubTag aProjectileType, ItemStack aStack, World aWorld, EntityLivingBase aEntity, + float aSpeed); +} diff --git a/src/main/java/gregtech/api/interfaces/IRecipeMap.java b/src/main/java/gregtech/api/interfaces/IRecipeMap.java new file mode 100644 index 0000000000..ce48b29927 --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/IRecipeMap.java @@ -0,0 +1,74 @@ +package gregtech.api.interfaces; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.function.Function; + +import javax.annotation.Nonnull; + +import gregtech.api.util.GT_Recipe; +import gregtech.api.util.GT_RecipeBuilder; +import gregtech.api.util.GT_Utility; + +/** + * Represents the target of a recipe adding action, usually, but not necessarily, is a recipe map itself. + */ +public interface IRecipeMap { + + /** + * Add a downstream recipe map that will get to handle the original builder. + * <p> + * Downstream recipe maps got passed the recipe builder after parent recipe map is done with its business. Notice + * at this time the original recipe builder might be modified by the parent recipe map in some form, but it will + * remain as valid. + * <p> + * A downstream will only be invoked if parent recipe map added something. + * + * @param downstream the downstream recipe map to add + */ + void addDownstream(IRecipeMap downstream); + + /** + * Actually add the recipe represented by the builder. CAN modify the builder's internal states!!! + */ + @Nonnull + Collection<GT_Recipe> doAdd(GT_RecipeBuilder builder); + + /** + * Return a variant of this recipe map that will perform a deep copy on input recipe builder before doing anything + * to it. + * <p> + * The returned recipe map will not have any downstreams, but can accept new downstreams. + */ + default IRecipeMap deepCopyInput() { + return newRecipeMap(b -> doAdd(b.copy())); + } + + static IRecipeMap newRecipeMap(Function<? super GT_RecipeBuilder, Collection<GT_Recipe>> func) { + return new IRecipeMap() { + + private final Collection<IRecipeMap> downstreams = new ArrayList<>(); + + @Override + public void addDownstream(IRecipeMap downstream) { + downstreams.add(downstream); + } + + @Nonnull + @Override + public Collection<GT_Recipe> doAdd(GT_RecipeBuilder builder) { + List<Collection<GT_Recipe>> ret = new ArrayList<>(); + Collection<GT_Recipe> out = func.apply(builder); + ret.add(out); + builder.clearInvalid(); + if (!out.isEmpty()) { + for (IRecipeMap downstream : downstreams) { + ret.add(downstream.doAdd(builder)); + } + } + return GT_Utility.concat(ret); + } + }; + } +} diff --git a/src/main/java/gregtech/api/interfaces/IRedstoneCircuitBlock.java b/src/main/java/gregtech/api/interfaces/IRedstoneCircuitBlock.java new file mode 100644 index 0000000000..0eea6ca3a4 --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/IRedstoneCircuitBlock.java @@ -0,0 +1,69 @@ +package gregtech.api.interfaces; + +import net.minecraft.block.Block; +import net.minecraft.tileentity.TileEntity; +import net.minecraftforge.common.util.ForgeDirection; + +import gregtech.api.interfaces.tileentity.ICoverable; +import gregtech.api.util.GT_CoverBehavior; + +/** + * Implemented by the MetaTileEntity of the Redstone Circuit Block + */ +public interface IRedstoneCircuitBlock { + + /** + * The Output Direction the Circuit Block is Facing + */ + ForgeDirection getOutputFacing(); + + /** + * sets Output Redstone State at Side + */ + boolean setRedstone(byte aStrength, ForgeDirection side); + + /** + * returns Output Redstone State at Side Note that setRedstone checks if there is a Difference between the old and + * the new Setting before consuming any Energy + */ + byte getOutputRedstone(ForgeDirection side); + + /** + * returns Input Redstone Signal at Side + */ + byte getInputRedstone(ForgeDirection side); + + /** + * If this Side is Covered up and therefor not doing any Redstone + */ + GT_CoverBehavior getCover(ForgeDirection side); + + int getCoverID(ForgeDirection side); + + int getCoverVariable(ForgeDirection side); + + /** + * returns whatever Block-ID is adjacent to the Redstone Circuit Block + */ + Block getBlockAtSide(ForgeDirection side); + + /** + * returns whatever Meta-Value is adjacent to the Redstone Circuit Block + */ + byte getMetaIDAtSide(ForgeDirection side); + + /** + * returns whatever TileEntity is adjacent to the Redstone Circuit Block + */ + TileEntity getTileEntityAtSide(ForgeDirection side); + + /** + * returns whatever TileEntity is used by the Redstone Circuit Block + */ + ICoverable getOwnTileEntity(); + + /** + * returns worldObj.rand.nextInt(aRange) + */ + int getRandom(int aRange); +} diff --git a/src/main/java/gregtech/api/interfaces/ISecondaryDescribable.java b/src/main/java/gregtech/api/interfaces/ISecondaryDescribable.java new file mode 100644 index 0000000000..1f480091fc --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/ISecondaryDescribable.java @@ -0,0 +1,30 @@ +package gregtech.api.interfaces; + +/** + * To get a tooltip with a secondary description + */ +public interface ISecondaryDescribable extends IDescribable { + + /** + * Convenient to call when overriding the `String[] getDescription()` method. + */ + default String[] getCurrentDescription() { + if (isDisplaySecondaryDescription() && getSecondaryDescription() != null) { + return getSecondaryDescription(); + } + return getPrimaryDescription(); + } + + String[] getPrimaryDescription(); + + String[] getSecondaryDescription(); + + /** + * This method will only be called on client side + * + * @return whether the secondary description should be display. default is false + */ + default boolean isDisplaySecondaryDescription() { + return false; + } +} diff --git a/src/main/java/gregtech/api/interfaces/ISubTagContainer.java b/src/main/java/gregtech/api/interfaces/ISubTagContainer.java new file mode 100644 index 0000000000..3e3690c67b --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/ISubTagContainer.java @@ -0,0 +1,21 @@ +package gregtech.api.interfaces; + +import gregtech.api.enums.SubTag; + +public interface ISubTagContainer { + + /** + * @return if the Tag is inside the List. + */ + boolean contains(SubTag aTag); + + /** + * @return The ISubTagContainer you called this Function on, for convenience. + */ + ISubTagContainer add(SubTag... aTags); + + /** + * @return if the Tag was there before it has been removed. + */ + boolean remove(SubTag aTag); +} diff --git a/src/main/java/gregtech/api/interfaces/ITexture.java b/src/main/java/gregtech/api/interfaces/ITexture.java new file mode 100644 index 0000000000..e97fa4539a --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/ITexture.java @@ -0,0 +1,55 @@ +package gregtech.api.interfaces; + +import net.minecraft.block.Block; +import net.minecraft.client.renderer.RenderBlocks; +import net.minecraft.client.renderer.Tessellator; + +public interface ITexture { + + void renderXPos(RenderBlocks aRenderer, Block aBlock, int aX, int aY, int aZ); + + void renderXNeg(RenderBlocks aRenderer, Block aBlock, int aX, int aY, int aZ); + + void renderYPos(RenderBlocks aRenderer, Block aBlock, int aX, int aY, int aZ); + + void renderYNeg(RenderBlocks aRenderer, Block aBlock, int aX, int aY, int aZ); + + void renderZPos(RenderBlocks aRenderer, Block aBlock, int aX, int aY, int aZ); + + void renderZNeg(RenderBlocks aRenderer, Block aBlock, int aX, int aY, int aZ); + + boolean isValidTexture(); + + /** + * @return {@code true} if this texture is from the old package + */ + default boolean isOldTexture() { + return true; + } + + /** + * Will initialize the {@link Tessellator} if rendering off-world (Inventory) + * + * @param aRenderer The {@link RenderBlocks} Renderer + * @param aNormalX The X Normal for current Quad Face + * @param aNormalY The Y Normal for current Quad Face + * @param aNormalZ The Z Normal for current Quad Face + */ + default void startDrawingQuads(RenderBlocks aRenderer, float aNormalX, float aNormalY, float aNormalZ) { + if (aRenderer.useInventoryTint && !isOldTexture()) { + Tessellator.instance.startDrawingQuads(); + Tessellator.instance.setNormal(aNormalX, aNormalY, aNormalZ); + } + } + + /** + * Will run the {@link Tessellator} to draw Quads if rendering off-world (Inventory) + * + * @param aRenderer The {@link RenderBlocks} Renderer + */ + default void draw(RenderBlocks aRenderer) { + if (aRenderer.useInventoryTint && !isOldTexture()) { + Tessellator.instance.draw(); + } + } +} diff --git a/src/main/java/gregtech/api/interfaces/ITextureBuilder.java b/src/main/java/gregtech/api/interfaces/ITextureBuilder.java new file mode 100644 index 0000000000..7c9672d521 --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/ITextureBuilder.java @@ -0,0 +1,109 @@ +package gregtech.api.interfaces; + +import net.minecraft.block.Block; +import net.minecraftforge.common.util.ForgeDirection; + +import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing; + +import gregtech.api.render.TextureFactory; + +/** + * <p> + * This Interface defines operations to configure and build instances of the {@link ITexture} implementations + * </p> + * <p> + * Use the {@link TextureFactory#builder()} method to get an instance of the {@link ITextureBuilder} implementation. + * </p> + */ +public interface ITextureBuilder { + + /** + * Build the {@link ITexture} + * + * @return The built {@link ITexture} + * @throws IllegalStateException if setFromBlock has never been called. + */ + ITexture build(); + + /** + * @param block The {@link Block} + * @param meta The meta value for the Block + * @return {@link ITextureBuilder} for chaining + */ + ITextureBuilder setFromBlock(final Block block, final int meta); + + /** + * @param side + * <p> + * The {@link ForgeDirection} side providing the texture + * </p> + * <p> + * Default is {@link ForgeDirection#UNKNOWN} to use same side as rendered + * </p> + * @return {@link ITextureBuilder} for chaining + */ + ITextureBuilder setFromSide(final ForgeDirection side); + + /** + * @param iconContainers The {@link IIconContainer}s to add + * @return {@link ITextureBuilder} for chaining + */ + ITextureBuilder addIcon(final IIconContainer... iconContainers); + + /** + * @param rgba The RGBA tint for this {@link ITexture} + * @return {@link ITextureBuilder} for chaining + */ + ITextureBuilder setRGBA(final short[] rgba); + + /** + * @param iTextures The {@link ITexture} layers to add + * @return {@link ITextureBuilder} for chaining + */ + ITextureBuilder addLayer(final ITexture... iTextures); + + /** + * Set alpha blending + * + * @param allowAlpha to set + * + * @return {@link ITextureBuilder} for chaining + */ + ITextureBuilder setAllowAlpha(final boolean allowAlpha); + + /** + * Texture will render with same orientation as with vanilla blocks + * + * @return {@link ITextureBuilder} for chaining + */ + ITextureBuilder stdOrient(); + + /** + * Force using world coord overload of getIcon. + * + * @return {@link ITextureBuilder} for chaining + * @throws IllegalStateException if setFromBlock has never been called. + */ + ITextureBuilder useWorldCoord(); + + /** + * Force using meta overload of getIcon. + * + * @return {@link ITextureBuilder} for chaining + */ + ITextureBuilder noWorldCoord(); + + /** + * Texture will orientate from block's {@link ExtendedFacing} + * + * @return {@link ITextureBuilder} for chaining + */ + ITextureBuilder extFacing(); + + /** + * Texture always render with full brightness to glow in the dark + * + * @return {@link ITextureBuilder} for chaining + */ + ITextureBuilder glow(); +} diff --git a/src/main/java/gregtech/api/interfaces/IToolStats.java b/src/main/java/gregtech/api/interfaces/IToolStats.java new file mode 100644 index 0000000000..9d8da63b6c --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/IToolStats.java @@ -0,0 +1,206 @@ +package gregtech.api.interfaces; + +import java.util.List; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import net.minecraft.block.Block; +import net.minecraft.enchantment.Enchantment; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.DamageSource; +import net.minecraft.world.World; +import net.minecraftforge.event.world.BlockEvent; + +import gregtech.api.items.GT_MetaGenerated_Tool; + +/** + * The Stats for GT Tools. Not including any Material Modifiers. + * <p/> + * And this is supposed to not have any ItemStack Parameters as these are generic Stats. + */ +public interface IToolStats { + + /** + * Called when aPlayer crafts this Tool + */ + void onToolCrafted(ItemStack aStack, EntityPlayer aPlayer); + + /** + * Called when this gets added to a Tool Item + */ + void onStatsAddedToTool(GT_MetaGenerated_Tool aItem, int aID); + + /** + * @implNote if you are only modifying drops, override + * {@link #convertBlockDrops(List, ItemStack, EntityPlayer, Block, int, int, int, byte, int, boolean, BlockEvent.HarvestDropsEvent)} + * @param player The player + * @param x Block pos + * @param y Block pos + * @param z Block pos + * @param block the block + * @param metadata block metadata + * @param tile TileEntity of the block if exist + * @param event the event, cancel it to prevent the block from being broken + */ + default void onBreakBlock(@Nonnull EntityPlayer player, int x, int y, int z, @Nonnull Block block, byte metadata, + @Nullable TileEntity tile, @Nonnull BlockEvent.BreakEvent event) {} + + /** + * @return Damage the Tool receives when breaking a Block. 100 is one Damage Point (or 100 EU). + */ + int getToolDamagePerBlockBreak(); + + /** + * @return Damage the Tool receives when converting the drops of a Block. 100 is one Damage Point (or 100 EU). + */ + int getToolDamagePerDropConversion(); + + /** + * @return Damage the Tool receives when being used as Container Item. 100 is one use, however it is usually 8 times + * more than normal. + */ + int getToolDamagePerContainerCraft(); + + /** + * @return Damage the Tool receives when being used as Weapon, 200 is the normal Value, 100 for actual Weapons. + */ + int getToolDamagePerEntityAttack(); + + /** + * @return Basic Quality of the Tool, 0 is normal. If increased, it will increase the general quality of all Tools + * of this Type. Decreasing is also possible. + */ + int getBaseQuality(); + + /** + * @return The Damage Bonus for this Type of Tool against Mobs. 1.0F is normal punch. + */ + float getBaseDamage(); + + /** + * @return This gets the Hurt Resistance time for Entities getting hit. (always does 1 as minimum) + */ + int getHurtResistanceTime(int aOriginalHurtResistance, Entity aEntity); + + /** + * @return This is a multiplier for the Tool Speed. 1.0F = no special Speed. + */ + float getSpeedMultiplier(); + + /** + * @return This is a multiplier for the Tool Speed. 1.0F = no special Durability. + */ + float getMaxDurabilityMultiplier(); + + DamageSource getDamageSource(EntityLivingBase aPlayer, Entity aEntity); + + String getMiningSound(); + + String getCraftingSound(); + + String getEntityHitSound(); + + String getBreakingSound(); + + Enchantment[] getEnchantments(ItemStack aStack); + + int[] getEnchantmentLevels(ItemStack aStack); + + /** + * @return If this Tool can be used for blocking Damage like a Sword. + */ + boolean canBlock(); + + /** + * @return If this Tool can be used as an RC Crowbar. + */ + boolean isCrowbar(); + + /** + * @return If this Tool can be used as an FR Grafter. + */ + boolean isGrafter(); + + boolean isChainsaw(); + + /** + * @return If this Tool can be used as an BC Wrench. + */ + boolean isWrench(); + + /** + * @return if this Tool can be used as an PR screwdriver + */ + default boolean isScrewdriver() { + return false; + } + + /** + * @return If this Tool can be used as Weapon i.e. if that is the main purpose. + */ + boolean isWeapon(); + + /** + * @return If this Tool is a Ranged Weapon. Return false at isWeapon unless you have a Blade attached to your + * Bow/Gun or something + */ + boolean isRangedWeapon(); + + /** + * @return If this Tool can be used as Weapon i.e. if that is the main purpose. + */ + boolean isMiningTool(); + + /** + * {@link Block#getHarvestTool(int)} can return the following Values for example. "axe", "pickaxe", "sword", + * "shovel", "hoe", "grafter", "saw", "wrench", "crowbar", "file", "hammer", "plow", "plunger", "scoop", + * "screwdriver", "sense", "scythe", "softhammer", "cutter", "plasmatorch" + * + * @return If this is a minable Block. Tool Quality checks (like Diamond Tier or something) are separate from this + * check. + */ + boolean isMinableBlock(Block aBlock, byte aMetaData); + + /** + * This lets you modify the Drop List, when this type of Tool has been used. + * + * @return the Amount of modified Items, used to determine the extra durability cost + */ + int convertBlockDrops(List<ItemStack> aDrops, ItemStack aStack, EntityPlayer aPlayer, Block aBlock, int aX, int aY, + int aZ, byte aMetaData, int aFortune, boolean aSilkTouch, BlockEvent.HarvestDropsEvent aEvent); + + /** + * @return Returns a broken Version of the Item. + */ + ItemStack getBrokenItem(ItemStack aStack); + + /** + * @return the Damage actually done to the Mob. + */ + float getNormalDamageAgainstEntity(float aOriginalDamage, Entity aEntity, ItemStack aStack, EntityPlayer aPlayer); + + /** + * @return the Damage actually done to the Mob. + */ + float getMagicDamageAgainstEntity(float aOriginalDamage, Entity aEntity, ItemStack aStack, EntityPlayer aPlayer); + + IIconContainer getIcon(boolean aIsToolHead, ItemStack aStack); + + short[] getRGBa(boolean aIsToolHead, ItemStack aStack); + + float getMiningSpeed(Block aBlock, byte aMetaData, float aDefault, EntityPlayer aPlayer, World worldObj, int aX, + int aY, int aZ); + + default String getToolTypeName() { + return null; + }; + + default byte getMaxMode() { + return 1; + } +} diff --git a/src/main/java/gregtech/api/interfaces/covers/IControlsWorkCover.java b/src/main/java/gregtech/api/interfaces/covers/IControlsWorkCover.java new file mode 100644 index 0000000000..d4cd1e9ec2 --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/covers/IControlsWorkCover.java @@ -0,0 +1,30 @@ +package gregtech.api.interfaces.covers; + +import net.minecraftforge.common.util.ForgeDirection; + +import gregtech.api.interfaces.tileentity.ICoverable; +import gregtech.api.interfaces.tileentity.IMachineProgress; + +/** + * Marker interface for covers that might control whether the work can start on a {@link IMachineProgress}. + */ +public interface IControlsWorkCover { + + /** + * Make sure there is only one GT_Cover_ControlsWork on the aTileEntity TODO this is a migration thing. Remove this + * after 2.3.0 is released. + * + * @return true if the cover is the first (side) one + **/ + static boolean makeSureOnlyOne(ForgeDirection aMySide, ICoverable aTileEntity) { + for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) { + if (aTileEntity.getCoverBehaviorAtSideNew(side) instanceof IControlsWorkCover + && side.ordinal() < aMySide.ordinal()) { + aTileEntity.dropCover(side, side, true); + aTileEntity.markDirty(); + return false; + } + } + return true; + } +} diff --git a/src/main/java/gregtech/api/interfaces/fluid/IFluidStore.java b/src/main/java/gregtech/api/interfaces/fluid/IFluidStore.java new file mode 100644 index 0000000000..047cf4df5b --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/fluid/IFluidStore.java @@ -0,0 +1,22 @@ +package gregtech.api.interfaces.fluid; + +import javax.annotation.Nonnull; + +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.IFluidTank; + +/** + * Objects implementing this interface can be used for storing certain fluid, especially for recipe output. + */ +public interface IFluidStore extends IFluidTank { + + /** + * @return If this does not have partially filled fluid nor have restriction on what fluid to accept. + */ + boolean isEmptyAndAcceptsAnyFluid(); + + /** + * @return Whether to allow given fluid to be inserted into this. + */ + boolean canStoreFluid(@Nonnull FluidStack fluidStack); +} diff --git a/src/main/java/gregtech/api/interfaces/fluid/IGT_Fluid.java b/src/main/java/gregtech/api/interfaces/fluid/IGT_Fluid.java new file mode 100644 index 0000000000..7c8b2b3f11 --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/fluid/IGT_Fluid.java @@ -0,0 +1,14 @@ +package gregtech.api.interfaces.fluid; + +import net.minecraftforge.fluids.FluidRegistry; + +@SuppressWarnings("unused") // API might legitimately expose unused methods within this local project's scope +public interface IGT_Fluid { + + /** + * Adds this {@link IGT_Fluid} to the {@link FluidRegistry} and internally-implemented registrations + * + * @return {@link IGT_RegisteredFluid} The GregTech registered fluid + */ + IGT_RegisteredFluid addFluid(); +} diff --git a/src/main/java/gregtech/api/interfaces/fluid/IGT_FluidBuilder.java b/src/main/java/gregtech/api/interfaces/fluid/IGT_FluidBuilder.java new file mode 100644 index 0000000000..f15b148fcb --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/fluid/IGT_FluidBuilder.java @@ -0,0 +1,96 @@ +package gregtech.api.interfaces.fluid; + +import javax.annotation.Nonnull; + +import net.minecraft.block.Block; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidRegistry; + +import gregtech.api.enums.FluidState; + +@SuppressWarnings("unused") // API might legitimately expose unused methods within this local project's scope +public interface IGT_FluidBuilder { + + /** + * @param colorRGBA The {@code short[]} RGBA color of the {@link Fluid} or {@code null} for no defined RGBA color + * @return {@link IGT_FluidBuilder} self for call chaining + */ + @SuppressWarnings("UnusedReturnValue") // Last call in chain, may not use this returned value + IGT_FluidBuilder withColorRGBA(final short[] colorRGBA); + + /** + * @param localizedName The localized name of this {@link IGT_FluidBuilder} + * @return {@link IGT_FluidBuilder} self for call chaining + */ + @SuppressWarnings("UnusedReturnValue") // Last call in chain, may not use this returned value + IGT_FluidBuilder withLocalizedName(final String localizedName); + + /** + * @param fluidState The {@link FluidState} of this {@link IGT_FluidBuilder} + * @param temperature The Kelvin temperature of this {@link IGT_FluidBuilder} + * @return {@link IGT_FluidBuilder} self for call chaining + */ + @SuppressWarnings("UnusedReturnValue") // Last call in chain, may not use this returned value + IGT_FluidBuilder withStateAndTemperature(final FluidState fluidState, final int temperature); + + /** + * @param stillIconResourceLocation the {@link ResourceLocation} of the still fluid icon + * @return {@link IGT_FluidBuilder} self for call chaining + */ + @SuppressWarnings("UnusedReturnValue") // Last call in chain, may not use this returned value + IGT_FluidBuilder withStillIconResourceLocation(final ResourceLocation stillIconResourceLocation); + + /** + * @param flowingIconResourceLocation the {@link ResourceLocation} of the flowing fluid icon + * @return {@link IGT_FluidBuilder} self for call chaining + */ + @SuppressWarnings("UnusedReturnValue") // Last call in chain, may not use this returned value + IGT_FluidBuilder withFlowingIconResourceLocation(final ResourceLocation flowingIconResourceLocation); + + /** + * @param textureName The name of the GregTech mod texture of this {@link IGT_FluidBuilder} + * @return {@link IGT_FluidBuilder} self for call chaining + */ + @SuppressWarnings("UnusedReturnValue") // Last call in chain, may not use this returned value + IGT_FluidBuilder withTextureName(final String textureName); + + /** + * @param fluidBlock the {@link Block} implementation of the {@link IGT_Fluid} + * @return {@link IGT_FluidBuilder} self for call chaining + */ + @SuppressWarnings("UnusedReturnValue") // Last call in chain, may not use this returned value + IGT_FluidBuilder withFluidBlock(final Block fluidBlock); + + /** + * @param fromFluid the {@link Fluid} to copy the icons from + * @return {@link IGT_FluidBuilder} self for call chaining + */ + @SuppressWarnings("UnusedReturnValue") // Last call in chain, may not use this returned value + IGT_FluidBuilder withIconsFrom(@Nonnull final Fluid fromFluid); + + /** + * @param stillIconResourceLocation The {@link ResourceLocation} of the still fluid texture + * @param flowingIconResourceLocation The {@link ResourceLocation} of the flowing fluid texture + * @return {@link IGT_FluidBuilder} self for call chaining + */ + @SuppressWarnings("UnusedReturnValue") // Last call in chain, may not use this returned value + IGT_FluidBuilder withTextures(final ResourceLocation stillIconResourceLocation, + final ResourceLocation flowingIconResourceLocation); + + /** + * Builds the {@link IGT_Fluid} + * + * @return the built {@link IGT_Fluid} + */ + IGT_Fluid build(); + + /** + * Builds, then adds the {@link IGT_Fluid} to the {@link FluidRegistry} + * + * @return the {@link IGT_Fluid} + * @see #build() + * @see IGT_Fluid#addFluid() + */ + IGT_RegisteredFluid buildAndRegister(); +} diff --git a/src/main/java/gregtech/api/interfaces/fluid/IGT_RegisteredFluid.java b/src/main/java/gregtech/api/interfaces/fluid/IGT_RegisteredFluid.java new file mode 100644 index 0000000000..181824874c --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/fluid/IGT_RegisteredFluid.java @@ -0,0 +1,60 @@ +package gregtech.api.interfaces.fluid; + +import net.minecraft.item.ItemStack; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidContainerRegistry; + +import gregtech.api.enums.FluidState; +import gregtech.api.enums.Materials; + +public interface IGT_RegisteredFluid { + + /** + * Registers the containers in the {@link FluidContainerRegistry} for this {@link IGT_RegisteredFluid} + * + * @param fullContainer The full fluid container + * @param emptyContainer The empty fluid container + * @param containerSize The size of the container + * @return The {@link IGT_RegisteredFluid} for call chaining + */ + @SuppressWarnings("UnusedReturnValue") // Last call in chain, may not use this returned value + IGT_RegisteredFluid registerContainers(final ItemStack fullContainer, final ItemStack emptyContainer, + final int containerSize); + + /** + * Registers the bucket-sized 1000L containers in the {@link FluidContainerRegistry} for this + * {@link IGT_RegisteredFluid} + * + * @param fullContainer The full container to associate with this {@link IGT_RegisteredFluid} + * @param emptyContainer The empty container associate with this {@link IGT_RegisteredFluid} + * @return {@link IGT_RegisteredFluid} for call chaining + */ + @SuppressWarnings("UnusedReturnValue") // Last call in chain, may not use this returned value + IGT_RegisteredFluid registerBContainers(final ItemStack fullContainer, final ItemStack emptyContainer); + + /** + * Registers the potion-sized 250L containers in the {@link FluidContainerRegistry} for this + * {@link IGT_RegisteredFluid} + * + * @param fullContainer The full container to associate with this {@link IGT_RegisteredFluid} + * @param emptyContainer The empty container associate with this {@link IGT_RegisteredFluid} + * @return {@link IGT_RegisteredFluid} self for call chaining + */ + @SuppressWarnings("UnusedReturnValue") // Last call in chain, may not use this returned value + IGT_RegisteredFluid registerPContainers(final ItemStack fullContainer, final ItemStack emptyContainer); + + /** + * Updates the {@link Materials}'s fluids from this {@link IGT_RegisteredFluid}'s state + * + * @param material the {@link Materials} to configure based on this {@link IGT_RegisteredFluid} and + * {@link FluidState} + * @return The {@link IGT_RegisteredFluid} for call chaining + */ + @SuppressWarnings("UnusedReturnValue") // Last call in chain, may not use this returned value + IGT_RegisteredFluid configureMaterials(final Materials material); + + /** + * @return this {@link IGT_RegisteredFluid} cast to {@link Fluid} + */ + Fluid asFluid(); +} diff --git a/src/main/java/gregtech/api/interfaces/internal/IBCTileEntity.java b/src/main/java/gregtech/api/interfaces/internal/IBCTileEntity.java new file mode 100644 index 0000000000..4acfa62549 --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/internal/IBCTileEntity.java @@ -0,0 +1,8 @@ +package gregtech.api.interfaces.internal; + +/** + * A simple compound Interface for generic BuildCraft Code. + */ +public interface IBCTileEntity /* extends IPowerReceptor */ { + // +} diff --git a/src/main/java/gregtech/api/interfaces/internal/IGT_CraftingRecipe.java b/src/main/java/gregtech/api/interfaces/internal/IGT_CraftingRecipe.java new file mode 100644 index 0000000000..3f29736470 --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/internal/IGT_CraftingRecipe.java @@ -0,0 +1,8 @@ +package gregtech.api.interfaces.internal; + +import net.minecraft.item.crafting.IRecipe; + +public interface IGT_CraftingRecipe extends IRecipe { + + boolean isRemovable(); +} diff --git a/src/main/java/gregtech/api/interfaces/internal/IGT_Mod.java b/src/main/java/gregtech/api/interfaces/internal/IGT_Mod.java new file mode 100644 index 0000000000..dbf888ef96 --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/internal/IGT_Mod.java @@ -0,0 +1,50 @@ +package gregtech.api.interfaces.internal; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.world.World; + +/** + * Interface used by the Mods Main Class to reference to internals. + * <p/> + * Don't even think about including this File in your Mod. + */ +public interface IGT_Mod { + + /** + * This means that Server specific Basefiles are definitely existing! Not if the World is actually server side or + * not! + */ + boolean isServerSide(); + + /** + * This means that Client specific Basefiles are definitely existing! Not if the World is actually client side or + * not! + */ + boolean isClientSide(); + + /** + * This means that Bukkit specific Basefiles are definitely existing! Not if the World is actually bukkit server or + * not! + */ + boolean isBukkitSide(); + + /** + * works only ClientSide otherwise returns null + */ + EntityPlayer getThePlayer(); + + // ---------- Internal Usage Only ---------- + + /** + * works only ClientSide otherwise returns 0 + * + * @return the Index of the added Armor + */ + int addArmor(String aArmorPrefix); + + /** + * Plays the Sonictron Sound for the ItemStack on the Client Side + */ + void doSonictronSound(ItemStack aStack, World aWorld, double aX, double aY, double aZ); +} diff --git a/src/main/java/gregtech/api/interfaces/internal/IGT_RecipeAdder.java b/src/main/java/gregtech/api/interfaces/internal/IGT_RecipeAdder.java new file mode 100644 index 0000000000..e7abfea98f --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/internal/IGT_RecipeAdder.java @@ -0,0 +1,1069 @@ +package gregtech.api.interfaces.internal; + +import net.minecraft.item.ItemStack; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidStack; + +import gregtech.api.util.GT_Recipe; +import gregtech.api.util.GT_RecipeBuilder; + +public interface IGT_RecipeAdder { + + /** + * Adds a FusionreactorRecipe Does not work anymore! + */ + + @Deprecated + boolean addFusionReactorRecipe(ItemStack aInput1, ItemStack aInput2, ItemStack aOutput1, int aFusionDurationInTicks, + int aFusionEnergyPerTick, int aEnergyNeededForStartingFusion); + + /** + * Adds a FusionreactorRecipe + * + * @param aInput1 = first Input (not null, and respects StackSize) + * @param aInput2 = second Input (not null, and respects StackSize) + * @param aOutput1 = Output of the Fusion (can be null, and respects StackSize) + * @param aFusionDurationInTicks = How many ticks the Fusion lasts (must be > 0) + * @param aFusionEnergyPerTick = The EU generated per Tick (can even be negative!) + * @param aEnergyNeededForStartingFusion = EU needed for heating the Reactor up (must be >= 0) + * @return true if the Recipe got added, otherwise false. + */ + + @Deprecated + boolean addFusionReactorRecipe(FluidStack aInput1, FluidStack aInput2, FluidStack aOutput1, + int aFusionDurationInTicks, int aFusionEnergyPerTick, int aEnergyNeededForStartingFusion); + + /** + * Adds a Fusion Reactor Recipe + * + * @param FluidInputArray Array of input fluids. Up to 16. + * @param FluidOutputArray Array of output fluids. Up to 16. + * @param aFusionDurationInTicks How many ticks the Fusion lasts (must be > 0). + * @param aFusionEnergyPerTick The EU consumed per tick to keep the reaction going. + * @param aEnergyNeededForStartingFusion EU needed to initialize the fusion reaction. (must be >= 0). + * @return true if the recipe got added, otherwise false. + */ + + @Deprecated + boolean addFusionReactorRecipe(FluidStack[] FluidInputArray, FluidStack[] FluidOutputArray, + int aFusionDurationInTicks, int aFusionEnergyPerTick, int aEnergyNeededForStartingFusion); + + /** + * Adds a Centrifuge Recipe + * + * @param aInput1 must be != null + * @param aOutput1 must be != null + * @param aOutput2 can be null + * @param aOutput3 can be null + * @param aOutput4 can be null + * @param aDuration must be > 0 + */ + + @Deprecated + boolean addCentrifugeRecipe(ItemStack aInput1, int aInput2, ItemStack aOutput1, ItemStack aOutput2, + ItemStack aOutput3, ItemStack aOutput4, ItemStack aOutput5, ItemStack aOutput6, int aDuration); + + @Deprecated + boolean addCentrifugeRecipe(ItemStack aInput1, int aInput2, ItemStack aOutput1, ItemStack aOutput2, + ItemStack aOutput3, ItemStack aOutput4, ItemStack aOutput5, ItemStack aOutput6, int aDuration, int aEUt); + + /** + * Adds a Centrifuge Recipe + * + * @param aInput1 must be != null + * @param aOutput1 must be != null + * @param aOutput2 can be null + * @param aOutput3 can be null + * @param aOutput4 can be null + * @param aDuration must be > 0 + */ + @Deprecated + boolean addCentrifugeRecipe(ItemStack aInput1, ItemStack aInput2, FluidStack aFluidInput, FluidStack aFluidOutput, + ItemStack aOutput1, ItemStack aOutput2, ItemStack aOutput3, ItemStack aOutput4, ItemStack aOutput5, + ItemStack aOutput6, int[] aChances, int aDuration, int aEUt); + + @Deprecated + boolean addCentrifugeRecipe(ItemStack aInput1, ItemStack aInput2, FluidStack aFluidInput, FluidStack aFluidOutput, + ItemStack aOutput1, ItemStack aOutput2, ItemStack aOutput3, ItemStack aOutput4, ItemStack aOutput5, + ItemStack aOutput6, int[] aChances, int aDuration, int aEUt, boolean aCleanroom); + + /** + * @param aInput1 must be != null + * @param aOutput1 must be != null + * @param aDuration must be > 0 + * @return if the recipe was successfully added + */ + + @Deprecated + boolean addCompressorRecipe(ItemStack aInput1, ItemStack aOutput1, int aDuration, int aEUt); + + /** + * Adds a Electrolyzer Recipe + * + * @param aInput1 must be != null + * @param aOutput1 must be != null + * @param aOutput2 can be null + * @param aOutput3 can be null + * @param aOutput4 can be null + * @param aDuration must be > 0 + * @param aEUt should be > 0 + */ + @Deprecated + boolean addElectrolyzerRecipe(ItemStack aInput1, int aInput2, ItemStack aOutput1, ItemStack aOutput2, + ItemStack aOutput3, ItemStack aOutput4, ItemStack aOutput5, ItemStack aOutput6, int aDuration, int aEUt); + + /** + * Adds a Electrolyzer Recipe + * + * @param aInput1 must be != null + * @param aOutput1 must be != null + * @param aOutput2 can be null + * @param aOutput3 can be null + * @param aOutput4 can be null + * @param aDuration must be > 0 + * @param aEUt should be > 0 + */ + @Deprecated + boolean addElectrolyzerRecipe(ItemStack aInput1, ItemStack aInput2, FluidStack aFluidInput, FluidStack aFluidOutput, + ItemStack aOutput1, ItemStack aOutput2, ItemStack aOutput3, ItemStack aOutput4, ItemStack aOutput5, + ItemStack aOutput6, int[] aChances, int aDuration, int aEUt); + + /** + * Adds a Chemical Recipe + * + * @param aInput1 must be != null + * @param aInput2 must be != null + * @param aOutput must be != null + * @param aDuration must be > 0 + */ + @Deprecated + boolean addChemicalRecipe(ItemStack aInput1, ItemStack aInput2, ItemStack aOutput, int aDuration); + + @Deprecated + boolean addChemicalRecipe(ItemStack aInput1, ItemStack aInput2, ItemStack aOutput, int aDuration, int aEUt); + + /** + * Adds a Chemical Recipe + * + * @param aInput1 must be != null + * @param aInput2 must be != null + * @param aOutput must be != null + * @param aDuration must be > 0 + */ + @Deprecated + boolean addChemicalRecipe(ItemStack aInput1, ItemStack aInput2, FluidStack aFluidInput, FluidStack aFluidOutput, + ItemStack aOutput, int aDuration); + + /** + * Adds a Chemical Recipe Only use this when the recipe conflicts in MultiBlock! + * + * @param aInput1 must be != null + * @param aInput2 must be != null + * @param aOutput must be != null + * @param aOutput2 must be != null + * @param aDuration must be > 0 + */ + @Deprecated + boolean addChemicalRecipeForBasicMachineOnly(ItemStack aInput1, ItemStack aInput2, FluidStack aFluidInput, + FluidStack aFluidOutput, ItemStack aOutput, ItemStack aOutput2, int aDuration, int aEUtick); + + /** + * Adds a Chemical Recipe + * + * @param aInput1 must be != null + * @param aInput2 must be != null + * @param aOutput must be != null + * @param aOutput2 must be != null + * @param aDuration must be > 0 + */ + @Deprecated + boolean addChemicalRecipe(ItemStack aInput1, ItemStack aInput2, FluidStack aFluidInput, FluidStack aFluidOutput, + ItemStack aOutput, ItemStack aOutput2, int aDuration); + + /** + * Adds Recipes for creating a radically polymerized polymer from a base Material (for example Ethylene -> + * Polyethylene) + * + * @param aBasicMaterial The basic Material + * @param aBasicMaterialCell The corresponding Cell basic Material + * @param aPolymer The polymer + */ + void addDefaultPolymerizationRecipes(Fluid aBasicMaterial, ItemStack aBasicMaterialCell, Fluid aPolymer); + + /** + * Adds a Chemical Recipe + * + * @param aInput1 must be != null + * @param aInput2 must be != null + * @param aOutput must be != null + * @param aDuration must be > 0 + * @param aEUtick must be > 0 + */ + @Deprecated + boolean addChemicalRecipe(ItemStack aInput1, ItemStack aInput2, FluidStack aFluidInput, FluidStack aFluidOutput, + ItemStack aOutput, int aDuration, int aEUtick); + + @Deprecated + boolean addChemicalRecipe(ItemStack aInput1, ItemStack aInput2, FluidStack aFluidInput, FluidStack aFluidOutput, + ItemStack aOutput, ItemStack aOutput2, int aDuration, int aEUtick, boolean aCleanroom); + + /** + * Adds a Chemical Recipe + * + * @param aInput1 must be != null + * @param aInput2 must be != null + * @param aOutput must be != null + * @param aOutput2 must be != null + * @param aDuration must be > 0 + * @param aEUtick must be > 0 + */ + @Deprecated + boolean addChemicalRecipe(ItemStack aInput1, ItemStack aInput2, FluidStack aFluidInput, FluidStack aFluidOutput, + ItemStack aOutput, ItemStack aOutput2, int aDuration, int aEUtick); + + /** + * Adds a Chemical Recipe that only exists in the Large Chemical Reactor + * + * @param aInputs item inputs + * @param aFluidInputs fluid inputs + * @param aFluidOutputs fluid outputs + * @param aOutputs item outputs + * @param aDuration must be > 0 + * @param aEUtick must be > 0 <br> + * aInputs and aFluidInputs must contain at least one valid input. <br> + * aOutputs and aFluidOutputs must contain at least one valid output. + * + */ + @Deprecated + boolean addMultiblockChemicalRecipe(ItemStack[] aInputs, FluidStack[] aFluidInputs, FluidStack[] aFluidOutputs, + ItemStack[] aOutputs, int aDuration, int aEUtick); + + /** + * Adds a Blast Furnace Recipe + * + * @param aInput1 must be != null + * @param aInput2 can be null + * @param aOutput1 must be != null + * @param aOutput2 can be null + * @param aDuration must be > 0 + * @param aEUt should be > 0 + * @param aLevel should be > 0 is the minimum Heat Level needed for this Recipe + */ + @Deprecated + boolean addBlastRecipe(ItemStack aInput1, ItemStack aInput2, ItemStack aOutput1, ItemStack aOutput2, int aDuration, + int aEUt, int aLevel); + + /** + * Adds a Blast Furnace Recipe + * + * @param aInput1 must be != null + * @param aInput2 can be null + * @param aOutput1 must be != null + * @param aOutput2 can be null + * @param aDuration must be > 0 + * @param aEUt should be > 0 + * @param aLevel should be > 0 is the minimum Heat Level needed for this Recipe + */ + @Deprecated + boolean addBlastRecipe(ItemStack aInput1, ItemStack aInput2, FluidStack aFluidInput, FluidStack aFluidOutput, + ItemStack aOutput1, ItemStack aOutput2, int aDuration, int aEUt, int aLevel); + + @Deprecated + boolean addBlastRecipe(ItemStack aInput1, ItemStack aInput2, ItemStack aInput3, ItemStack aInput4, + FluidStack aFluidInput, FluidStack aFluidOutput, ItemStack aOutput1, ItemStack aOutput2, ItemStack aOutput3, + ItemStack aOutput4, int aDuration, int aEUt, int aLevel); + + /** + * Adds a Plasma Forge Recipe + * + * @param ItemInputArray Array of input items. + * @param FluidInputArray Array of output items. + * @param OutputItemArray Array of input fluids. + * @param FluidOutputArray Array of output items. + * @param aDuration Must be > 0. Duration in ticks. + * @param aEUt Should be > 0. EU/t. + * @param coil_heat_level Should be > 0. Heat of the coils used. + */ + @Deprecated + boolean addPlasmaForgeRecipe(ItemStack[] ItemInputArray, FluidStack[] FluidInputArray, ItemStack[] OutputItemArray, + FluidStack[] FluidOutputArray, int aDuration, int aEUt, int coil_heat_level); + + @Deprecated + boolean addPrimitiveBlastRecipe(ItemStack aInput1, ItemStack aInput2, int aCoalAmount, ItemStack aOutput1, + ItemStack aOutput2, int aDuration); + + /** + * Adds a Canning Machine Recipe + * + * @param aInput1 must be != null + * @param aOutput1 must be != null + * @param aDuration must be > 0, 100 ticks is standard. + * @param aEUt should be > 0, 1 EU/t is standard. + */ + @Deprecated + boolean addCannerRecipe(ItemStack aInput1, ItemStack aInput2, ItemStack aOutput1, ItemStack aOutput2, int aDuration, + int aEUt); + + /** + * Adds an Alloy Smelter Recipe + * + * @param aInput1 must be != null + * @param aInput2 can be null + * @param aOutput1 must be != null + * @param aDuration must be > 0 + * @param aEUt should be > 0 + */ + @Deprecated + boolean addAlloySmelterRecipe(ItemStack aInput1, ItemStack aInput2, ItemStack aOutput1, int aDuration, int aEUt); + + @Deprecated + boolean addAlloySmelterRecipe(ItemStack aInput1, ItemStack aInput2, ItemStack aOutput1, int aDuration, int aEUt, + boolean hidden); + + /** + * Adds an Assembler Recipe + * + * @param aInput1 must be != null + * @param aOutput1 must be != null + * @param aInput2 must be != null + * @param aDuration must be > 0 + * @param aEUt should be > 0 + */ + @Deprecated + boolean addAssemblerRecipe(ItemStack aInput1, ItemStack aInput2, ItemStack aOutput1, int aDuration, int aEUt); + + /** + * Adds an Assembler Recipe + * + * @param aInputs must be != null + * @param aOutput1 must be != null + * @param aDuration must be > 0 + * @param aEUt should be > 0 + * + */ + @Deprecated + boolean addAssemblerRecipe(ItemStack[] aInputs, FluidStack aFluidInput, ItemStack aOutput1, int aDuration, + int aEUt); + + /** + * Adds an Assembler Recipe + * + * @param aInput1 must be != null + * @param aOutput1 must be != null + * @param aDuration must be > 0 + * @param aEUt should be > 0 + */ + @Deprecated + boolean addAssemblerRecipe(ItemStack aInput1, ItemStack aInput2, FluidStack aFluidInput, ItemStack aOutput1, + int aDuration, int aEUt); + + @Deprecated + boolean addAssemblerRecipe(ItemStack aInput1, Object aOreDict, int aAmount, FluidStack aFluidInput, + ItemStack aOutput1, int aDuration, int aEUt); + + @Deprecated + boolean addAssemblerRecipe(ItemStack[] aInputs, Object aOreDict, int aAmount, FluidStack aFluidInput, + ItemStack aOutput1, int aDuration, int aEUt); + + @Deprecated + boolean addAssemblerRecipe(ItemStack aInput1, ItemStack aInput2, FluidStack aFluidInput, ItemStack aOutput1, + int aDuration, int aEUt, boolean aCleanroom); + + @Deprecated + boolean addAssemblerRecipe(ItemStack[] aInputs, FluidStack aFluidInput, ItemStack aOutput1, int aDuration, int aEUt, + boolean aCleanroom); + + /** + * Adds an Circuit Assembler Recipe + * + * @param aInputs must be 1-6 ItemStacks + * @param aFluidInput 0-1 fluids + * @param aOutput must be != null + * @param aDuration must be > 0 + * @param aEUt should be > 0 + */ + @Deprecated + boolean addCircuitAssemblerRecipe(ItemStack[] aInputs, FluidStack aFluidInput, ItemStack aOutput, int aDuration, + int aEUt); + + @Deprecated + boolean addCircuitAssemblerRecipe(ItemStack[] aInputs, FluidStack aFluidInput, ItemStack aOutput, int aDuration, + int aEUt, boolean aCleanroom); + + /** + * Adds an Assemblyline Recipe + * + * @param aInputs must be != null, 4-16 inputs + * @param aFluidInputs 0-4 fluids + * @param aOutput1 must be != null + * @param aDuration must be > 0 + * @param aEUt should be > 0 + */ + @Deprecated + boolean addAssemblylineRecipe(ItemStack aResearchItem, int aResearchTime, ItemStack[] aInputs, + FluidStack[] aFluidInputs, ItemStack aOutput1, int aDuration, int aEUt); + + /** + * Adds a Assemblyline Recipe + * + * @param aInputs elements should be: ItemStack for single item; ItemStack[] for multiple equivalent items; + * {OreDict, amount} for oredict. + */ + @Deprecated + boolean addAssemblylineRecipe(ItemStack aResearchItem, int aResearchTime, Object[] aInputs, + FluidStack[] aFluidInputs, ItemStack aOutput1, int aDuration, int aEUt); + + /** + * Adds a Forge Hammer Recipe + * + * @param aInput1 must be != null + * @param aOutput1 must be != null + * @param aDuration must be > 0 + * @param aEUt should be > 0 + */ + @Deprecated + boolean addForgeHammerRecipe(ItemStack aInput1, ItemStack aOutput1, int aDuration, int aEUt); + + // Allows fluids as well as multiple items. + @Deprecated + boolean addForgeHammerRecipe(ItemStack[] ItemInputArray, FluidStack[] FluidInputArray, ItemStack[] ItemOutputArray, + FluidStack[] FluidOutputArray, int aDuration, int aEUt); + + /** + * Adds a Wiremill Recipe + * + * @param aInput must be != null + * @param aOutput must be != null + * @param aDuration must be > 0 + * @param aEUt should be > 0 + */ + @Deprecated + boolean addWiremillRecipe(ItemStack aInput, ItemStack aOutput, int aDuration, int aEUt); + + @Deprecated + boolean addWiremillRecipe(ItemStack aInput, ItemStack aCircuit, ItemStack aOutput, int aDuration, int aEUt); + + /** + * Adds a Polariser Recipe + * + * @param aInput must be != null + * @param aOutput must be != null + * @param aDuration must be > 0 + * @param aEUt should be > 0 + */ + @Deprecated + boolean addPolarizerRecipe(ItemStack aInput, ItemStack aOutput, int aDuration, int aEUt); + + /** + * Adds a Plate Bending Machine Recipe + * + * @param aInput must be != null + * @param aOutput must be != null + * @param aDuration must be > 0 + * @param aEUt should be > 0 + */ + @Deprecated + boolean addBenderRecipe(ItemStack aInput, ItemStack aOutput, int aDuration, int aEUt); + + @Deprecated + boolean addBenderRecipe(ItemStack aInput, ItemStack aCircuit, ItemStack aOutput, int aDuration, int aEUt); + + /** + * Adds a Extruder Machine Recipe + * + * @param aInput must be != null + * @param aShape must be != null, Set the stackSize to 0 if you don't want to let it consume this Item. + * @param aOutput must be != null + * @param aDuration must be > 0 + * @param aEUt should be > 0 + */ + @Deprecated + boolean addExtruderRecipe(ItemStack aInput, ItemStack aShape, ItemStack aOutput, int aDuration, int aEUt); + + /** + * Adds a Slicer Machine Recipe + * + * @param aInput must be != null + * @param aShape must be != null, Set the stackSize to 0 if you don't want to let it consume this Item. + * @param aOutput must be != null + * @param aDuration must be > 0 + * @param aEUt should be > 0 + */ + @Deprecated + boolean addSlicerRecipe(ItemStack aInput, ItemStack aShape, ItemStack aOutput, int aDuration, int aEUt); + + /** + * @param aInput must be != null + * @param aFluidInput must be != null + * @param aOutput1 must be != null + * @param aDuration must be > 0 + * @param aEUt should be > 0 + * @return if the recipe was successfully added + */ + @Deprecated + boolean addOreWasherRecipe(ItemStack aInput, ItemStack aOutput1, ItemStack aOutput2, ItemStack aOutput3, + FluidStack aFluidInput, int aDuration, int aEUt); + + @Deprecated + boolean addOreWasherRecipe(ItemStack aInput, ItemStack aOutput1, ItemStack aOutput2, ItemStack aOutput3, + FluidStack aFluidInput, int[] aChances, int aDuration, int aEUt); + + /** + * Adds an Implosion Compressor Recipe + * + * @param aInput1 must be != null + * @param aInput2 amount of ITNT, should be > 0 + * @param aOutput1 must be != null + * @param aOutput2 can be null + */ + @Deprecated + boolean addImplosionRecipe(ItemStack aInput1, int aInput2, ItemStack aOutput1, ItemStack aOutput2); + + /** + * Adds a Grinder Recipe + * + * @param aInput1 must be != null + * @param aInput2 id for the Cell needed for this Recipe + * @param aOutput1 must be != null + * @param aOutput2 can be null + * @param aOutput3 can be null + * @param aOutput4 can be null + */ + @Deprecated + boolean addGrinderRecipe(ItemStack aInput1, ItemStack aInput2, ItemStack aOutput1, ItemStack aOutput2, + ItemStack aOutput3, ItemStack aOutput4); + + /** + * Adds a Distillation Tower Recipe + * + * @param aInput must be != null + * @param aOutputs must be != null 1-5 Fluids + * @param aOutput2 can be null + */ + @Deprecated + boolean addDistillationTowerRecipe(FluidStack aInput, FluidStack[] aOutputs, ItemStack aOutput2, int aDuration, + int aEUt); + + @Deprecated + boolean addDistillationTowerRecipe(FluidStack aInput, ItemStack[] aCircuit, FluidStack[] aOutputs, + ItemStack aOutput2, int aDuration, int aEUt); + + @Deprecated + boolean addSimpleArcFurnaceRecipe(ItemStack aInput, FluidStack aFluidInput, ItemStack[] aOutputs, int[] aChances, + int aDuration, int aEUt); + + @Deprecated + boolean addPlasmaArcFurnaceRecipe(ItemStack aInput, FluidStack aFluidInput, ItemStack[] aOutputs, int[] aChances, + int aDuration, int aEUt); + + @Deprecated + boolean addPlasmaArcFurnaceRecipe(ItemStack aInput, FluidStack aFluidInput, ItemStack[] aOutputs, + FluidStack aFluidPutput, int[] aChances, int aDuration, int aEUt); + + /** + * Adds a Distillation Tower Recipe + */ + @Deprecated + boolean addDistillationRecipe(ItemStack aInput1, int aInput2, ItemStack aOutput1, ItemStack aOutput2, + ItemStack aOutput3, ItemStack aOutput4, int aDuration, int aEUt); + + /** + * Adds a Lathe Machine Recipe + */ + @Deprecated + boolean addLatheRecipe(ItemStack aInput1, ItemStack aOutput1, ItemStack aOutput2, int aDuration, int aEUt); + + /** + * Adds a Cutter Recipe + */ + @Deprecated + boolean addCutterRecipe(ItemStack aInput, FluidStack aLubricant, ItemStack aOutput1, ItemStack aOutput2, + int aDuration, int aEUt); + + /** + * Adds Cutter Recipes with default Lubricants + */ + @Deprecated + boolean addCutterRecipe(ItemStack aInput, ItemStack aOutput1, ItemStack aOutput2, int aDuration, int aEUt); + + @Deprecated + boolean addCutterRecipe(ItemStack aInput, ItemStack aOutput1, ItemStack aOutput2, int aDuration, int aEUt, + boolean aCleanroom); + + @Deprecated + boolean addCutterRecipe(ItemStack aInput, ItemStack aCircuit, ItemStack aOutput1, ItemStack aOutput2, int aDuration, + int aEUt); + + @Deprecated + boolean addCutterRecipe(ItemStack aInput, ItemStack aCircuit, ItemStack aOutput1, ItemStack aOutput2, int aDuration, + int aEUt, boolean aCleanroom); + + @Deprecated + boolean addCutterRecipe(ItemStack[] aInputs, ItemStack[] aOutputs, int aDuration, int aEUt, int aSpecial); + + @Deprecated + boolean addCutterRecipe(ItemStack[] aInputs, ItemStack[] aOutputs, int aDuration, int aEUt, boolean aCleanroom); + + /** + * Adds a Boxing Recipe + */ + @Deprecated + boolean addBoxingRecipe(ItemStack aContainedItem, ItemStack aEmptyBox, ItemStack aFullBox, int aDuration, int aEUt); + + /** + * @param aInput must be != null + * @param aOutput1 must be != null + * @param aDuration must be > 0 + * @param aEUt should be > 0 + * @return if the recipe was successfully added + */ + @Deprecated + boolean addThermalCentrifugeRecipe(ItemStack aInput, ItemStack aOutput1, ItemStack aOutput2, ItemStack aOutput3, + int aDuration, int aEUt); + + @Deprecated + boolean addThermalCentrifugeRecipe(ItemStack aInput, ItemStack aOutput1, ItemStack aOutput2, ItemStack aOutput3, + int[] aChances, int aDuration, int aEUt); + + /** + * Adds an Unboxing Recipe + */ + @Deprecated + boolean addUnboxingRecipe(ItemStack aFullBox, ItemStack aContainedItem, ItemStack aEmptyBox, int aDuration, + int aEUt); + + /** + * Adds a Vacuum Freezer Recipe + * + * @param aInput1 must be != null + * @param aOutput1 must be != null + * @param aDuration must be > 0 + */ + @Deprecated + boolean addVacuumFreezerRecipe(ItemStack aInput1, ItemStack aOutput1, int aDuration); + + @Deprecated + boolean addVacuumFreezerRecipe(ItemStack aInput1, ItemStack aOutput1, int aDuration, int aEUt); + + @Deprecated + boolean addVacuumFreezerRecipe(FluidStack aInput1, FluidStack aOutput1, int aDuration, int aEUt); + + @Deprecated + boolean addVacuumFreezerRecipe(ItemStack[] aItemInput, FluidStack[] aFluidInput, ItemStack[] aItemOutput, + FluidStack[] aFluidOutput, int aDuration, int aEUt); + + /** + * Adds a Fuel for My Generators + * + * @param aInput1 must be != null + * @param aOutput1 can be null + * @param aEU EU per MilliBucket. If no Liquid Form of this Container is available, then it will give you + * EU*1000 per Item. + * @param aType 0 = Diesel; 1 = Gas Turbine; 2 = Thermal; 3 = Dense Fluid; 4 = Plasma; 5 = Magic; And if + * something is unclear or missing, then look at the GT_Recipe-Class + */ + @Deprecated + boolean addFuel(ItemStack aInput1, ItemStack aOutput1, int aEU, int aType); + + /** + * Adds an Amplifier Recipe for the Amplifabricator + */ + @Deprecated + boolean addAmplifier(ItemStack aAmplifierItem, int aDuration, int aAmplifierAmountOutputted); + + /** + * Adds a Recipe for the Brewing Machine (intentionally limited to Fluid IDs) + */ + @Deprecated + boolean addBrewingRecipe(ItemStack aIngredient, Fluid aInput, Fluid aOutput, boolean aHidden); + + @Deprecated + boolean addBrewingRecipe(ItemStack aIngredient, Fluid aInput, Fluid aOutput, int aDuration, int aEUt, + boolean aHidden); + + @Deprecated + boolean addBrewingRecipeCustom(ItemStack aIngredient, FluidStack aInput, FluidStack aOutput, int aDuration, + int aEUt, boolean aHidden); + + /** + * Adds a Recipe for the Fermenter + */ + @Deprecated + boolean addFermentingRecipe(FluidStack aInput, FluidStack aOutput, int aDuration, boolean aHidden); + + @Deprecated + boolean addFermentingRecipe(FluidStack aInput, FluidStack aOutput, int aDuration, int aEUT, boolean aHidden); + + /** + * Adds a Recipe for the Fluid Heater + */ + @Deprecated + boolean addFluidHeaterRecipe(ItemStack aCircuit, FluidStack aOutput, int aDuration, int aEUt); + + @Deprecated + boolean addFluidHeaterRecipe(ItemStack aCircuit, FluidStack aInput, FluidStack aOutput, int aDuration, int aEUt); + + /** + * Adds a Recipe for the Distillery + */ + @Deprecated + boolean addDistilleryRecipe(ItemStack aCircuit, FluidStack aInput, FluidStack aOutput, ItemStack aSolidOutput, + int aDuration, int aEUt, boolean aHidden); + + @Deprecated + boolean addDistilleryRecipe(ItemStack aCircuit, FluidStack aInput, FluidStack aOutput, int aDuration, int aEUt, + boolean aHidden); + + @Deprecated + boolean addDistilleryRecipe(int circuitConfig, FluidStack aInput, FluidStack aOutput, ItemStack aSolidOutput, + int aDuration, int aEUt, boolean aHidden); + + @Deprecated + boolean addDistilleryRecipe(int aCircuit, FluidStack aInput, FluidStack aOutput, int aDuration, int aEUt, + boolean aHidden); + + /** + * Adds a Recipe for the Fluid Solidifier + */ + @Deprecated + boolean addFluidSolidifierRecipe(ItemStack aMold, FluidStack aInput, ItemStack aOutput, int aDuration, int aEUt); + + /** + * Adds a Recipe for the Fluid Solidifier + */ + @Deprecated + boolean addFluidSolidifierRecipe(final ItemStack[] itemInputs, final FluidStack[] fluidInputs, + final ItemStack[] itemOutputs, final FluidStack[] fluidOutputs, final int EUPerTick, + final int aDurationInTicks); + + /** + * Adds a Recipe for Fluid Smelting + */ + @Deprecated + boolean addFluidSmelterRecipe(ItemStack aInput, ItemStack aRemains, FluidStack aOutput, int aChance, int aDuration, + int aEUt); + + /** + * Adds a Recipe for Fluid Smelting + */ + @Deprecated + boolean addFluidSmelterRecipe(ItemStack aInput, ItemStack aRemains, FluidStack aOutput, int aChance, int aDuration, + int aEUt, boolean hidden); + + /** + * Adds a Recipe for Fluid Extraction + */ + @Deprecated + boolean addFluidExtractionRecipe(ItemStack aInput, ItemStack aRemains, FluidStack aOutput, int aChance, + int aDuration, int aEUt); + + /** + * Adds a Recipe for the Fluid Canner + */ + @Deprecated + boolean addFluidCannerRecipe(ItemStack aInput, ItemStack aOutput, FluidStack aFluidInput, FluidStack aFluidOutput); + + @Deprecated + boolean addFluidCannerRecipe(ItemStack aInput, ItemStack aOutput, FluidStack aFluidInput, FluidStack aFluidOutput, + int aDuration, int aEUt); + + /** + * Adds a Recipe for the Chemical Bath + */ + @Deprecated + boolean addChemicalBathRecipe(ItemStack aInput, FluidStack aBathingFluid, ItemStack aOutput1, ItemStack aOutput2, + ItemStack aOutput3, int[] aChances, int aDuration, int aEUt); + + @Deprecated + boolean addChemicalBathRecipe(ItemStack aInput, FluidStack aBathingFluid, FluidStack aFluidOutput, + ItemStack aOutput1, ItemStack aOutput2, ItemStack aOutput3, int[] aChances, int aDuration, int aEUt); + + /** + * Adds a Recipe for the Electromagnetic Separator + */ + @Deprecated + boolean addElectromagneticSeparatorRecipe(ItemStack aInput, ItemStack aOutput1, ItemStack aOutput2, + ItemStack aOutput3, int[] aChances, int aDuration, int aEUt); + + /** + * Adds a Recipe for the Extractor + */ + @Deprecated + boolean addExtractorRecipe(ItemStack aInput, ItemStack aOutput, int aDuration, int aEUt); + + /** + * Adds a Recipe for the Printer + */ + @Deprecated + boolean addPrinterRecipe(ItemStack aInput, FluidStack aFluid, ItemStack aSpecialSlot, ItemStack aOutput, + int aDuration, int aEUt); + + /** + * Adds a Recipe for the Autoclave + */ + @Deprecated + boolean addAutoclaveRecipe(ItemStack aInput, FluidStack aFluid, ItemStack aOutput, int aChance, int aDuration, + int aEUt); + + @Deprecated + boolean addAutoclaveRecipe(ItemStack aInput, FluidStack aFluid, ItemStack aOutput, int aChance, int aDuration, + int aEUt, boolean aCleanroom); + + @Deprecated + boolean addAutoclaveRecipe(ItemStack aInput, ItemStack aCircuit, FluidStack aFluid, ItemStack aOutput, int aChance, + int aDuration, int aEUt, boolean aCleanroom); + + @Deprecated + boolean addAutoclaveRecipe(ItemStack aInput, ItemStack aCircuit, FluidStack aFluidIn, FluidStack aFluidOut, + ItemStack aOutput, int aChance, int aDuration, int aEUt, boolean aCleanroom); + + @Deprecated + boolean addAutoclaveSpaceRecipe(ItemStack aInput, FluidStack aFluid, ItemStack aOutput, int aChance, int aDuration, + int aEUt, boolean aCleanroom); + + @Deprecated + boolean addAutoclaveSpaceRecipe(ItemStack aInput, ItemStack aCircuit, FluidStack aFluid, ItemStack aOutput, + int aChance, int aDuration, int aEUt, boolean aCleanroom); + + @Deprecated + boolean addAutoclave4Recipe(ItemStack aInput, ItemStack aCircuit, FluidStack aFluidIn, FluidStack aFluidOut, + ItemStack[] aOutputs, int[] aChances, int aDuration, int aEUt, boolean aCleanroom); + + /** + * Adds a Recipe for the Mixer + */ + @Deprecated + boolean addMixerRecipe(ItemStack aInput1, ItemStack aInput2, ItemStack aInput3, ItemStack aInput4, + FluidStack aFluidInput, FluidStack aFluidOutput, ItemStack aOutput, int aDuration, int aEUt); + + @Deprecated + boolean addMixerRecipe(ItemStack aInput1, ItemStack aInput2, ItemStack aInput3, ItemStack aInput4, + ItemStack aInput5, ItemStack aInput6, FluidStack aFluidInput, FluidStack aFluidOutput, ItemStack aOutput, + int aDuration, int aEUt); + + @Deprecated + boolean addMixerRecipe(ItemStack aInput1, ItemStack aInput2, ItemStack aInput3, ItemStack aInput4, + ItemStack aInput5, ItemStack aInput6, ItemStack aInput7, ItemStack aInput8, ItemStack aInput9, + FluidStack aFluidInput, FluidStack aFluidOutput, ItemStack aOutput, int aDuration, int aEUt); + + @Deprecated + boolean addMixerRecipe(ItemStack aInput1, ItemStack aInput2, ItemStack aInput3, ItemStack aInput4, + ItemStack aInput5, ItemStack aInput6, ItemStack aInput7, ItemStack aInput8, ItemStack aInput9, + FluidStack aFluidInput, FluidStack aFluidOutput, ItemStack aOutput1, ItemStack aOutput2, ItemStack aOutput3, + ItemStack aOutput4, int aDuration, int aEUt); + + // Use me only from now on! + @Deprecated + boolean addMixerRecipe(ItemStack[] ItemInputArray, FluidStack[] FluidInputArray, ItemStack[] ItemOutputArray, + FluidStack[] FluidOutputArray, int aDuration, int aEUt); + + /** + * Adds a Recipe for the Laser Engraver. + */ + @Deprecated + boolean addLaserEngraverRecipe(ItemStack aItemToEngrave, ItemStack aLens, ItemStack aEngravedItem, int aDuration, + int aEUt); + + /** + * Adds a Recipe for the Laser Engraver. + */ + @Deprecated + boolean addLaserEngraverRecipe(ItemStack aItemToEngrave, ItemStack aLens, ItemStack aEngravedItem, int aDuration, + int aEUt, boolean aCleanroom); + + /** + * Adds a Generalised Laser Engraver Recipe. + * + * @param ItemInputArray Array of input items. + * @param FluidInputArray Array of output items. + * @param OutputItemArray Array of input fluids. + * @param FluidOutputArray Array of output items. + * @param aDuration Must be > 0. Duration in ticks. + * @param aEUt Should be > 0. EU/t. + * @param aCleanroom Boolean for usage of cleanroom in recipe. + */ + @Deprecated + boolean addLaserEngraverRecipe(ItemStack[] ItemInputArray, FluidStack[] FluidInputArray, + ItemStack[] OutputItemArray, FluidStack[] FluidOutputArray, int aDuration, int aEUt, boolean aCleanroom); + + /** + * Adds a Recipe for the Forming Press + */ + @Deprecated + boolean addFormingPressRecipe(ItemStack aItemToImprint, ItemStack aForm, ItemStack aImprintedItem, int aDuration, + int aEUt); + + // Allows more than 2 inputs and multiple outputs + @Deprecated + boolean addFormingPressRecipe(ItemStack[] ItemInputArray, ItemStack[] OutputItemArray, int aDuration, int aEUt); + + /** + * Adds a Recipe for the Sifter. (up to 9 Outputs) + */ + @Deprecated + boolean addSifterRecipe(ItemStack aItemToSift, ItemStack[] aSiftedItems, int[] aChances, int aDuration, int aEUt); + + /** + * Adds a Generalised Sifter Recipe. + * + * @param ItemInputArray Array of input items. + * @param FluidInputArray Array of output items. + * @param OutputItemArray Array of input fluids. + * @param FluidOutputArray Array of output items. + * @param aChances Array of output chances. + * @param aDuration Must be > 0. Duration in ticks. + * @param aEUt Should be > 0. EU/t. + * @param aCleanroom Boolean for usage of cleanroom in recipe. + */ + @Deprecated + boolean addSifterRecipe(ItemStack[] ItemInputArray, FluidStack[] FluidInputArray, ItemStack[] OutputItemArray, + FluidStack[] FluidOutputArray, int[] aChances, int aDuration, int aEUt, boolean aCleanroom); + + /** + * Adds a Recipe for the Arc Furnace. (up to 4 Outputs) + */ + @Deprecated + boolean addArcFurnaceRecipe(ItemStack aInput, ItemStack[] aOutputs, int[] aChances, int aDuration, int aEUt); + + /** + * Adds a Recipe for the Arc Furnace. (up to 4 Outputs) + */ + @Deprecated + boolean addArcFurnaceRecipe(ItemStack aInput, ItemStack[] aOutputs, int[] aChances, int aDuration, int aEUt, + boolean hidden); + + /** + * Adds a Recipe for the GT Pulveriser. (up to 4 Outputs) + */ + @Deprecated + boolean addPulveriserRecipe(ItemStack aInput, ItemStack[] aOutputs, int[] aChances, int aDuration, int aEUt); + + /** + * Adds a Recipe for the GT Pulveriser. (up to 4 Outputs) + */ + @Deprecated + boolean addPulveriserRecipe(ItemStack aInput, ItemStack[] aOutputs, int[] aChances, int aDuration, int aEUt, + boolean hidden); + + /** + * Adds a Distillation Tower Recipe Every Fluid also gets separate distillation recipes + * + * @param aInput must be != null + * @param aOutputs must be != null 1-5 Fluids + * @param aOutput2 can be null + */ + @Deprecated + boolean addUniversalDistillationRecipe(FluidStack aInput, FluidStack[] aOutputs, ItemStack aOutput2, int aDuration, + int aEUt); + + @Deprecated + boolean addUniversalDistillationRecipewithCircuit(FluidStack aInput, ItemStack[] aCircuit, FluidStack[] aOutputs, + ItemStack aOutput2, int aDuration, int aEUt); + + /** + * Adds Pyrolyse Recipe + * + * @param aInput input item stack + * @param aFluidInput fluid input + * @param intCircuit circuit index + * @param aOutput output item stack + * @param aFluidOutput fluid output + * @param aDuration recipe duration + * @param aEUt recipe EU/t expenditure + * + * @return if the recipe was successfully added + */ + @Deprecated + boolean addPyrolyseRecipe(ItemStack aInput, FluidStack aFluidInput, int intCircuit, ItemStack aOutput, + FluidStack aFluidOutput, int aDuration, int aEUt); + + /** + * Adds Oil Cracking Recipe + * + * @param aInput input item stack + * @param aOutput output item stack + * @param aDuration recipe duration + * @param aEUt recipe EU/t expenditure + */ + @Deprecated + boolean addCrackingRecipe(FluidStack aInput, FluidStack aOutput, int aDuration, int aEUt); + + /** + * Adds Oil Cracking Recipe + * + * @param circuitConfig The circuit configuration to control cracking severity + * @param aInput The fluid to be cracked + * @param aInput2 The fluid to catalyze the cracking (typically Hydrogen or Steam) + * @param aOutput The cracked fluid + * @param aDuration recipe duration + * @param aEUt recipe EU/t expenditure + */ + @Deprecated + boolean addCrackingRecipe(int circuitConfig, FluidStack aInput, FluidStack aInput2, FluidStack aOutput, + int aDuration, int aEUt); + + /** + * Adds a Sound to the Sonictron9001 you should NOT call this in the preInit-Phase! + * + * @param aItemStack = The Item you want to display for this Sound + * @param aSoundName = The Name of the Sound in the resources/newsound-folder like Vanillasounds + * @return true if the Sound got added, otherwise false. + */ + @Deprecated + boolean addSonictronSound(ItemStack aItemStack, String aSoundName); + + @Deprecated + boolean addChemicalBathRecipe(ItemStack aInput, FluidStack aBathingFluid, ItemStack aOutput1, ItemStack aOutput2, + ItemStack aOutput3, FluidStack aFluidOutput, int[] aChances, int aDuration, int aEUt); + + /** + * Add a Nano Forge Recipe. The Nano Forge's main use is to make nanites/nanorobots. Tier 1 Nano Forge - Can make + * partly biological, partly metal nanites TIer 2 Nano Forge - Can make mostly metal nanites with some biological + * aspects TIer 3 Nano Forge - Can make nanites entierly out of metal + * + * @param aInputs must not be null + * @param aFluidInputs can be null + * @param aOutputs must not be null, the nanite or other output + * @param aFluidOutputs can be null + * @param aChances can be null + * @param aDuration recipe duration + * @param aEUt recipe EU/t expenditure + * @param aSpecialValue defines the tier of nano forge required. + * + */ + @Deprecated + boolean addNanoForgeRecipe(ItemStack[] aInputs, FluidStack[] aFluidInputs, ItemStack[] aOutputs, + FluidStack[] aFluidOutputs, int[] aChances, int aDuration, int aEUt, int aSpecialValue); + + /** + * Add a breeder cell. + * + * @param input raw stack. should be undamaged. + * @param output breed output + * @param heatMultiplier bonus progress per neutron pulse per heat step + * @param heatStep divisor for hull heat + * @param reflector true if also acts as a neutron reflector, false otherwise. + * @param requiredPulses progress required to complete breeding + * @return added fake recipe + */ + GT_Recipe addIC2ReactorBreederCell(ItemStack input, ItemStack output, boolean reflector, int heatStep, + int heatMultiplier, int requiredPulses); + + /** + * Add a fuel cell. + * + * @param input raw stack. should be undamaged. + * @param output depleted stack + * @param aMox true if has mox behavior, false if uranium behavior. + * @param aHeat inherent heat output multiplier of the fuel material. should not add the extra heat from being a + * multi-cell! + * @param aEnergy inherent energy output multiplier of the fuel material. should not add the extra energy from being + * a multi-cell! + * @param aCells cell count + * @return added fake recipe + */ + GT_Recipe addIC2ReactorFuelCell(ItemStack input, ItemStack output, boolean aMox, float aHeat, float aEnergy, + int aCells); + + GT_RecipeBuilder stdBuilder(); +} diff --git a/src/main/java/gregtech/api/interfaces/internal/IIC2TileEntity.java b/src/main/java/gregtech/api/interfaces/internal/IIC2TileEntity.java new file mode 100644 index 0000000000..ce9b4c4282 --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/internal/IIC2TileEntity.java @@ -0,0 +1,14 @@ +package gregtech.api.interfaces.internal; + +import gregtech.api.interfaces.tileentity.IHasWorldObjectAndCoords; +import ic2.api.energy.tile.IEnergySink; +import ic2.api.energy.tile.IEnergySource; +import ic2.api.tile.IEnergyStorage; + +/** + * A simple compound Interface for generic EnergyTileEntities. I don't want to have imports of the IC2-API in my + * main-code + */ +public interface IIC2TileEntity extends IEnergyStorage, IEnergySink, IEnergySource, IHasWorldObjectAndCoords { + // +} diff --git a/src/main/java/gregtech/api/interfaces/internal/IThaumcraftCompat.java b/src/main/java/gregtech/api/interfaces/internal/IThaumcraftCompat.java new file mode 100644 index 0000000000..5d99f83689 --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/internal/IThaumcraftCompat.java @@ -0,0 +1,46 @@ +package gregtech.api.interfaces.internal; + +import java.util.List; + +import net.minecraft.block.Block; +import net.minecraft.enchantment.Enchantment; +import net.minecraft.item.ItemStack; + +import gregtech.api.enums.TC_Aspects; +import gregtech.api.enums.TC_Aspects.TC_AspectStack; + +public interface IThaumcraftCompat { + + int RESEARCH_TYPE_NORMAL = 0, RESEARCH_TYPE_SECONDARY = 1, RESEARCH_TYPE_FREE = 2, RESEARCH_TYPE_HIDDEN = 4, + RESEARCH_TYPE_VIRTUAL = 8, RESEARCH_TYPE_ROUND = 16, RESEARCH_TYPE_SPECIAL = 32, RESEARCH_TYPE_AUTOUNLOCK = 64; + + /** + * The Research Keys of GT + */ + String IRON_TO_STEEL = "GT_IRON_TO_STEEL", FILL_WATER_BUCKET = "GT_FILL_WATER_BUCKET", + WOOD_TO_CHARCOAL = "GT_WOOD_TO_CHARCOAL", TRANSZINC = "GT_TRANSZINC", TRANSNICKEL = "GT_TRANSNICKEL", + TRANSCOBALT = "GT_TRANSCOBALT", TRANSBISMUTH = "GT_TRANSBISMUTH", TRANSANTIMONY = "GT_TRANSANTIMONY", + TRANSCUPRONICKEL = "GT_TRANSCUPRONICKEL", TRANSBATTERYALLOY = "GT_TRANSBATTERYALLOY", + TRANSSOLDERINGALLOY = "GT_TRANSSOLDERINGALLOY", TRANSBRASS = "GT_TRANSBRASS", TRANSBRONZE = "GT_TRANSBRONZE", + TRANSINVAR = "GT_TRANSINVAR", TRANSELECTRUM = "GT_TRANSELECTRUM", TRANSALUMINIUM = "GT_TRANSALUMINIUM", + CRYSTALLISATION = "GT_CRYSTALLISATION", ADVANCEDENTROPICPROCESSING = "GT_ADVANCEDENTROPICPROCESSING", // unused + ADVANCEDMETALLURGY = "GT_ADVANCEDMETALLURGY"; + + boolean registerPortholeBlacklistedBlock(Block aBlock); + + boolean registerThaumcraftAspectsToItem(ItemStack aStack, List<TC_AspectStack> aAspects, boolean aAdditive); + + boolean registerThaumcraftAspectsToItem(ItemStack aStack, List<TC_AspectStack> aAspects, String aOreDict); + + Object addCrucibleRecipe(String aResearch, Object aInput, ItemStack aOutput, List<TC_AspectStack> aAspects); + + Object addInfusionRecipe(String aResearch, ItemStack aMainInput, ItemStack[] aSideInputs, ItemStack aOutput, + int aInstability, List<TC_Aspects.TC_AspectStack> aAspects); + + Object addInfusionEnchantmentRecipe(String aResearch, Enchantment aEnchantment, int aInstability, + List<TC_Aspects.TC_AspectStack> aAspects, ItemStack[] aSideInputs); + + Object addResearch(String aResearch, String aName, String aText, String[] aParentResearches, String aCategory, + ItemStack aIcon, int aComplexity, int aType, int aX, int aY, List<TC_AspectStack> aAspects, + ItemStack[] aResearchTriggers, Object[] aPages); +} diff --git a/src/main/java/gregtech/api/interfaces/internal/IUETileEntity.java b/src/main/java/gregtech/api/interfaces/internal/IUETileEntity.java new file mode 100644 index 0000000000..7c9d487e05 --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/internal/IUETileEntity.java @@ -0,0 +1,5 @@ +package gregtech.api.interfaces.internal; + +public interface IUETileEntity /* extends IElectrical */ { + // +} diff --git a/src/main/java/gregtech/api/interfaces/metatileentity/IConnectable.java b/src/main/java/gregtech/api/interfaces/metatileentity/IConnectable.java new file mode 100644 index 0000000000..0ca519d40b --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/metatileentity/IConnectable.java @@ -0,0 +1,34 @@ +package gregtech.api.interfaces.metatileentity; + +import net.minecraftforge.common.util.ForgeDirection; + +/** + * For pipes, wires, and other MetaTiles which need to be decided whether they should connect to the block at each side. + */ +public interface IConnectable { + + int NO_CONNECTION = 0b00000000; + int CONNECTED_DOWN = 0b00000001; + int CONNECTED_UP = 0b00000010; + int CONNECTED_NORTH = 0b00000100; + int CONNECTED_SOUTH = 0b00001000; + int CONNECTED_WEST = 0b00010000; + int CONNECTED_EAST = 0b00100000; + int CONNECTED_ALL = 0b00111111; + int HAS_FRESHFOAM = 0b01000000; + int HAS_HARDENEDFOAM = 0b10000000; + int HAS_FOAM = 0b11000000; + + /** + * Try to connect to the Block at the specified side returns the connection state. Non-positive values for failed, + * others for succeeded. + */ + int connect(ForgeDirection side); + + /** + * Try to disconnect to the Block at the specified side + */ + void disconnect(ForgeDirection side); + + boolean isConnectedAtSide(ForgeDirection side); +} diff --git a/src/main/java/gregtech/api/interfaces/metatileentity/IFluidLockable.java b/src/main/java/gregtech/api/interfaces/metatileentity/IFluidLockable.java new file mode 100644 index 0000000000..f7f6112680 --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/metatileentity/IFluidLockable.java @@ -0,0 +1,27 @@ +package gregtech.api.interfaces.metatileentity; + +import net.minecraftforge.fluids.Fluid; + +/** + * Implement this interface if your MetaTileEntity supports fluid lock mechanism. + */ +@SuppressWarnings({ "BooleanMethodIsAlwaysInverted" }) +public interface IFluidLockable { + + /** + * Use {@link Fluid#getName()} instead of {@link Fluid#getUnlocalizedName()} for fluid name + */ + void setLockedFluidName(String name); + + String getLockedFluidName(); + + /** + * Set fluid lock state. Would be useful when you don't necessarily want to change mode when locked fluid is + * changed. + */ + void lockFluid(boolean lock); + + boolean isFluidLocked(); + + boolean acceptsFluidLock(String name); +} diff --git a/src/main/java/gregtech/api/interfaces/metatileentity/IMetaTileEntity.java b/src/main/java/gregtech/api/interfaces/metatileentity/IMetaTileEntity.java new file mode 100644 index 0000000000..2c222e76a8 --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/metatileentity/IMetaTileEntity.java @@ -0,0 +1,539 @@ +package gregtech.api.interfaces.metatileentity; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import javax.annotation.Nullable; + +import net.minecraft.block.Block; +import net.minecraft.client.renderer.RenderBlocks; +import net.minecraft.client.renderer.texture.IIconRegister; +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.inventory.IInventory; +import net.minecraft.inventory.ISidedInventory; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.AxisAlignedBB; +import net.minecraft.world.IBlockAccess; +import net.minecraft.world.World; +import net.minecraftforge.common.util.ForgeDirection; +import net.minecraftforge.fluids.IFluidHandler; +import net.minecraftforge.fluids.IFluidTank; + +import com.gtnewhorizons.modularui.api.forge.ItemStackHandler; + +import appeng.api.crafting.ICraftingIconProvider; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import gregtech.api.enums.Dyes; +import gregtech.api.interfaces.ITexture; +import gregtech.api.interfaces.modularui.IGetGUITextureSet; +import gregtech.api.interfaces.tileentity.IGearEnergyTileEntity; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.interfaces.tileentity.IGregtechWailaProvider; +import gregtech.api.interfaces.tileentity.IMachineBlockUpdateable; +import gregtech.api.objects.GT_ItemStack; +import gregtech.api.util.GT_Config; +import gregtech.api.util.GT_Util; + +/** + * Warning, this Interface has just been made to be able to add multiple kinds of MetaTileEntities (Cables, Pipes, + * Transformers, but not the regular Blocks) + * <p/> + * Don't implement this yourself and expect it to work. Extend @MetaTileEntity itself. + */ +public interface IMetaTileEntity extends ISidedInventory, IFluidTank, IFluidHandler, IGearEnergyTileEntity, + IMachineBlockUpdateable, IGregtechWailaProvider, IGetGUITextureSet, ICraftingIconProvider { + + /** + * This determines the BaseMetaTileEntity belonging to this MetaTileEntity by using the Meta ID of the Block itself. + * <p/> + * 0 = BaseMetaTileEntity, Wrench lvl 0 to dismantle 1 = BaseMetaTileEntity, Wrench lvl 1 to dismantle 2 = + * BaseMetaTileEntity, Wrench lvl 2 to dismantle 3 = BaseMetaTileEntity, Wrench lvl 3 to dismantle 4 = + * BaseMetaPipeEntity, Wrench lvl 0 to dismantle 5 = BaseMetaPipeEntity, Wrench lvl 1 to dismantle 6 = + * BaseMetaPipeEntity, Wrench lvl 2 to dismantle 7 = BaseMetaPipeEntity, Wrench lvl 3 to dismantle 8 = + * BaseMetaPipeEntity, Cutter lvl 0 to dismantle 9 = BaseMetaPipeEntity, Cutter lvl 1 to dismantle 10 = + * BaseMetaPipeEntity, Cutter lvl 2 to dismantle 11 = BaseMetaPipeEntity, Cutter lvl 3 to dismantle 12 = GT++ 13 = + * GT++ 14 = GT++ 15 = GT++ + */ + byte getTileEntityBaseType(); + + /** + * @param aTileEntity is just because the internal Variable "mBaseMetaTileEntity" is set after this Call. + * @return a newly created and ready MetaTileEntity + */ + IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity); + + /** + * @return an ItemStack representing this MetaTileEntity. + */ + ItemStack getStackForm(long aAmount); + + /** + * new getter for the BaseMetaTileEntity, which restricts usage to certain Functions. + */ + IGregTechTileEntity getBaseMetaTileEntity(); + + /** + * Sets the BaseMetaTileEntity of this + */ + void setBaseMetaTileEntity(IGregTechTileEntity aBaseMetaTileEntity); + + /** + * when placing a Machine in World, to initialize default Modes. aNBT can be null! + */ + void initDefaultModes(NBTTagCompound aNBT); + + /** + * ^= writeToNBT + */ + void saveNBTData(NBTTagCompound aNBT); + + /** + * ^= readFromNBT + */ + void loadNBTData(NBTTagCompound aNBT); + + /** + * Adds the NBT-Information to the ItemStack, when being dismanteled properly Used to store Machine specific Upgrade + * Data. + */ + void setItemNBT(NBTTagCompound aNBT); + + /** + * Called in the registered MetaTileEntity when the Server starts, to reset static variables + */ + void onServerStart(); + + /** + * Called in the registered MetaTileEntity when the Server ticks a World the first time, to load things from the + * World Save + */ + void onWorldLoad(File aSaveDirectory); + + /** + * Called in the registered MetaTileEntity when the Server stops, to save the Game. + */ + void onWorldSave(File aSaveDirectory); + + /** + * Called to set Configuration values for this MetaTileEntity. Use aConfig.get(ConfigCategories.machineconfig, + * "MetaTileEntityName.Ability", DEFAULT_VALUE); to set the Values. + */ + void onConfigLoad(GT_Config aConfig); + + /** + * If a Cover of that Type can be placed on this Side. Also Called when the Facing of the Block Changes and a Cover + * is on said Side. + */ + boolean allowCoverOnSide(ForgeDirection side, GT_ItemStack aStack); + + /** + * When a Player right-clicks the Facing with a Screwdriver. + */ + void onScrewdriverRightClick(ForgeDirection side, EntityPlayer aPlayer, float aX, float aY, float aZ, + ItemStack aTool); + + /** + * When a Player right-clicks the Facing with a Wrench. + */ + boolean onWrenchRightClick(ForgeDirection side, ForgeDirection wrenchingSide, EntityPlayer entityPlayer, float aX, + float aY, float aZ, ItemStack aTool); + + /** + * When a Player right-clicks the Facing with a wire cutter. + */ + boolean onWireCutterRightClick(ForgeDirection side, ForgeDirection wrenchingSide, EntityPlayer entityPlayer, + float aX, float aY, float aZ, ItemStack aTool); + + /** + * When a Player right-clicks the Facing with a soldering iron. + */ + boolean onSolderingToolRightClick(ForgeDirection side, ForgeDirection wrenchingSide, EntityPlayer entityPlayer, + float aX, float aY, float aZ, ItemStack aTool); + + /** + * Called right before this Machine explodes + */ + void onExplosion(); + + /** + * The First processed Tick which was passed to this MetaTileEntity + */ + void onFirstTick(IGregTechTileEntity aBaseMetaTileEntity); + + /** + * The Tick before all the generic handling happens, what gives a slightly faster reaction speed. Don't use this if + * you really don't need to. @onPostTick is better suited for ticks. This happens still after the Cover handling. + */ + void onPreTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick); + + /** + * The Tick after all the generic handling happened. Recommended to use this like updateEntity. + */ + void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick); + + /** + * Called when this MetaTileEntity gets (intentionally) disconnected from the BaseMetaTileEntity. Doesn't get called + * when this thing is moved by Frames or similar hacks. + */ + void inValidate(); + + /** + * Called when the BaseMetaTileEntity gets invalidated, what happens right before the @inValidate above gets called + */ + void onRemoval(); + + /** + * Called when the BaseMetaTileEntity gets unloaded (chunk or world) + */ + default void onUnload() {} + + /** + * @param facing the facing direction to check + * @return if aFacing would be a valid Facing for this Device. Used for wrenching. + */ + boolean isFacingValid(ForgeDirection facing); + + /** + * @return the Server Side Container + * @deprecated Use ModularUI + */ + @Deprecated + default Object getServerGUI(int aID, InventoryPlayer aPlayerInventory, IGregTechTileEntity aBaseMetaTileEntity) { + throw new UnsupportedOperationException(); + } + + /** + * @return the Client Side GUI Container + * @deprecated Use ModularUI + */ + @Deprecated + default Object getClientGUI(int aID, InventoryPlayer aPlayerInventory, IGregTechTileEntity aBaseMetaTileEntity) { + throw new UnsupportedOperationException(); + } + + /** + * For back compatibility, you need to override this if this MetaTileEntity uses ModularUI. + */ + default boolean useModularUI() { + return false; + } + + /** + * From new ISidedInventory + */ + boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side, ItemStack aStack); + + /** + * From new ISidedInventory + */ + boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side, ItemStack aStack); + + /** + * @return if aIndex is a valid Slot. false for things like HoloSlots. Is used for determining if an Item is dropped + * upon Block destruction and for Inventory Access Management + */ + boolean isValidSlot(int aIndex); + + /** + * Check if the item at the specified index should be dropped + * + * @param index Index that will be checked + * @return True if the item at the index should be dropped, else false + */ + boolean shouldDropItemAt(int index); + + /** + * @return if aIndex can be set to Zero stackSize, when being removed. + */ + boolean setStackToZeroInsteadOfNull(int aIndex); + + /** + * If this Side can connect to inputting pipes + */ + boolean isLiquidInput(ForgeDirection side); + + /** + * If this Side can connect to outputting pipes + */ + boolean isLiquidOutput(ForgeDirection side); + + /** + * Just an Accessor for the Name variable. + */ + String getMetaName(); + + /** + * @return true if the Machine can be accessed + */ + boolean isAccessAllowed(EntityPlayer aPlayer); + + /** + * a Player right-clicks the Machine Sneaky right clicks are not getting passed to this! + * + * @return mostly {@code false}. Probably is left for compatibility. + */ + boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer, ForgeDirection side, float aX, + float aY, float aZ); + + /** + * a Player leftclicks the Machine Sneaky leftclicks are getting passed to this unlike with the rightclicks. + */ + void onLeftclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer); + + /** + * Called Clientside with the Data got from @getUpdateData + */ + void onValueUpdate(byte aValue); + + /** + * return a small bit of Data, like a secondary Facing for example with this Function, for the Client. The + * BaseMetaTileEntity detects changes to this Value and will then send an Update. This is only for Information, + * which is visible as Texture to the outside. + * <p/> + * If you just want to have an Active/Redstone State then set the Active State inside the BaseMetaTileEntity + * instead. + */ + byte getUpdateData(); + + /** + * For the rare case you need this Function + */ + void receiveClientEvent(byte aEventID, byte aValue); + + /** + * Called to actually play the sound on client side. Client/Server check is already done. + */ + void doSound(byte aIndex, double aX, double aY, double aZ); + + void startSoundLoop(byte aIndex, double aX, double aY, double aZ); + + void stopSoundLoop(byte aValue, double aX, double aY, double aZ); + + /** + * Sends the Event for the Sound Triggers, only usable Server Side! + */ + void sendSound(byte aIndex); + + /** + * Sends the Event for the Sound Triggers, only usable Server Side! + */ + void sendLoopStart(byte aIndex); + + /** + * Sends the Event for the Sound Triggers, only usable Server Side! + */ + void sendLoopEnd(byte aIndex); + + /** + * Called when the Machine explodes. Override the Explosion code here. + * + * @param aExplosionPower explosion power + */ + void doExplosion(long aExplosionPower); + + /** + * If this is just a simple Machine, which can be wrenched at 100% + */ + boolean isSimpleMachine(); + + /** + * If there should be a Lag Warning if something laggy happens during this Tick. + * <p/> + * The Advanced Pump uses this to not cause the Lag Message, while it scans for all close Fluids. The Item Pipes and + * Retrievers neither send this Message, when scanning for Pipes. + */ + boolean doTickProfilingMessageDuringThisTick(); + + /** + * returns the DebugLog + */ + ArrayList<String> getSpecialDebugInfo(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer, int aLogLevel, + ArrayList<String> aList); + + /** + * get a small Description + */ + String[] getDescription(); + + /** + * In case the Output Voltage varies. + */ + String getSpecialVoltageToolTip(); + + /** + * Icon of the Texture. If this returns null then it falls back to getTextureIndex. + * + * @param side is the Side of the Block + * @param facing is the direction the Block is facing + * @param colorIndex The Minecraft Color the Block is having + * @param active if the Machine is currently active (use this instead of calling + * {@code mBaseMetaTileEntity.mActive)}. Note: In case of Pipes this means if this Side is + * connected to something or not. + * @param redstoneLevel if the Machine is currently outputting a RedstoneSignal (use this instead of calling + * {@code mBaseMetaTileEntity.mRedstone} !!!) + */ + ITexture[] getTexture(IGregTechTileEntity baseMetaTileEntity, ForgeDirection side, ForgeDirection facing, + int colorIndex, boolean active, boolean redstoneLevel); + + /** + * Register Icons here. This gets called when the Icons get initialized by the Base Block Best is you put your Icons + * in a static Array for quick and easy access without relying on the MetaTileList. + * + * @param aBlockIconRegister The Block Icon Register + */ + @SideOnly(Side.CLIENT) + void registerIcons(IIconRegister aBlockIconRegister); + + /** + * @return true if you override the Rendering. + */ + @SideOnly(Side.CLIENT) + boolean renderInInventory(Block aBlock, int aMeta, RenderBlocks aRenderer); + + /** + * @return true if you override the Rendering. + */ + @SideOnly(Side.CLIENT) + boolean renderInWorld(IBlockAccess aWorld, int aX, int aY, int aZ, Block aBlock, RenderBlocks aRenderer); + + /** + * Gets the Output for the comparator on the given Side + */ + byte getComparatorValue(ForgeDirection side); + + float getExplosionResistance(ForgeDirection side); + + String[] getInfoData(); + + boolean isGivingInformation(); + + ItemStack[] getRealInventory(); + + boolean connectsToItemPipe(ForgeDirection side); + + void onColorChangeServer(byte aColor); + + void onColorChangeClient(byte aColor); + + /** + * @return Actual color shown on GUI + */ + default int getGUIColorization() { + if (getBaseMetaTileEntity() != null) { + return getBaseMetaTileEntity().getGUIColorization(); + } else { + return GT_Util.getRGBInt(Dyes.MACHINE_METAL.getRGBA()); + } + } + + int getLightOpacity(); + + boolean allowGeneralRedstoneOutput(); + + void addCollisionBoxesToList(World aWorld, int aX, int aY, int aZ, AxisAlignedBB inputAABB, + List<AxisAlignedBB> outputAABB, Entity collider); + + AxisAlignedBB getCollisionBoundingBoxFromPool(World aWorld, int aX, int aY, int aZ); + + void onEntityCollidedWithBlock(World aWorld, int aX, int aY, int aZ, Entity collider); + + /** + * The onCreated Function of the Item Class redirects here + */ + void onCreated(ItemStack aStack, World aWorld, EntityPlayer aPlayer); + + boolean hasAlternativeModeText(); + + String getAlternativeModeText(); + + boolean shouldJoinIc2Enet(); + + /** + * The Machine Update, which is called when the Machine needs an Update of its Parts. I suggest to wait 1-5 seconds + * before actually checking the Machine Parts. RP-Frames could for example cause Problems when you instacheck the + * Machine Parts. + * <p> + * just do stuff since we are already in meta tile... + */ + @Override + void onMachineBlockUpdate(); + + /** + * just return in should recurse since we are already in meta tile... + */ + @Override + default boolean isMachineBlockUpdateRecursive() { + return true; + } + + /** + * A randomly called display update to be able to add particles or other items for display + * + * @param aBaseMetaTileEntity The entity that will handle the {@see Block#randomDisplayTick} + */ + @SideOnly(Side.CLIENT) + default void onRandomDisplayTick(IGregTechTileEntity aBaseMetaTileEntity) { + /* do nothing */ + } + + default int getGUIWidth() { + return 176; + } + + default int getGUIHeight() { + return 166; + } + + /* + * ModularUI Support + */ + default ItemStackHandler getInventoryHandler() { + return null; + } + + default String getLocalName() { + return "Unknown"; + } + + default boolean doesBindPlayerInventory() { + return true; + } + + default int getTextColorOrDefault(String textType, int defaultColor) { + return defaultColor; + } + + /** + * Called before block is destroyed. This is before inventory dropping code has executed. + */ + default void onBlockDestroyed() {} + + /** + * Allows to add additional data to the tooltip, which is specific to an instance of the machine + * + * @param stack Item stack of this MTE + * @param tooltip Tooltip to which can be added + */ + default void addAdditionalTooltipInformation(ItemStack stack, List<String> tooltip) {} + + @Override + default ItemStack getMachineCraftingIcon() { + return null; + } + + /** + * Gets items to be displayed for HoloInventory mod. + * + * @return null if default implementation should be used, i.e. {@link IInventory#getStackInSlot}. + * Otherwise, a list of items to be displayed. Null element may be contained. + */ + @Nullable + default List<ItemStack> getItemsForHoloGlasses() { + return null; + } +} diff --git a/src/main/java/gregtech/api/interfaces/metatileentity/IMetaTileEntityCable.java b/src/main/java/gregtech/api/interfaces/metatileentity/IMetaTileEntityCable.java new file mode 100644 index 0000000000..a2d672e765 --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/metatileentity/IMetaTileEntityCable.java @@ -0,0 +1,19 @@ +package gregtech.api.interfaces.metatileentity; + +import java.util.ArrayList; +import java.util.HashSet; + +import net.minecraft.tileentity.TileEntity; +import net.minecraftforge.common.util.ForgeDirection; + +public interface IMetaTileEntityCable extends IMetaTileEntityPipe { + + @Deprecated + long transferElectricity(ForgeDirection side, long aVoltage, long aAmperage, + ArrayList<TileEntity> aAlreadyPassedTileEntityList); + + default long transferElectricity(ForgeDirection side, long aVoltage, long aAmperage, + HashSet<TileEntity> aAlreadyPassedSet) { + return transferElectricity(side, aVoltage, aAmperage, new ArrayList<>(aAlreadyPassedSet)); + } +} diff --git a/src/main/java/gregtech/api/interfaces/metatileentity/IMetaTileEntityItemPipe.java b/src/main/java/gregtech/api/interfaces/metatileentity/IMetaTileEntityItemPipe.java new file mode 100644 index 0000000000..5daf895c20 --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/metatileentity/IMetaTileEntityItemPipe.java @@ -0,0 +1,123 @@ +package gregtech.api.interfaces.metatileentity; + +import java.util.Map; + +import net.minecraftforge.common.util.ForgeDirection; + +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.metatileentity.BaseMetaPipeEntity; + +public interface IMetaTileEntityItemPipe extends IMetaTileEntityPipe { + + /** + * @return if this Pipe can still be used. + */ + boolean pipeCapacityCheck(); + + /** + * @return if this Pipe can still be used. + */ + boolean incrementTransferCounter(int aIncrement); + + /** + * Sends an ItemStack from aSender to the adjacent Blocks. + * + * @param aSender the BaseMetaTileEntity sending the Stack. + * @return if it was able to send something + */ + boolean sendItemStack(Object aSender); + + /** + * Executes the Sending Code for inserting Stacks into the TileEntities. + * + * @param aSender the BaseMetaTileEntity sending the Stack. + * @param side the Side of the PIPE facing the TileEntity. + * @return if this Side was allowed to Output into the Block. + */ + boolean insertItemStackIntoTileEntity(Object aSender, ForgeDirection side); + + /** + * Can be used to make flow control Pipes, like Redpowers Restriction Tubes. Every normal Pipe returns a Value of + * 32768, so you can easily insert lower Numbers to set Routing priorities. Negative Numbers to "suck" Items into a + * certain direction are also possible. + */ + int getStepSize(); + + /** + * Utility for the Item Network + */ + class Util { + + /** + * @return connected Item Pipes + */ + public static Map<IMetaTileEntityItemPipe, Long> scanPipes(IMetaTileEntityItemPipe aMetaTileEntity, + Map<IMetaTileEntityItemPipe, Long> aMap, long aStep, boolean aSuckItems, boolean aIgnoreCapacity) { + aStep += aMetaTileEntity.getStepSize(); + if (aIgnoreCapacity || aMetaTileEntity.pipeCapacityCheck()) + if (aMap.get(aMetaTileEntity) == null || aMap.get(aMetaTileEntity) > aStep) { + final IGregTechTileEntity aBaseMetaTileEntity = aMetaTileEntity.getBaseMetaTileEntity(); + aMap.put(aMetaTileEntity, aStep); + for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) { + if (aMetaTileEntity instanceof IConnectable + && !((IConnectable) aMetaTileEntity).isConnectedAtSide(side)) continue; + final ForgeDirection oppositeSide = side.getOpposite(); + if (aSuckItems) { + if (aBaseMetaTileEntity.getCoverInfoAtSide(side) + .letsItemsIn(-2)) { + final IGregTechTileEntity tItemPipe = aBaseMetaTileEntity + .getIGregTechTileEntityAtSide(side); + if (aBaseMetaTileEntity.getColorization() >= 0) { + final byte tColor = tItemPipe.getColorization(); + if (tColor >= 0 && tColor != aBaseMetaTileEntity.getColorization()) { + continue; + } + } + if (tItemPipe instanceof BaseMetaPipeEntity) { + final IMetaTileEntity tMetaTileEntity = tItemPipe.getMetaTileEntity(); + if (tMetaTileEntity instanceof IMetaTileEntityItemPipe + && tItemPipe.getCoverInfoAtSide(oppositeSide) + .letsItemsOut(-2)) { + scanPipes( + (IMetaTileEntityItemPipe) tMetaTileEntity, + aMap, + aStep, + aSuckItems, + aIgnoreCapacity); + } + } + } + } else { + if (aBaseMetaTileEntity.getCoverInfoAtSide(side) + .letsItemsOut(-2)) { + final IGregTechTileEntity tItemPipe = aBaseMetaTileEntity + .getIGregTechTileEntityAtSide(side); + if (tItemPipe != null) { + if (aBaseMetaTileEntity.getColorization() >= 0) { + final byte tColor = tItemPipe.getColorization(); + if (tColor >= 0 && tColor != aBaseMetaTileEntity.getColorization()) { + continue; + } + } + if (tItemPipe instanceof BaseMetaPipeEntity) { + final IMetaTileEntity tMetaTileEntity = tItemPipe.getMetaTileEntity(); + if (tMetaTileEntity instanceof IMetaTileEntityItemPipe + && tItemPipe.getCoverInfoAtSide(oppositeSide) + .letsItemsIn(-2)) { + scanPipes( + (IMetaTileEntityItemPipe) tMetaTileEntity, + aMap, + aStep, + aSuckItems, + aIgnoreCapacity); + } + } + } + } + } + } + } + return aMap; + } + } +} diff --git a/src/main/java/gregtech/api/interfaces/metatileentity/IMetaTileEntityPipe.java b/src/main/java/gregtech/api/interfaces/metatileentity/IMetaTileEntityPipe.java new file mode 100644 index 0000000000..0ac29b2e45 --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/metatileentity/IMetaTileEntityPipe.java @@ -0,0 +1,24 @@ +package gregtech.api.interfaces.metatileentity; + +import net.minecraftforge.common.util.ForgeDirection; + +import gregtech.api.interfaces.ITexture; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; + +public interface IMetaTileEntityPipe extends IMetaTileEntity { + + /** + * Icon of the Texture. If this returns null then it falls back to getTextureIndex. + * + * @param side is the Side of the Block + * @param facingBitMask is the Bitmask of all Connections + * @param colorIndex The Minecraft Color the Block is having + * @param active if the Machine is currently active (use this instead of calling + * mBaseMetaTileEntity.mActive!!!). Note: In case of Pipes this means if this Side is connected + * to something or not. + * @param redstoneLevel if the Machine is currently outputting a RedstoneSignal (use this instead of calling + * mBaseMetaTileEntity.mRedstone!!!) + */ + ITexture[] getTexture(IGregTechTileEntity baseMetaTileEntity, ForgeDirection side, int facingBitMask, + int colorIndex, boolean active, boolean redstoneLevel); +} diff --git a/src/main/java/gregtech/api/interfaces/metatileentity/IMetricsExporter.java b/src/main/java/gregtech/api/interfaces/metatileentity/IMetricsExporter.java new file mode 100644 index 0000000000..f97cd79ed6 --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/metatileentity/IMetricsExporter.java @@ -0,0 +1,28 @@ +package gregtech.api.interfaces.metatileentity; + +import java.util.List; + +import org.jetbrains.annotations.NotNull; + +import gregtech.api.metatileentity.BaseMetaTileEntity; + +/** + * Metrics Transmitter covers look for this interface for retrieving custom metrics for a machine. If this interface is + * not present on the machine housing the cover, it will use {@link BaseMetaTileEntity#getInfoData()} to retrieve info + * instead. + */ +public interface IMetricsExporter { + + /** + * Attached metrics covers will call this method to receive reported metrics. + * <p> + * When reporting metrics, try to keep the number of entries small, and ordering of entries consistent. Advanced + * Sensor Cards allow the user to selectively turn off individual lines using the panel's UI, and doing so is + * aggravated by a metrics set that is inconsistent and/or large. + * + * @return A list of strings to print on the information panel the advanced sensor card is utilizing. Each item in + * the list will be printed on its own line. + */ + @NotNull + List<String> reportMetrics(); +} diff --git a/src/main/java/gregtech/api/interfaces/modularui/ControllerWithOptionalFeatures.java b/src/main/java/gregtech/api/interfaces/modularui/ControllerWithOptionalFeatures.java new file mode 100644 index 0000000000..22694cdafd --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/modularui/ControllerWithOptionalFeatures.java @@ -0,0 +1,282 @@ +package gregtech.api.interfaces.modularui; + +import static gregtech.api.metatileentity.BaseTileEntity.BUTTON_FORBIDDEN_TOOLTIP; +import static gregtech.api.metatileentity.BaseTileEntity.TOOLTIP_DELAY; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Set; + +import net.minecraft.util.StatCollector; + +import com.gtnewhorizons.modularui.api.drawable.IDrawable; +import com.gtnewhorizons.modularui.api.drawable.UITexture; +import com.gtnewhorizons.modularui.api.math.Pos2d; +import com.gtnewhorizons.modularui.api.widget.IWidgetBuilder; +import com.gtnewhorizons.modularui.api.widget.Widget; +import com.gtnewhorizons.modularui.common.widget.ButtonWidget; +import com.gtnewhorizons.modularui.common.widget.FakeSyncWidget; + +import gregtech.api.enums.SoundResource; +import gregtech.api.enums.VoidingMode; +import gregtech.api.gui.modularui.GT_UITextures; +import gregtech.api.interfaces.tileentity.IRecipeLockable; +import gregtech.api.interfaces.tileentity.IVoidable; + +/** + * Machines implementing this interface can have logic and GUI buttons + * to configure various behaviors regarding multiblock. + * <ul> + * <li>Power switch</li> + * <li>Void protection</li> + * <li>Separated input buses</li> + * <li>Batch mode</li> + * <li>Recipe locking</li> + * </ul> + */ +public interface ControllerWithOptionalFeatures extends IVoidable, IRecipeLockable { + + boolean isAllowedToWork(); + + void disableWorking(); + + void enableWorking(); + + Pos2d getPowerSwitchButtonPos(); + + default ButtonWidget createPowerSwitchButton(IWidgetBuilder<?> builder) { + Widget button = new ButtonWidget().setOnClick((clickData, widget) -> { + if (isAllowedToWork()) { + disableWorking(); + } else { + enableWorking(); + } + }) + .setPlayClickSoundResource( + () -> isAllowedToWork() ? SoundResource.GUI_BUTTON_UP.resourceLocation + : SoundResource.GUI_BUTTON_DOWN.resourceLocation) + .setBackground(() -> { + if (isAllowedToWork()) { + return new IDrawable[] { GT_UITextures.BUTTON_STANDARD_PRESSED, + GT_UITextures.OVERLAY_BUTTON_POWER_SWITCH_ON }; + } else { + return new IDrawable[] { GT_UITextures.BUTTON_STANDARD, + GT_UITextures.OVERLAY_BUTTON_POWER_SWITCH_OFF }; + } + }) + .attachSyncer(new FakeSyncWidget.BooleanSyncer(this::isAllowedToWork, val -> { + if (val) enableWorking(); + else disableWorking(); + }), builder) + .addTooltip(StatCollector.translateToLocal("GT5U.gui.button.power_switch")) + .setTooltipShowUpDelay(TOOLTIP_DELAY) + .setPos(getPowerSwitchButtonPos()) + .setSize(16, 16); + return (ButtonWidget) button; + } + + Pos2d getVoidingModeButtonPos(); + + default ButtonWidget createVoidExcessButton(IWidgetBuilder<?> builder) { + Widget button = new ButtonWidget().setOnClick((clickData, widget) -> { + if (supportsVoidProtection()) { + Set<VoidingMode> allowed = getAllowedVoidingModes(); + switch (clickData.mouseButton) { + case 0 -> setVoidingMode(getVoidingMode().nextInCollection(allowed)); + case 1 -> setVoidingMode(getVoidingMode().previousInCollection(allowed)); + } + widget.notifyTooltipChange(); + } + }) + .setPlayClickSound(supportsVoidProtection()) + .setBackground(() -> { + List<UITexture> ret = new ArrayList<>(); + ret.add(getVoidingMode().buttonTexture); + ret.add(getVoidingMode().buttonOverlay); + if (!supportsVoidProtection()) { + ret.add(GT_UITextures.OVERLAY_BUTTON_FORBIDDEN); + } + return ret.toArray(new IDrawable[0]); + }) + .attachSyncer( + new FakeSyncWidget.IntegerSyncer( + () -> getVoidingMode().ordinal(), + val -> setVoidingMode(VoidingMode.fromOrdinal(val))), + builder) + .dynamicTooltip( + () -> Arrays.asList( + StatCollector.translateToLocal("GT5U.gui.button.voiding_mode"), + StatCollector.translateToLocal(getVoidingMode().getTransKey()))) + .setTooltipShowUpDelay(TOOLTIP_DELAY) + .setPos(getVoidingModeButtonPos()) + .setSize(16, 16); + if (!supportsVoidProtection()) { + button.addTooltip(StatCollector.translateToLocal(BUTTON_FORBIDDEN_TOOLTIP)); + } + return (ButtonWidget) button; + } + + /** + * @return if the multi supports input separation. + */ + boolean supportsInputSeparation(); + + /** + * @return true if input separation is enabled, else false. This is getter is used for displaying the icon in the + * GUI + */ + boolean isInputSeparationEnabled(); + + void setInputSeparation(boolean enabled); + + default boolean getDefaultInputSeparationMode() { + return supportsInputSeparation(); + } + + Pos2d getInputSeparationButtonPos(); + + default ButtonWidget createInputSeparationButton(IWidgetBuilder<?> builder) { + Widget button = new ButtonWidget().setOnClick((clickData, widget) -> { + if (supportsInputSeparation()) { + setInputSeparation(!isInputSeparationEnabled()); + } + }) + .setPlayClickSound(supportsInputSeparation()) + .setBackground(() -> { + List<UITexture> ret = new ArrayList<>(); + if (isInputSeparationEnabled()) { + ret.add(GT_UITextures.BUTTON_STANDARD_PRESSED); + if (supportsInputSeparation()) { + ret.add(GT_UITextures.OVERLAY_BUTTON_INPUT_SEPARATION_ON); + } else { + ret.add(GT_UITextures.OVERLAY_BUTTON_INPUT_SEPARATION_ON_DISABLED); + } + } else { + ret.add(GT_UITextures.BUTTON_STANDARD); + if (supportsInputSeparation()) { + ret.add(GT_UITextures.OVERLAY_BUTTON_INPUT_SEPARATION_OFF); + } else { + ret.add(GT_UITextures.OVERLAY_BUTTON_INPUT_SEPARATION_OFF_DISABLED); + } + } + if (!supportsInputSeparation()) { + ret.add(GT_UITextures.OVERLAY_BUTTON_FORBIDDEN); + } + return ret.toArray(new IDrawable[0]); + }) + .attachSyncer( + new FakeSyncWidget.BooleanSyncer(this::isInputSeparationEnabled, this::setInputSeparation), + builder) + .addTooltip(StatCollector.translateToLocal("GT5U.gui.button.input_separation")) + .setTooltipShowUpDelay(TOOLTIP_DELAY) + .setPos(getInputSeparationButtonPos()) + .setSize(16, 16); + if (!supportsInputSeparation()) { + button.addTooltip(StatCollector.translateToLocal(BUTTON_FORBIDDEN_TOOLTIP)); + } + return (ButtonWidget) button; + } + + /** + * @return if the multi supports batch mode. + */ + boolean supportsBatchMode(); + + /** + * @return true if batch mode is enabled, else false. This is getter is used for displaying the icon in the GUI + */ + boolean isBatchModeEnabled(); + + void setBatchMode(boolean enabled); + + default boolean getDefaultBatchMode() { + return false; + } + + Pos2d getBatchModeButtonPos(); + + default ButtonWidget createBatchModeButton(IWidgetBuilder<?> builder) { + Widget button = new ButtonWidget().setOnClick((clickData, widget) -> { + if (supportsBatchMode()) { + setBatchMode(!isBatchModeEnabled()); + } + }) + .setPlayClickSound(supportsBatchMode()) + .setBackground(() -> { + List<UITexture> ret = new ArrayList<>(); + if (isBatchModeEnabled()) { + ret.add(GT_UITextures.BUTTON_STANDARD_PRESSED); + if (supportsBatchMode()) { + ret.add(GT_UITextures.OVERLAY_BUTTON_BATCH_MODE_ON); + } else { + ret.add(GT_UITextures.OVERLAY_BUTTON_BATCH_MODE_ON_DISABLED); + } + } else { + ret.add(GT_UITextures.BUTTON_STANDARD); + if (supportsBatchMode()) { + ret.add(GT_UITextures.OVERLAY_BUTTON_BATCH_MODE_OFF); + } else { + ret.add(GT_UITextures.OVERLAY_BUTTON_BATCH_MODE_OFF_DISABLED); + } + } + if (!supportsBatchMode()) { + ret.add(GT_UITextures.OVERLAY_BUTTON_FORBIDDEN); + } + return ret.toArray(new IDrawable[0]); + }) + .attachSyncer(new FakeSyncWidget.BooleanSyncer(this::isBatchModeEnabled, this::setBatchMode), builder) + .addTooltip(StatCollector.translateToLocal("GT5U.gui.button.batch_mode")) + .setTooltipShowUpDelay(TOOLTIP_DELAY) + .setPos(getBatchModeButtonPos()) + .setSize(16, 16); + if (!supportsBatchMode()) { + button.addTooltip(StatCollector.translateToLocal(BUTTON_FORBIDDEN_TOOLTIP)); + } + return (ButtonWidget) button; + } + + Pos2d getRecipeLockingButtonPos(); + + default ButtonWidget createLockToSingleRecipeButton(IWidgetBuilder<?> builder) { + Widget button = new ButtonWidget().setOnClick((clickData, widget) -> { + if (supportsSingleRecipeLocking()) { + setRecipeLocking(!isRecipeLockingEnabled()); + } + }) + .setPlayClickSound(supportsSingleRecipeLocking()) + .setBackground(() -> { + List<UITexture> ret = new ArrayList<>(); + if (isRecipeLockingEnabled()) { + ret.add(GT_UITextures.BUTTON_STANDARD_PRESSED); + if (supportsSingleRecipeLocking()) { + ret.add(GT_UITextures.OVERLAY_BUTTON_RECIPE_LOCKED); + } else { + ret.add(GT_UITextures.OVERLAY_BUTTON_RECIPE_LOCKED_DISABLED); + } + } else { + ret.add(GT_UITextures.BUTTON_STANDARD); + if (supportsSingleRecipeLocking()) { + ret.add(GT_UITextures.OVERLAY_BUTTON_RECIPE_UNLOCKED); + } else { + ret.add(GT_UITextures.OVERLAY_BUTTON_RECIPE_UNLOCKED_DISABLED); + } + } + if (!supportsSingleRecipeLocking()) { + ret.add(GT_UITextures.OVERLAY_BUTTON_FORBIDDEN); + } + return ret.toArray(new IDrawable[0]); + }) + .attachSyncer( + new FakeSyncWidget.BooleanSyncer(this::isRecipeLockingEnabled, this::setRecipeLocking), + builder) + .addTooltip(StatCollector.translateToLocal("GT5U.gui.button.lock_recipe")) + .setTooltipShowUpDelay(TOOLTIP_DELAY) + .setPos(getRecipeLockingButtonPos()) + .setSize(16, 16); + if (!supportsSingleRecipeLocking()) { + button.addTooltip(StatCollector.translateToLocal(BUTTON_FORBIDDEN_TOOLTIP)); + } + return (ButtonWidget) button; + } +} diff --git a/src/main/java/gregtech/api/interfaces/modularui/IAddGregtechLogo.java b/src/main/java/gregtech/api/interfaces/modularui/IAddGregtechLogo.java new file mode 100644 index 0000000000..12ac32c143 --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/modularui/IAddGregtechLogo.java @@ -0,0 +1,8 @@ +package gregtech.api.interfaces.modularui; + +import com.gtnewhorizons.modularui.api.screen.ModularWindow; + +public interface IAddGregtechLogo { + + default void addGregTechLogo(ModularWindow.Builder builder) {} +} diff --git a/src/main/java/gregtech/api/interfaces/modularui/IAddInventorySlots.java b/src/main/java/gregtech/api/interfaces/modularui/IAddInventorySlots.java new file mode 100644 index 0000000000..4111848ecb --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/modularui/IAddInventorySlots.java @@ -0,0 +1,15 @@ +package gregtech.api.interfaces.modularui; + +import com.gtnewhorizons.modularui.api.drawable.IDrawable; +import com.gtnewhorizons.modularui.api.screen.ModularWindow; + +public interface IAddInventorySlots { + + default void add1by1Slot(ModularWindow.Builder builder, IDrawable... background) {} + + default void add2by2Slots(ModularWindow.Builder builder, IDrawable... background) {} + + default void add3by3Slots(ModularWindow.Builder builder, IDrawable... background) {} + + default void add4by4Slots(ModularWindow.Builder builder, IDrawable... background) {} +} diff --git a/src/main/java/gregtech/api/interfaces/modularui/IAddUIWidgets.java b/src/main/java/gregtech/api/interfaces/modularui/IAddUIWidgets.java new file mode 100644 index 0000000000..1fa317b17f --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/modularui/IAddUIWidgets.java @@ -0,0 +1,9 @@ +package gregtech.api.interfaces.modularui; + +import com.gtnewhorizons.modularui.api.screen.ModularWindow; +import com.gtnewhorizons.modularui.api.screen.UIBuildContext; + +public interface IAddUIWidgets { + + default void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) {} +} diff --git a/src/main/java/gregtech/api/interfaces/modularui/IBindPlayerInventoryUI.java b/src/main/java/gregtech/api/interfaces/modularui/IBindPlayerInventoryUI.java new file mode 100644 index 0000000000..426a24ad38 --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/modularui/IBindPlayerInventoryUI.java @@ -0,0 +1,9 @@ +package gregtech.api.interfaces.modularui; + +import com.gtnewhorizons.modularui.api.screen.ModularWindow; +import com.gtnewhorizons.modularui.api.screen.UIBuildContext; + +public interface IBindPlayerInventoryUI { + + void bindPlayerInventoryUI(ModularWindow.Builder builder, UIBuildContext buildContext); +} diff --git a/src/main/java/gregtech/api/interfaces/modularui/IGetGUITextureSet.java b/src/main/java/gregtech/api/interfaces/modularui/IGetGUITextureSet.java new file mode 100644 index 0000000000..59aa2723c3 --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/modularui/IGetGUITextureSet.java @@ -0,0 +1,10 @@ +package gregtech.api.interfaces.modularui; + +import gregtech.api.gui.modularui.GUITextureSet; + +public interface IGetGUITextureSet { + + default GUITextureSet getGUITextureSet() { + return null; + } +} diff --git a/src/main/java/gregtech/api/interfaces/modularui/IGetTitleColor.java b/src/main/java/gregtech/api/interfaces/modularui/IGetTitleColor.java new file mode 100644 index 0000000000..76d94ee299 --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/modularui/IGetTitleColor.java @@ -0,0 +1,11 @@ +package gregtech.api.interfaces.modularui; + +import gregtech.api.enums.Dyes; +import gregtech.api.util.GT_Util; + +public interface IGetTitleColor { + + default int getTitleColor() { + return GT_Util.getRGBaInt(Dyes.dyeWhite.getRGBA()); + } +} diff --git a/src/main/java/gregtech/api/interfaces/tileentity/IAllSidedTexturedTileEntity.java b/src/main/java/gregtech/api/interfaces/tileentity/IAllSidedTexturedTileEntity.java new file mode 100644 index 0000000000..1d00aa5d76 --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/tileentity/IAllSidedTexturedTileEntity.java @@ -0,0 +1,24 @@ +package gregtech.api.interfaces.tileentity; + +import net.minecraft.block.Block; +import net.minecraftforge.common.util.ForgeDirection; + +import gregtech.api.interfaces.ITexture; + +/** + * Block which has same texture on all sides + */ +public interface IAllSidedTexturedTileEntity extends ITexturedTileEntity { + + /** + * @return the Textures rendered by the GT Rendering + */ + default ITexture[] getTexture(Block aBlock, ForgeDirection side) { + return getTexture(aBlock); + } + + /** + * @return the Textures rendered by the GT Rendering + */ + ITexture[] getTexture(Block aBlock); +} diff --git a/src/main/java/gregtech/api/interfaces/tileentity/IBasicEnergyContainer.java b/src/main/java/gregtech/api/interfaces/tileentity/IBasicEnergyContainer.java new file mode 100644 index 0000000000..37d62894bb --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/tileentity/IBasicEnergyContainer.java @@ -0,0 +1,110 @@ +package gregtech.api.interfaces.tileentity; + +import net.minecraftforge.common.util.ForgeDirection; + +/** + * Interface for internal Code, which is mainly used for independent Energy conversion. + */ +public interface IBasicEnergyContainer extends IEnergyConnected, IHasWorldObjectAndCoords { + + /** + * Gets if that Amount of Energy is stored inside the Machine. It is used for checking the contained Energy before + * consuming it. If this returns false, it will also give a Message inside the Scanner, that this Machine doesn't + * have enough Energy. + */ + boolean isUniversalEnergyStored(long aEnergyAmount); + + /** + * Gets the stored electric, kinetic or steam Energy (with EU as reference Value) Always returns the largest one. + */ + long getUniversalEnergyStored(); + + /** + * Gets the largest electric, kinetic or steam Energy Capacity (with EU as reference Value) + */ + long getUniversalEnergyCapacity(); + + /** + * Gets the amount of Energy Packets per tick. + */ + long getOutputAmperage(); + + /** + * Gets the Output in EU/p. + */ + long getOutputVoltage(); + + /** + * Gets the amount of Energy Packets per tick. + */ + long getInputAmperage(); + + /** + * Gets the maximum Input in EU/p. + */ + long getInputVoltage(); + + /** + * Decreases the Amount of stored universal Energy. If ignoring too less Energy, then it just sets the Energy to 0 + * and returns false. + */ + boolean decreaseStoredEnergyUnits(long aEnergy, boolean aIgnoreTooLessEnergy); + + /** + * Increases the Amount of stored electric Energy. If ignoring too much Energy, then the Energy Limit is just being + * ignored. + */ + boolean increaseStoredEnergyUnits(long aEnergy, boolean aIgnoreTooMuchEnergy); + + /** + * Drain Energy Call for Electricity. + */ + boolean drainEnergyUnits(ForgeDirection side, long aVoltage, long aAmperage); + + /** + * returns the amount of Electricity, accepted by this Block the last 5 ticks as Average. + */ + long getAverageElectricInput(); + + /** + * returns the amount of Electricity, outputted by this Block the last 5 ticks as Average. + */ + long getAverageElectricOutput(); + + /** + * returns the amount of electricity contained in this Block, in EU units! + */ + long getStoredEU(); + + /** + * returns the amount of electricity containable in this Block, in EU units! + */ + long getEUCapacity(); + + /** + * returns the amount of Steam contained in this Block, in EU units! + */ + default long getStoredSteam() { + return 0; + } + + /** + * returns the amount of Steam containable in this Block, in EU units! + */ + default long getSteamCapacity() { + return 0; + } + + /** + * Increases stored Energy. Energy Base Value is in EU, even though it's Steam! + * + * @param aEnergy The Energy to add to the Machine. + * @param aIgnoreTooMuchEnergy if it shall ignore if it has too much Energy. + * @return if it was successful + * <p/> + * And yes, you can't directly decrease the Steam of a Machine. That is done by decreaseStoredEnergyUnits + */ + default boolean increaseStoredSteam(long aEnergy, boolean aIgnoreTooMuchEnergy) { + return false; + } +} diff --git a/src/main/java/gregtech/api/interfaces/tileentity/IColoredTileEntity.java b/src/main/java/gregtech/api/interfaces/tileentity/IColoredTileEntity.java new file mode 100644 index 0000000000..ec760dd346 --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/tileentity/IColoredTileEntity.java @@ -0,0 +1,27 @@ +package gregtech.api.interfaces.tileentity; + +import gregtech.api.enums.Dyes; +import gregtech.api.util.GT_Util; + +public interface IColoredTileEntity { + + /** + * @return 0 - 15 are Colors, while -1 means uncolored + */ + byte getColorization(); + + /** + * Sets the Color Modulation of the Block + * + * @param aColor the Color you want to set it to. -1 for reset. + */ + byte setColorization(byte aColor); + + /** + * @return Actual color shown on GUI + */ + default int getGUIColorization() { + return GT_Util + .getRGBInt((getColorization() != -1 ? Dyes.get(getColorization()) : Dyes.MACHINE_METAL).getRGBA()); + } +} diff --git a/src/main/java/gregtech/api/interfaces/tileentity/ICoverable.java b/src/main/java/gregtech/api/interfaces/tileentity/ICoverable.java new file mode 100644 index 0000000000..8834678984 --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/tileentity/ICoverable.java @@ -0,0 +1,91 @@ +package gregtech.api.interfaces.tileentity; + +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.item.ItemStack; +import net.minecraftforge.common.util.ForgeDirection; + +import gregtech.api.util.GT_CoverBehavior; +import gregtech.api.util.GT_CoverBehaviorBase; +import gregtech.api.util.ISerializableObject; +import gregtech.common.covers.CoverInfo; + +public interface ICoverable extends IRedstoneTileEntity, IHasInventory, IBasicEnergyContainer { + + boolean canPlaceCoverIDAtSide(ForgeDirection side, int aID); + + boolean canPlaceCoverItemAtSide(ForgeDirection side, ItemStack aCover); + + boolean dropCover(ForgeDirection side, ForgeDirection droppedSide, boolean aForced); + + @Deprecated + void setCoverDataAtSide(ForgeDirection side, int aData); + + default void setCoverDataAtSide(ForgeDirection side, ISerializableObject aData) { + if (aData instanceof ISerializableObject.LegacyCoverData) + setCoverDataAtSide(side, ((ISerializableObject.LegacyCoverData) aData).get()); + } + + void setCoverIdAndDataAtSide(ForgeDirection side, int aId, ISerializableObject aData); + + void setCoverIDAtSide(ForgeDirection side, int aID); + + boolean setCoverIDAtSideNoUpdate(ForgeDirection side, int aID); + + void setCoverItemAtSide(ForgeDirection side, ItemStack aCover); + + @Deprecated + int getCoverDataAtSide(ForgeDirection side); + + default CoverInfo getCoverInfoAtSide(ForgeDirection side) { + return null; + } + + default ISerializableObject getComplexCoverDataAtSide(ForgeDirection side) { + return new ISerializableObject.LegacyCoverData(getCoverDataAtSide(side)); + } + + int getCoverIDAtSide(ForgeDirection side); + + ItemStack getCoverItemAtSide(ForgeDirection side); + + @Deprecated + GT_CoverBehavior getCoverBehaviorAtSide(ForgeDirection side); + + default GT_CoverBehaviorBase<?> getCoverBehaviorAtSideNew(ForgeDirection side) { + return getCoverBehaviorAtSide(side); + } + + /** + * For use by the regular MetaTileEntities. Returns the Cover Manipulated input Redstone. Don't use this if you are + * a Cover Behavior. Only for MetaTileEntities. + */ + byte getInternalInputRedstoneSignal(ForgeDirection side); + + /** + * For use by the regular MetaTileEntities. This makes it not conflict with Cover based Redstone Signals. Don't use + * this if you are a Cover Behavior. Only for MetaTileEntities. + */ + void setInternalOutputRedstoneSignal(ForgeDirection side, byte aStrength); + + /** + * Causes a general Cover Texture update. Sends 6 Integers to Client + causes @issueTextureUpdate() + */ + void issueCoverUpdate(ForgeDirection side); + + /** + * Receiving a packet with cover data. + */ + void receiveCoverData(ForgeDirection coverSide, int coverID, int coverData); + + /** + * Receiving a packet with cover data. + * + * @param coverSide cover side + * @param aPlayer the player who made the change + */ + default void receiveCoverData(ForgeDirection coverSide, int aCoverID, ISerializableObject aCoverData, + EntityPlayerMP aPlayer) { + if (aCoverData instanceof ISerializableObject.LegacyCoverData) + receiveCoverData(coverSide, aCoverID, ((ISerializableObject.LegacyCoverData) aCoverData).get()); + } +} diff --git a/src/main/java/gregtech/api/interfaces/tileentity/IDebugableTileEntity.java b/src/main/java/gregtech/api/interfaces/tileentity/IDebugableTileEntity.java new file mode 100644 index 0000000000..5208944d66 --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/tileentity/IDebugableTileEntity.java @@ -0,0 +1,18 @@ +package gregtech.api.interfaces.tileentity; + +import java.util.ArrayList; + +import net.minecraft.entity.player.EntityPlayer; + +public interface IDebugableTileEntity { + + /** + * Returns a Debug Message, for a generic DebugItem + * + * @param aPlayer the Player, who rightclicked with his Debug Item + * @param aLogLevel the Log Level of the Debug Item. 0 = Obvious 1 = Visible for the regular Scanner 2 = Only + * visible to more advanced Scanners 3 = Debug ONLY + * @return a String-Array containing the DebugInfo, every Index is a separate line (0 = first Line) + */ + ArrayList<String> getDebugInfo(EntityPlayer aPlayer, int aLogLevel); +} diff --git a/src/main/java/gregtech/api/interfaces/tileentity/IDigitalChest.java b/src/main/java/gregtech/api/interfaces/tileentity/IDigitalChest.java new file mode 100644 index 0000000000..9a08e65b78 --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/tileentity/IDigitalChest.java @@ -0,0 +1,33 @@ +package gregtech.api.interfaces.tileentity; + +import net.minecraft.item.ItemStack; + +/** + * You are allowed to include this File in your Download, as i will not change it. + */ +public interface IDigitalChest extends IHasWorldObjectAndCoords { + + /** + * Is this even a TileEntity of a Digital Chest? I need things like this Function for MetaTileEntities, you MUST + * check this!!! Do not assume that it's a Digital Chest or similar Device, when it just implements this Interface. + */ + boolean isDigitalChest(); + + /** + * Gives an Array of Stacks with Size (of all the Data-stored Items) of the correspondent Item kinds (regular + * QChests have only one) Does NOT include the 64 "ready" Items inside the Slots, and neither the 128 Items in the + * overflow Buffer. + */ + ItemStack[] getStoredItemData(); + + /** + * A generic Interface for just setting the amount of contained Items + */ + void setItemCount(int aCount); + + /** + * Gets the maximum Item count for this QChest alike Storage. This applies to the Data-Storage, not for the up to + * 192 buffered Items! + */ + int getMaxItemCount(); +} diff --git a/src/main/java/gregtech/api/interfaces/tileentity/IEnergyConductor.java b/src/main/java/gregtech/api/interfaces/tileentity/IEnergyConductor.java new file mode 100644 index 0000000000..e285c6f0e3 --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/tileentity/IEnergyConductor.java @@ -0,0 +1,41 @@ +package gregtech.api.interfaces.tileentity; + +import gregtech.api.enums.Materials; + +/** + * Informative Class for Cables. Not used for now. + * <p/> + * Not all Data might be reliable. This is just for Information sake. + */ +public interface IEnergyConductor extends IEnergyConnected, IHasWorldObjectAndCoords { + + /** + * @return if this is actually a Cable. (you must check this) + */ + boolean isConductor(); + + /** + * @return the maximum Voltage of the Cable. + */ + long getMaxVoltage(); + + /** + * @return the maximum Amperage of the Cable, per Wire. + */ + long getMaxAmperage(); + + /** + * @return the Loss of the Cable, per Meter. + */ + long getLossPerMeter(); + + /** + * @return the Material the Cable consists of. (may return Materials._NULL) + */ + Materials getCableMaterial(); + + /** + * @return the Material the Cable Insulation consists of. (may return Materials._NULL) + */ + Materials getInsulationMaterial(); +} diff --git a/src/main/java/gregtech/api/interfaces/tileentity/IEnergyConnected.java b/src/main/java/gregtech/api/interfaces/tileentity/IEnergyConnected.java new file mode 100644 index 0000000000..91a9759e47 --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/tileentity/IEnergyConnected.java @@ -0,0 +1,178 @@ +package gregtech.api.interfaces.tileentity; + +import java.util.Objects; + +import javax.annotation.Nonnull; + +import net.minecraft.tileentity.TileEntity; +import net.minecraftforge.common.util.ForgeDirection; + +import cofh.api.energy.IEnergyReceiver; +import gregtech.api.GregTech_API; +import gregtech.api.logic.PowerLogic; +import gregtech.api.logic.interfaces.PowerLogicHost; +import gregtech.api.util.GT_Utility; +import ic2.api.energy.tile.IEnergySink; + +/** + * Interface for getting Connected to the GregTech Energy Network. + * <p/> + * This is all you need to connect to the GT Network. IColoredTileEntity is needed for not connecting differently + * coloured Blocks to each other. IHasWorldObjectAndCoords is needed for the InWorld related Stuff. @BaseTileEntity does + * implement most of that Interface. + */ +public interface IEnergyConnected extends IColoredTileEntity { + + /** + * Inject Energy Call for Electricity. Gets called by EnergyEmitters to inject Energy into your Block + * <p/> + * Note: you have to check for @inputEnergyFrom because the Network won't check for that by itself. + * + * @param side 0 - 5 = Vanilla Directions of YOUR Block the Energy gets inserted to. 6 = No specific Side (don't do + * Side checks for this Side) + * @return amount of used Amperes. 0 if not accepted anything. + */ + long injectEnergyUnits(ForgeDirection side, long aVoltage, long aAmperage); + + /** + * Sided Energy Input + */ + boolean inputEnergyFrom(ForgeDirection side); + + default boolean inputEnergyFrom(ForgeDirection side, boolean waitForActive) { + return inputEnergyFrom(side); + } + + /** + * Sided Energy Output + */ + boolean outputsEnergyTo(ForgeDirection side); + + default boolean outputsEnergyTo(ForgeDirection side, boolean waitForActive) { + return outputsEnergyTo(side); + } + + /** + * Utility for the Network + */ + final class Util { + + // TODO: Deduplicate code by rewokring the Enet system using the GTCEu one as inspiration - BlueWeabo + /** + * Emits Energy to the E-net. Also compatible with adjacent IC2 TileEntities. + * + * @return the used Amperage. + */ + public static long emitEnergyToNetwork(long voltage, long amperage, IEnergyConnected emitter) { + long usedAmperes = 0; + if (!(emitter instanceof IHasWorldObjectAndCoords emitterTile)) { + return 0; + } + + for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) { + if (usedAmperes > amperage) break; + if (!emitter.outputsEnergyTo(side)) { + continue; + } + + final ForgeDirection oppositeSide = Objects.requireNonNull(side.getOpposite()); + final TileEntity tTileEntity = emitterTile.getTileEntityAtSide(side); + if (tTileEntity instanceof PowerLogicHost host) { + + final PowerLogic logic = host.getPowerLogic(oppositeSide); + if (logic == null || logic.isEnergyReceiver()) { + continue; + } + + usedAmperes += logic.injectEnergy(voltage, amperage - usedAmperes); + } else if (tTileEntity instanceof IEnergyConnected energyConnected) { + if (emitter.getColorization() >= 0) { + final byte tColor = energyConnected.getColorization(); + if (tColor >= 0 && tColor != emitter.getColorization()) continue; + } + usedAmperes += energyConnected.injectEnergyUnits(oppositeSide, voltage, amperage - usedAmperes); + + } else if (tTileEntity instanceof IEnergySink sink) { + if (sink.acceptsEnergyFrom((TileEntity) emitter, oppositeSide)) { + while (amperage > usedAmperes && sink.getDemandedEnergy() > 0 + && sink.injectEnergy(oppositeSide, voltage, voltage) < voltage) usedAmperes++; + } + } else if (GregTech_API.mOutputRF && tTileEntity instanceof IEnergyReceiver receiver) { + final int rfOut = GT_Utility.safeInt(voltage * GregTech_API.mEUtoRF / 100); + if (receiver.receiveEnergy(oppositeSide, rfOut, true) == rfOut) { + receiver.receiveEnergy(oppositeSide, rfOut, false); + usedAmperes++; + } + } + } + return usedAmperes; + } + + /** + * Same as {@link #emitEnergyToNetwork(long, long, IEnergyConnected)}, but instead we remove the energy directly from the logic itself. + * @param emitter The host, which is trying to emit energy in the network + * @param outputSide side from where energy is being outputted to. If its {@link ForgeDirection#UNKNOWN} then it doesn't emit energy to the network + */ + public static void emitEnergyToNetwork(@Nonnull final PowerLogicHost emitter, @Nonnull final ForgeDirection outputSide) { + if (outputSide == ForgeDirection.UNKNOWN) return; + final PowerLogic emitterLogic = emitter.getPowerLogic(); + long usedAmperes = 0; + long voltage = emitterLogic.getVoltage(); + long amperage = emitterLogic.getMaxAmperage(); + if (!(emitter instanceof final IHasWorldObjectAndCoords emitterTile)) { + return; + } + // We need to make sure we can actually output energy on this side. This is more of a safety check. + if (emitter.getPowerLogic(outputSide) == null) { + return; + } + + final ForgeDirection oppositeSide = Objects.requireNonNull(outputSide.getOpposite()); + final TileEntity tileEntity = emitterTile.getTileEntityAtSide(outputSide); + if (tileEntity instanceof PowerLogicHost host) { + + final PowerLogic logic = host.getPowerLogic(oppositeSide); + if (logic == null || logic.isEnergyReceiver()) { + return; + } + + usedAmperes += logic.injectEnergy(voltage, amperage); + emitterLogic.removeEnergyUnsafe(usedAmperes * voltage); + return; + } + + if (tileEntity instanceof IEnergyConnected energyConnected) { + if (emitter instanceof IColoredTileEntity coloredEmitter && coloredEmitter.getColorization() >= 0) { + final byte tColor = energyConnected.getColorization(); + if (tColor >= 0 && tColor != coloredEmitter.getColorization()) { + return; + } + } + usedAmperes += energyConnected.injectEnergyUnits(oppositeSide, voltage, amperage - usedAmperes); + emitterLogic.removeEnergyUnsafe(usedAmperes * voltage); + return; + } + + if (tileEntity instanceof IEnergySink sink) { + if (sink.acceptsEnergyFrom((TileEntity) emitter, oppositeSide)) { + while (amperage > usedAmperes && sink.getDemandedEnergy() > 0 + && sink.injectEnergy(oppositeSide, voltage, voltage) < voltage) { + usedAmperes++; + } + emitterLogic.removeEnergyUnsafe(usedAmperes * voltage); + return; + } + } + + if (GregTech_API.mOutputRF && tileEntity instanceof IEnergyReceiver receiver) { + final int rfOut = GT_Utility.safeInt(voltage * GregTech_API.mEUtoRF / 100); + if (receiver.receiveEnergy(oppositeSide, rfOut, true) == rfOut) { + receiver.receiveEnergy(oppositeSide, rfOut, false); + usedAmperes++; + emitterLogic.removeEnergyUnsafe(usedAmperes * voltage); + return; + } + } + } + } +} diff --git a/src/main/java/gregtech/api/interfaces/tileentity/IFibreConnected.java b/src/main/java/gregtech/api/interfaces/tileentity/IFibreConnected.java new file mode 100644 index 0000000000..2f7f26e723 --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/tileentity/IFibreConnected.java @@ -0,0 +1,34 @@ +package gregtech.api.interfaces.tileentity; + +import net.minecraftforge.common.util.ForgeDirection; + +/** + * This File has just internal Information about the Fibre Redstone State of a TileEntity + */ +public interface IFibreConnected extends IColoredTileEntity, IHasWorldObjectAndCoords { + + /** + * If this Blocks accepts Fibre from this Side + */ + void inputFibreFrom(ForgeDirection side); + + /** + * If this Blocks emits Fibre to this Side + */ + void outputsFibreTo(ForgeDirection side); + + /** + * Sets the Signal this Blocks outputs to this Fibre Color + */ + void setFibreOutput(ForgeDirection side, byte aColor, byte aRedstoneStrength); + + /** + * Gets the Signal this Blocks outputs to this Fibre Color + */ + byte getFibreOutput(ForgeDirection side, byte aColor); + + /** + * Gets the Signal this Blocks receives from this Fibre Color + */ + byte getFibreInput(ForgeDirection side, byte aColor); +} diff --git a/src/main/java/gregtech/api/interfaces/tileentity/IGTEnet.java b/src/main/java/gregtech/api/interfaces/tileentity/IGTEnet.java new file mode 100644 index 0000000000..77b894fea7 --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/tileentity/IGTEnet.java @@ -0,0 +1,19 @@ +package gregtech.api.interfaces.tileentity; + +public interface IGTEnet { + + /** + * @return true if this Device consumes Energy at all + */ + default boolean isEnetInput() { + return false; + } + + /** + * + * @return true if this Device emits Energy at all + */ + default boolean isEnetOutput() { + return false; + } +} diff --git a/src/main/java/gregtech/api/interfaces/tileentity/IGearEnergyTileEntity.java b/src/main/java/gregtech/api/interfaces/tileentity/IGearEnergyTileEntity.java new file mode 100644 index 0000000000..7eeee90c8c --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/tileentity/IGearEnergyTileEntity.java @@ -0,0 +1,21 @@ +package gregtech.api.interfaces.tileentity; + +import net.minecraftforge.common.util.ForgeDirection; + +public interface IGearEnergyTileEntity { + + /** + * If Rotation Energy can be accepted on this Side. This means that the Gear/Axle will connect to this Side, and can + * cause the Gear/Axle to stop if the Energy isn't accepted. + */ + boolean acceptsRotationalEnergy(ForgeDirection side); + + /** + * Inject Energy Call for Rotational Energy. Rotation Energy can't be stored, this is just for things like internal + * Dynamos, which convert it into Energy, or into Progress. + * + * @param side inject to this side + * @param aSpeed Positive = Clockwise, Negative = Counterclockwise + */ + boolean injectRotationalEnergy(ForgeDirection side, long aSpeed, long aEnergy); +} diff --git a/src/main/java/gregtech/api/interfaces/tileentity/IGregTechDeviceInformation.java b/src/main/java/gregtech/api/interfaces/tileentity/IGregTechDeviceInformation.java new file mode 100644 index 0000000000..001ed44825 --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/tileentity/IGregTechDeviceInformation.java @@ -0,0 +1,22 @@ +package gregtech.api.interfaces.tileentity; + +/** + * You are allowed to include this File in your Download, as i will not change it. + */ +public interface IGregTechDeviceInformation { + + /** + * Is this even a TileEntity which allows GregTech Sensor Kits? I need things like this Function for + * MetaTileEntities, you MUST check this!!! Do not assume that it's a Information returning Device, when it just + * implements this Interface. + */ + boolean isGivingInformation(); + + /** + * Up to 8 Strings can be returned. Note: If you insert "\\\\" in the String it tries to translate seperate Parts of + * the String instead of the String as a whole. + * + * @return an Array of Information Strings. Don't return null! + */ + String[] getInfoData(); +} diff --git a/src/main/java/gregtech/api/interfaces/tileentity/IGregTechTileEntity.java b/src/main/java/gregtech/api/interfaces/tileentity/IGregTechTileEntity.java new file mode 100644 index 0000000000..d8231ee544 --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/tileentity/IGregTechTileEntity.java @@ -0,0 +1,206 @@ +package gregtech.api.interfaces.tileentity; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +import javax.annotation.Nonnull; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.AxisAlignedBB; +import net.minecraft.world.World; +import net.minecraftforge.common.util.ForgeDirection; +import net.minecraftforge.fluids.IFluidHandler; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import gregtech.api.interfaces.IDescribable; +import gregtech.api.interfaces.metatileentity.IMetaTileEntity; +import gregtech.api.interfaces.modularui.IAddInventorySlots; +import gregtech.api.interfaces.modularui.IGetGUITextureSet; +import gregtech.api.util.shutdown.ShutDownReason; +import gregtech.common.blocks.GT_Block_Machines; + +/** + * A simple compound Interface for all my TileEntities. + * <p/> + * Also delivers most of the Information about my TileEntities. + * <p/> + * It can cause Problems to include this Interface! + */ +public interface IGregTechTileEntity extends ITexturedTileEntity, IGearEnergyTileEntity, ICoverable, IFluidHandler, + ITurnable, IGregTechDeviceInformation, IUpgradableMachine, IDigitalChest, IDescribable, IMachineBlockUpdateable, + IGregtechWailaProvider, IGetGUITextureSet, IAddInventorySlots { + + /** + * gets the Error displayed on the GUI + */ + int getErrorDisplayID(); + + /** + * sets the Error displayed on the GUI + */ + void setErrorDisplayID(int aErrorID); + + /** + * @return the MetaID of the Block or the MetaTileEntity ID. + */ + int getMetaTileID(); + + /** + * Internal Usage only! + */ + int setMetaTileID(short aID); + + /** + * @return the MetaTileEntity which is belonging to this, or null if it doesnt has one. + */ + IMetaTileEntity getMetaTileEntity(); + + /** + * Sets the MetaTileEntity. Even though this uses the Universal Interface, certain BaseMetaTileEntities only accept + * one kind of MetaTileEntity so only use this if you are sure its the correct one or you will get a Class cast + * Error. + * + * @param aMetaTileEntity a MetaTileEntity + */ + void setMetaTileEntity(IMetaTileEntity aMetaTileEntity); + + /** + * Causes a general Texture update. + * <p/> + * Only used Client Side to mark Blocks dirty. + */ + void issueTextureUpdate(); + + /** + * Causes the Machine to send its initial Data, like Covers and its ID. + */ + void issueClientUpdate(); + + /** + * causes Explosion. Strength in Overload-EU + */ + void doExplosion(long aExplosionEU); + + /** + * Sets the Block on Fire in all 6 Directions + */ + void setOnFire(); + + /** + * Sets the Block to Fire + */ + void setToFire(); + + /** + * Sets the Owner of the Machine. Returns the set Name. + */ + String setOwnerName(String aName); + + /** + * gets the Name of the Machines Owner or "Player" if not set. + */ + String getOwnerName(); + + /** + * Gets the UniqueID of the Machines Owner. + */ + UUID getOwnerUuid(); + + /** + * Sets the UniqueID of the Machines Owner. + */ + void setOwnerUuid(UUID uuid); + + /** + * Sets initial Values from NBT + * + * @param aNBT is the NBTTag of readFromNBT + * @param aID is the MetaTileEntityID + */ + void setInitialValuesAsNBT(NBTTagCompound aNBT, short aID); + + /** + * Called when leftclicking the TileEntity + */ + void onLeftclick(EntityPlayer aPlayer); + + /** + * Called when rightclicking the TileEntity + */ + boolean onRightclick(EntityPlayer aPlayer, ForgeDirection side, float aX, float aY, float aZ); + + float getBlastResistance(ForgeDirection side); + + default void onBlockDestroyed() {} + + ArrayList<ItemStack> getDrops(); + + /** + * Check if the item at the specific index should be dropped or not + * + * @param index Index that will be checked + * @return True if it should drop, else false + */ + boolean shouldDropItemAt(int index); + + /** + * 255 = 100% + */ + int getLightOpacity(); + + void addCollisionBoxesToList(World aWorld, int aX, int aY, int aZ, AxisAlignedBB inputAABB, + List<AxisAlignedBB> outputAABB, Entity collider); + + AxisAlignedBB getCollisionBoundingBoxFromPool(World aWorld, int aX, int aY, int aZ); + + void onEntityCollidedWithBlock(World aWorld, int aX, int aY, int aZ, Entity collider); + + /** + * Checks validity of meta tile and delegates to it + */ + @Override + default void onMachineBlockUpdate() { + if (!isDead() && getMetaTileEntity() != null && getMetaTileEntity().getBaseMetaTileEntity() == this) { + getMetaTileEntity().onMachineBlockUpdate(); + } + } + + /** + * Checks validity of meta tile and delegates to it + */ + @Override + default boolean isMachineBlockUpdateRecursive() { + return !isDead() && getMetaTileEntity() != null + && getMetaTileEntity().getBaseMetaTileEntity() == this + && getMetaTileEntity().isMachineBlockUpdateRecursive(); + } + + default void setShutdownStatus(boolean newStatus) {} + + default void setShutDownReason(@Nonnull ShutDownReason reason) {} + + /** + * A randomly called display update to be able to add particles or other items for display The event is proxied by + * the {@link GT_Block_Machines#randomDisplayTick} + */ + @SideOnly(Side.CLIENT) + default void onRandomDisplayTick() { + if (getMetaTileEntity() != null && getMetaTileEntity().getBaseMetaTileEntity() == this) { + getMetaTileEntity().onRandomDisplayTick(this); + } + } + + /** + * gets the time statistics used for CPU timing + */ + default int[] getTimeStatistics() { + return null; + } + + default void startTimeStatistics() {} +} diff --git a/src/main/java/gregtech/api/interfaces/tileentity/IGregtechWailaProvider.java b/src/main/java/gregtech/api/interfaces/tileentity/IGregtechWailaProvider.java new file mode 100644 index 0000000000..61566d82fb --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/tileentity/IGregtechWailaProvider.java @@ -0,0 +1,21 @@ +package gregtech.api.interfaces.tileentity; + +import java.util.List; + +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.world.World; + +import mcp.mobius.waila.api.IWailaConfigHandler; +import mcp.mobius.waila.api.IWailaDataAccessor; + +public interface IGregtechWailaProvider { + + default void getWailaBody(ItemStack itemStack, List<String> currenttip, IWailaDataAccessor accessor, + IWailaConfigHandler config) {} + + default void getWailaNBTData(final EntityPlayerMP player, final TileEntity tile, final NBTTagCompound tag, + final World world, int x, int y, int z) {} +} diff --git a/src/main/java/gregtech/api/interfaces/tileentity/IHasInventory.java b/src/main/java/gregtech/api/interfaces/tileentity/IHasInventory.java new file mode 100644 index 0000000000..bee1756217 --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/tileentity/IHasInventory.java @@ -0,0 +1,37 @@ +package gregtech.api.interfaces.tileentity; + +import net.minecraft.inventory.ISidedInventory; +import net.minecraft.item.ItemStack; + +public interface IHasInventory extends ISidedInventory, IHasWorldObjectAndCoords { + + default void markInventoryBeenModified() {} + + /** + * if the Inventory of this TileEntity got modified this tick + */ + boolean hasInventoryBeenModified(); + + /** + * if this is just a Holoslot + */ + boolean isValidSlot(int aIndex); + + /** + * Tries to add a Stack to the Slot. It doesn't matter if the Slot is valid or invalid as described at the Function + * above. + * + * @return true if aStack == null, then false if aIndex is out of bounds, then false if aStack cannot be added, and + * then true if aStack has been added + */ + boolean addStackToSlot(int aIndex, ItemStack aStack); + + /** + * Tries to add X Items of a Stack to the Slot. It doesn't matter if the Slot is valid or invalid as described at + * the Function above. + * + * @return true if aStack == null, then false if aIndex is out of bounds, then false if aStack cannot be added, and + * then true if aStack has been added + */ + boolean addStackToSlot(int aIndex, ItemStack aStack, int aAmount); +} diff --git a/src/main/java/gregtech/api/interfaces/tileentity/IHasWorldObjectAndCoords.java b/src/main/java/gregtech/api/interfaces/tileentity/IHasWorldObjectAndCoords.java new file mode 100644 index 0000000000..6d81d5c401 --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/tileentity/IHasWorldObjectAndCoords.java @@ -0,0 +1,190 @@ +package gregtech.api.interfaces.tileentity; + +import net.minecraft.block.Block; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.IInventory; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.ChunkCoordinates; +import net.minecraft.world.World; +import net.minecraft.world.biome.BiomeGenBase; +import net.minecraftforge.common.util.ForgeDirection; +import net.minecraftforge.fluids.IFluidHandler; + +/** + * This is a bunch of Functions my TileEntities provide, to make life much easier, and to get rid of internal TileEntity + * stuff. + * <p/> + * This also makes access to adjacent TileEntities more Efficient. + * <p/> + * Note: It doesn't have to be a TileEntity in certain cases! And only certain cases, such as the Recipe checking of the + * findRecipe Function. + */ +public interface IHasWorldObjectAndCoords { + + World getWorld(); + + int getXCoord(); + + short getYCoord(); + + int getZCoord(); + + default ChunkCoordinates getCoords() { + return new ChunkCoordinates(getXCoord(), getYCoord(), getZCoord()); + } + + boolean isServerSide(); + + boolean isClientSide(); + + int getRandomNumber(int aRange); + + TileEntity getTileEntity(int aX, int aY, int aZ); + + TileEntity getTileEntityOffset(int aX, int aY, int aZ); + + TileEntity getTileEntityAtSide(ForgeDirection side); + + TileEntity getTileEntityAtSideAndDistance(ForgeDirection side, int aDistance); + + IInventory getIInventory(int aX, int aY, int aZ); + + IInventory getIInventoryOffset(int aX, int aY, int aZ); + + IInventory getIInventoryAtSide(ForgeDirection side); + + IInventory getIInventoryAtSideAndDistance(ForgeDirection side, int aDistance); + + IFluidHandler getITankContainer(int aX, int aY, int aZ); + + IFluidHandler getITankContainerOffset(int aX, int aY, int aZ); + + IFluidHandler getITankContainerAtSide(ForgeDirection side); + + IFluidHandler getITankContainerAtSideAndDistance(ForgeDirection side, int aDistance); + + IGregTechTileEntity getIGregTechTileEntity(int aX, int aY, int aZ); + + IGregTechTileEntity getIGregTechTileEntityOffset(int aX, int aY, int aZ); + + IGregTechTileEntity getIGregTechTileEntityAtSide(ForgeDirection side); + + IGregTechTileEntity getIGregTechTileEntityAtSideAndDistance(ForgeDirection side, int aDistance); + + Block getBlock(int aX, int aY, int aZ); + + Block getBlockOffset(int aX, int aY, int aZ); + + Block getBlockAtSide(ForgeDirection side); + + Block getBlockAtSideAndDistance(ForgeDirection side, int aDistance); + + byte getMetaID(int aX, int aY, int aZ); + + byte getMetaIDOffset(int aX, int aY, int aZ); + + byte getMetaIDAtSide(ForgeDirection side); + + byte getMetaIDAtSideAndDistance(ForgeDirection side, int aDistance); + + byte getLightLevel(int aX, int aY, int aZ); + + byte getLightLevelOffset(int aX, int aY, int aZ); + + byte getLightLevelAtSide(ForgeDirection side); + + byte getLightLevelAtSideAndDistance(ForgeDirection side, int aDistance); + + boolean getOpacity(int aX, int aY, int aZ); + + boolean getOpacityOffset(int aX, int aY, int aZ); + + boolean getOpacityAtSide(ForgeDirection side); + + boolean getOpacityAtSideAndDistance(ForgeDirection side, int aDistance); + + boolean getSky(int aX, int aY, int aZ); + + boolean getSkyOffset(int aX, int aY, int aZ); + + boolean getSkyAtSide(ForgeDirection side); + + boolean getSkyAtSideAndDistance(ForgeDirection side, int aDistance); + + boolean getAir(int aX, int aY, int aZ); + + boolean getAirOffset(int aX, int aY, int aZ); + + boolean getAirAtSide(ForgeDirection side); + + boolean getAirAtSideAndDistance(ForgeDirection side, int aDistance); + + BiomeGenBase getBiome(); + + BiomeGenBase getBiome(int aX, int aZ); + + int getOffsetX(ForgeDirection side, int aMultiplier); + + short getOffsetY(ForgeDirection side, int aMultiplier); + + int getOffsetZ(ForgeDirection side, int aMultiplier); + + /** + * Checks if the TileEntity is Invalid or Unloaded. Stupid Minecraft cannot do that btw. + */ + boolean isDead(); + + /** + * Sends a Block Event to the Client TileEntity. + * + * @param aValue value to sync + */ + void sendBlockEvent(byte aID, byte aValue); + + /** + * @return the Time this TileEntity has been loaded. + */ + long getTimer(); + + /** + * Sets the Light Level of this Block on a Scale of 0 - 15 It could be that it doesn't work. This is just for + * convenience. + */ + void setLightValue(byte aLightValue); + + /** + * Function of the regular TileEntity + */ + void writeToNBT(NBTTagCompound aNBT); + + /** + * Function of the regular TileEntity + */ + void readFromNBT(NBTTagCompound aNBT); + + /** + * Function of the regular TileEntity + */ + boolean isInvalidTileEntity(); + + /** + * Opens the GUI with this ID of this MetaTileEntity + * + * @deprecated Use ModularUI + */ + @Deprecated + default boolean openGUI(EntityPlayer aPlayer, int aID) { + return false; + } + + /** + * Opens the GUI with the ID = 0 of this TileEntity + * + * @deprecated Use ModularUI + */ + @Deprecated + default boolean openGUI(EntityPlayer aPlayer) { + return false; + } +} diff --git a/src/main/java/gregtech/api/interfaces/tileentity/IIC2Enet.java b/src/main/java/gregtech/api/interfaces/tileentity/IIC2Enet.java new file mode 100644 index 0000000000..971558c092 --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/tileentity/IIC2Enet.java @@ -0,0 +1,17 @@ +package gregtech.api.interfaces.tileentity; + +/** + * IC2 Energy Compat + */ +public interface IIC2Enet { + + /** + * Should this tile/block join the ic2 enet + */ + boolean shouldJoinIc2Enet(); + + /** + * Update the ic2 enet + */ + void doEnetUpdate(); +} diff --git a/src/main/java/gregtech/api/interfaces/tileentity/IMachineBlockUpdateable.java b/src/main/java/gregtech/api/interfaces/tileentity/IMachineBlockUpdateable.java new file mode 100644 index 0000000000..f772c1ee69 --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/tileentity/IMachineBlockUpdateable.java @@ -0,0 +1,24 @@ +package gregtech.api.interfaces.tileentity; + +/** + * You are allowed to include this File in your Download, as i will not change it. Simple Interface for Machines, which + * need my Machine Blocks for MultiBlockStructures. + * <p/> + * Every Machine implementing this Interface will conduct Machine updates. + */ +public interface IMachineBlockUpdateable { + + /** + * The Machine Update, which is called when the Machine needs an Update of its Parts. I suggest to wait 1-5 seconds + * before actually checking the Machine Parts. RP-Frames could for example cause Problems when you instacheck the + * Machine Parts. + */ + void onMachineBlockUpdate(); + + /** + * Should recurse? + */ + default boolean isMachineBlockUpdateRecursive() { + return true; + } +} diff --git a/src/main/java/gregtech/api/interfaces/tileentity/IMachineProgress.java b/src/main/java/gregtech/api/interfaces/tileentity/IMachineProgress.java new file mode 100644 index 0000000000..83570fedcc --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/tileentity/IMachineProgress.java @@ -0,0 +1,96 @@ +package gregtech.api.interfaces.tileentity; + +import javax.annotation.Nonnull; + +import gregtech.api.util.shutdown.ShutDownReason; +import gregtech.api.util.shutdown.ShutDownReasonRegistry; + +/** + * For Machines which have Progress + */ +public interface IMachineProgress extends IHasWorldObjectAndCoords { + + /** + * returns the Progress this Machine has made. Warning, this can also be negative! + */ + int getProgress(); + + /** + * returns the Progress the Machine needs to complete its task. + */ + int getMaxProgress(); + + /** + * Manually increases the Progress of the Machine by vent cover. + */ + boolean increaseProgress(int aProgressAmountInTicks); + + /** + * returns if the Machine currently does something. + */ + boolean hasThingsToDo(); + + /** + * returns if the Machine just got enableWorking called after being disabled. Used for Translocators, which need to + * check if they need to transfer immediately. + */ + boolean hasWorkJustBeenEnabled(); + + /** + * allows Machine to work + */ + void enableWorking(); + + /** + * disallows Machine to work + */ + void disableWorking(); + + /** + * if the Machine is allowed to Work + */ + boolean isAllowedToWork(); + + default void setAllowedToWork(Boolean allowedToWork) { + if (allowedToWork) { + enableWorking(); + } else { + disableWorking(); + } + } + + /** + * used to control Machines via Redstone Signal Strength by special Covers In case of 0 the Machine is very likely + * doing nothing, or is just not being controlled at all. + */ + default byte getWorkDataValue() { + return 0; + } + + /** + * used to control Machines via Redstone Signal Strength by special Covers only Values between 0 and 15! + */ + default void setWorkDataValue(byte aValue) {} + + /** + * gives you the Active Status of the Machine + */ + boolean isActive(); + + /** + * sets the visible Active Status of the Machine + */ + void setActive(boolean aActive); + + /** + * Indicates if the object in question was forced to shut down (i.e. loss of power) + */ + default boolean wasShutdown() { + return false; + } + + @Nonnull + default ShutDownReason getLastShutDownReason() { + return ShutDownReasonRegistry.NONE; + } +} diff --git a/src/main/java/gregtech/api/interfaces/tileentity/IOverclockDescriptionProvider.java b/src/main/java/gregtech/api/interfaces/tileentity/IOverclockDescriptionProvider.java new file mode 100644 index 0000000000..495cd9def4 --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/tileentity/IOverclockDescriptionProvider.java @@ -0,0 +1,15 @@ +package gregtech.api.interfaces.tileentity; + +import javax.annotation.Nullable; + +import gregtech.api.objects.overclockdescriber.OverclockDescriber; + +/** + * Classes implementing this interface can provide {@link OverclockDescriber} to provide overclock behavior and NEI + * description. + */ +public interface IOverclockDescriptionProvider { + + @Nullable + OverclockDescriber getOverclockDescriber(); +} diff --git a/src/main/java/gregtech/api/interfaces/tileentity/IPipeRenderedTileEntity.java b/src/main/java/gregtech/api/interfaces/tileentity/IPipeRenderedTileEntity.java new file mode 100644 index 0000000000..ab476449f0 --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/tileentity/IPipeRenderedTileEntity.java @@ -0,0 +1,18 @@ +package gregtech.api.interfaces.tileentity; + +import net.minecraftforge.common.util.ForgeDirection; + +import gregtech.api.interfaces.ITexture; + +public interface IPipeRenderedTileEntity extends ICoverable, ITexturedTileEntity { + + float getThickNess(); + + byte getConnections(); + + ITexture[] getTextureUncovered(ForgeDirection side); + + default ITexture[] getTextureCovered(ForgeDirection side) { + return getTextureUncovered(side); + } +} diff --git a/src/main/java/gregtech/api/interfaces/tileentity/IRecipeLockable.java b/src/main/java/gregtech/api/interfaces/tileentity/IRecipeLockable.java new file mode 100644 index 0000000000..54d178af3c --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/tileentity/IRecipeLockable.java @@ -0,0 +1,31 @@ +package gregtech.api.interfaces.tileentity; + +import gregtech.api.recipe.check.SingleRecipeCheck; + +/** + * Machines implementing this interface can have logic to lock to a single recipe. + */ +public interface IRecipeLockable extends RecipeMapWorkable { + + /** + * @return if this machine supports single recipe locking. + */ + boolean supportsSingleRecipeLocking(); + + /** + * @return true if recipe locking is enabled, else false. This is getter is used for displaying the icon in the GUI + */ + boolean isRecipeLockingEnabled(); + + void setRecipeLocking(boolean enabled); + + default boolean getDefaultRecipeLockingMode() { + return false; + } + + default SingleRecipeCheck getSingleRecipeCheck() { + return null; + } + + default void setSingleRecipeCheck(SingleRecipeCheck recipeCheck) {} +} diff --git a/src/main/java/gregtech/api/interfaces/tileentity/IRedstoneEmitter.java b/src/main/java/gregtech/api/interfaces/tileentity/IRedstoneEmitter.java new file mode 100644 index 0000000000..1b280184ce --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/tileentity/IRedstoneEmitter.java @@ -0,0 +1,52 @@ +package gregtech.api.interfaces.tileentity; + +import net.minecraftforge.common.util.ForgeDirection; + +/** + * This File has just internal Information about the Redstone State of a TileEntity + */ +public interface IRedstoneEmitter extends IHasWorldObjectAndCoords { + + /** + * gets the Redstone Level the TileEntity should emit to the given Output Side + * + * @param side the {@link ForgeDirection} side + * @return the Redstone Level the TileEntity + */ + byte getOutputRedstoneSignal(ForgeDirection side); + + /** + * sets the Redstone Level the TileEntity should emit to the given Output Side + * <p/> + * Do not use this if ICoverable is implemented. ICoverable has @getInternalOutputRedstoneSignal for Machine + * internal Output Redstone, so that it doesnt conflict with Cover Redstone. This sets the true Redstone Output + * Signal. Only Cover Behaviors should use it, not MetaTileEntities. + */ + void setOutputRedstoneSignal(ForgeDirection side, byte aStrength); + + /** + * gets the Redstone Level the TileEntity should emit to the given Output Side + */ + byte getStrongOutputRedstoneSignal(ForgeDirection side); + + /** + * sets the Redstone Level the TileEntity should emit to the given Output Side + * <p/> + * Do not use this if ICoverable is implemented. ICoverable has @getInternalOutputRedstoneSignal for Machine + * internal Output Redstone, so that it doesnt conflict with Cover Redstone. This sets the true Redstone Output + * Signal. Only Cover Behaviors should use it, not MetaTileEntities. + */ + void setStrongOutputRedstoneSignal(ForgeDirection side, byte aStrength); + + /** + * Gets the Output for the comparator on the given Side + */ + byte getComparatorValue(ForgeDirection side); + + /** + * Get the redstone output signal strength for a given side + */ + default byte getGeneralRS(ForgeDirection side) { + return 0; + } +} diff --git a/src/main/java/gregtech/api/interfaces/tileentity/IRedstoneReceiver.java b/src/main/java/gregtech/api/interfaces/tileentity/IRedstoneReceiver.java new file mode 100644 index 0000000000..a41e9aaf7a --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/tileentity/IRedstoneReceiver.java @@ -0,0 +1,33 @@ +package gregtech.api.interfaces.tileentity; + +import net.minecraftforge.common.util.ForgeDirection; + +/** + * This File has just internal Information about the Redstone State of a TileEntity + */ +public interface IRedstoneReceiver extends IHasWorldObjectAndCoords { + + /** + * gets the Redstone Level of the TileEntity to the given Input Side + * <p/> + * Do not use this if ICoverable is implemented. ICoverable has @getInternalInputRedstoneSignal for Machine internal + * Input Redstone This returns the true incoming Redstone Signal. Only Cover Behaviors should check it, not + * MetaTileEntities. + */ + byte getInputRedstoneSignal(ForgeDirection side); + + /** + * gets the strongest Redstone Level the TileEntity receives + */ + byte getStrongestRedstone(); + + /** + * gets if the TileEntity receives Redstone + */ + boolean getRedstone(); + + /** + * gets if the TileEntity receives Redstone at this Side + */ + boolean getRedstone(ForgeDirection side); +} diff --git a/src/main/java/gregtech/api/interfaces/tileentity/IRedstoneTileEntity.java b/src/main/java/gregtech/api/interfaces/tileentity/IRedstoneTileEntity.java new file mode 100644 index 0000000000..1deb5f1d7c --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/tileentity/IRedstoneTileEntity.java @@ -0,0 +1,17 @@ +package gregtech.api.interfaces.tileentity; + +/** + * This File has just internal Information about the Redstone State of a TileEntity + */ +public interface IRedstoneTileEntity extends IRedstoneEmitter, IRedstoneReceiver { + + /** + * enables/disables Redstone Output in general. + */ + void setGenericRedstoneOutput(boolean aOnOff); + + /** + * Causes a general Block update. Sends nothing to Client, just causes a Block Update. + */ + void issueBlockUpdate(); +} diff --git a/src/main/java/gregtech/api/interfaces/tileentity/ITexturedTileEntity.java b/src/main/java/gregtech/api/interfaces/tileentity/ITexturedTileEntity.java new file mode 100644 index 0000000000..379111b07e --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/tileentity/ITexturedTileEntity.java @@ -0,0 +1,14 @@ +package gregtech.api.interfaces.tileentity; + +import net.minecraft.block.Block; +import net.minecraftforge.common.util.ForgeDirection; + +import gregtech.api.interfaces.ITexture; + +public interface ITexturedTileEntity { + + /** + * @return the Textures rendered by the GT Rendering + */ + ITexture[] getTexture(Block aBlock, ForgeDirection side); +} diff --git a/src/main/java/gregtech/api/interfaces/tileentity/ITurnable.java b/src/main/java/gregtech/api/interfaces/tileentity/ITurnable.java new file mode 100644 index 0000000000..64cc8675ef --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/tileentity/ITurnable.java @@ -0,0 +1,46 @@ +package gregtech.api.interfaces.tileentity; + +import net.minecraftforge.common.util.ForgeDirection; + +/** + * Implemented by all my Machines. However without any security checks, if the Players are even allowed to rotate it. + */ +public interface ITurnable { + + /** + * Get the block's facing. + * + * @return front Block facing + */ + ForgeDirection getFrontFacing(); + + /** + * Set the block's facing + * + * @param side facing to set the block to + */ + void setFrontFacing(ForgeDirection side); + + /** + * Get the block's back facing. + * + * @return opposite Block facing + */ + ForgeDirection getBackFacing(); + + /** + * Determine if the wrench can be used to set the block's facing. + */ + boolean isValidFacing(ForgeDirection side); + + /** + * Get the list of valid facings + */ + default boolean[] getValidFacings() { + final boolean[] validFacings = new boolean[6]; + for (final ForgeDirection facing : ForgeDirection.VALID_DIRECTIONS) { + validFacings[facing.ordinal()] = isValidFacing(facing); + } + return validFacings; + } +} diff --git a/src/main/java/gregtech/api/interfaces/tileentity/IUpgradableMachine.java b/src/main/java/gregtech/api/interfaces/tileentity/IUpgradableMachine.java new file mode 100644 index 0000000000..546ada0c9d --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/tileentity/IUpgradableMachine.java @@ -0,0 +1,42 @@ +package gregtech.api.interfaces.tileentity; + +/** + * To access my Machines a bit easier + */ +public interface IUpgradableMachine extends IMachineProgress { + + /** + * Accepts Upgrades. Some Machines have an Upgrade Limit. + */ + boolean isUpgradable(); + + /** + * Accepts Muffler Upgrades + */ + boolean isMufflerUpgradable(); + + /** + * Accepts Steam-Converter Upgrades + */ + boolean isSteamEngineUpgradable(); + + /** + * Adds Muffler Upgrade + */ + boolean addMufflerUpgrade(); + + /** + * Adds MJ-Converter Upgrade + */ + boolean addSteamEngineUpgrade(); + + /** + * Does this Machine have an Muffler + */ + boolean hasMufflerUpgrade(); + + /** + * Does this Machine have a Steam-Converter + */ + boolean hasSteamEngineUpgrade(); +} diff --git a/src/main/java/gregtech/api/interfaces/tileentity/IVoidable.java b/src/main/java/gregtech/api/interfaces/tileentity/IVoidable.java new file mode 100644 index 0000000000..841fd07bba --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/tileentity/IVoidable.java @@ -0,0 +1,89 @@ +package gregtech.api.interfaces.tileentity; + +import java.util.List; +import java.util.Set; + +import net.minecraft.item.ItemStack; +import net.minecraftforge.fluids.FluidStack; + +import gregtech.api.enums.VoidingMode; +import gregtech.api.interfaces.fluid.IFluidStore; + +/** + * Machines implementing this interface can have logic to configure whether to void excess output or not. + */ +public interface IVoidable { + + /** + * @return if this machine can prevent excess item and fluid from voiding. + */ + boolean supportsVoidProtection(); + + default Set<VoidingMode> getAllowedVoidingModes() { + return VoidingMode.ALL_OPTIONS; + } + + /** + * @return if this machine is configured to not void excess item. + */ + default boolean protectsExcessItem() { + return supportsVoidProtection() && getVoidingMode().protectItem; + } + + /** + * @return if this machine is configured to not void excess fluid. + */ + default boolean protectsExcessFluid() { + return supportsVoidProtection() && getVoidingMode().protectFluid; + } + + VoidingMode getVoidingMode(); + + void setVoidingMode(VoidingMode mode); + + default VoidingMode getDefaultVoidingMode() { + return supportsVoidProtection() ? VoidingMode.VOID_NONE : VoidingMode.VOID_ALL; + } + + /** + * @param toOutput List of items this machine is going to output. + * @return List of slots available for item outputs. Null element represents empty slot. + */ + List<ItemStack> getItemOutputSlots(ItemStack[] toOutput); + + /** + * @param toOutput List of fluids this machine is going to output. + * @return List of slots available for fluid outputs. + */ + List<? extends IFluidStore> getFluidOutputSlots(FluidStack[] toOutput); + + /** + * @return How many slots of items this machine can output per recipe. Item outputs whose slot number + * exceeding this limit will be voided. + */ + default int getItemOutputLimit() { + return Integer.MAX_VALUE; + } + + /** + * @return How many slots of fluids this machine can output per recipe. Fluid outputs whose slot number + * exceeding this limit will be voided. + */ + default int getFluidOutputLimit() { + return Integer.MAX_VALUE; + } + + /** + * @return If this machine has ability to dump item outputs to ME network. + * This doesn't need to check if it can actually dump to ME, + * as this might be called every tick and cause lag. + */ + boolean canDumpItemToME(); + + /** + * @return If this machine has ability to dump fluid outputs to ME network. + * This doesn't need to check if it can actually dump to ME, + * as this might be called every tick and cause lag. + */ + boolean canDumpFluidToME(); +} diff --git a/src/main/java/gregtech/api/interfaces/tileentity/IWirelessEnergyHatchInformation.java b/src/main/java/gregtech/api/interfaces/tileentity/IWirelessEnergyHatchInformation.java new file mode 100644 index 0000000000..fa58e5dd54 --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/tileentity/IWirelessEnergyHatchInformation.java @@ -0,0 +1,17 @@ +package gregtech.api.interfaces.tileentity; + +public interface IWirelessEnergyHatchInformation { + + // This interface is solely for usage by wireless hatches/dynamos. + + // Ticks between energy additions to the hatch. For a dynamo this is how many ticks between energy being consumed + // and added to the global energy map. + long ticks_between_energy_addition = 100L * 20L; + + // Total number of energy additions this multi can store before it is full. + long number_of_energy_additions = 4L; + + default long totalStorage(long tier_eu_per_tick) { + return tier_eu_per_tick * ticks_between_energy_addition * number_of_energy_additions; + } +} diff --git a/src/main/java/gregtech/api/interfaces/tileentity/RecipeMapWorkable.java b/src/main/java/gregtech/api/interfaces/tileentity/RecipeMapWorkable.java new file mode 100644 index 0000000000..7d4db4396c --- /dev/null +++ b/src/main/java/gregtech/api/interfaces/tileentity/RecipeMapWorkable.java @@ -0,0 +1,49 @@ +package gregtech.api.interfaces.tileentity; + +import java.util.Collection; +import java.util.Collections; + +import javax.annotation.Nonnull; + +import net.minecraft.item.ItemStack; + +import gregtech.api.recipe.RecipeMap; + +/** + * Machines implementing this interface are capable of executing certain recipes provided by {@link RecipeMap}. + * They will also be automatically registered as NEI recipe catalyst for the corresponding recipemaps. + */ +public interface RecipeMapWorkable { + + /** + * @return RecipeMap this machine currently can execute. In general, it's allowed to be null. + */ + RecipeMap<?> getRecipeMap(); + + /** + * @return ItemStack form of this machine. + */ + ItemStack getStackForm(long amount); + + /** + * If the machine supports multiple recipemaps by switching mode, override this method so that it will be displayed + * as NEI recipe catalyst on all the supported recipemaps. + * + * @return List of possible {@link RecipeMap}s this machine can execute. Must not contain null element. + */ + @Nonnull + default Collection<RecipeMap<?>> getAvailableRecipeMaps() { + RecipeMap<?> recipeMap = getRecipeMap(); + if (recipeMap != null) { + return Collections.singletonList(recipeMap); + } + return Collections.emptyList(); + } + + /** + * @return Priority for NEI recipe catalyst. Higher priority comes first. + */ + default int getRecipeCatalystPriority() { + return 0; + } +} diff --git a/src/main/java/gregtech/api/items/GT_Block_LongDistancePipe.java b/src/main/java/gregtech/api/items/GT_Block_LongDistancePipe.java new file mode 100644 index 0000000000..31a44f3dbc --- /dev/null +++ b/src/main/java/gregtech/api/items/GT_Block_LongDistancePipe.java @@ -0,0 +1,125 @@ +package gregtech.api.items; + +import java.util.List; + +import net.minecraft.block.Block; +import net.minecraft.client.renderer.texture.IIconRegister; +import net.minecraft.creativetab.CreativeTabs; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EnumCreatureType; +import net.minecraft.init.Blocks; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.util.IIcon; +import net.minecraft.util.StatCollector; +import net.minecraft.world.IBlockAccess; +import net.minecraft.world.World; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import gregtech.api.GregTech_API; +import gregtech.api.enums.ItemList; +import gregtech.api.enums.Textures; +import gregtech.api.interfaces.IIconContainer; +import gregtech.api.util.GT_LanguageManager; +import gregtech.common.blocks.GT_Item_LongDistancePipe; +import gregtech.common.blocks.GT_Material_Machines; + +public class GT_Block_LongDistancePipe extends GT_Generic_Block { + + public IIconContainer[] mIcons; + + public GT_Block_LongDistancePipe() { + super(GT_Item_LongDistancePipe.class, "gt.block.longdistancepipe", new GT_Material_Machines()); + setStepSound(soundTypeMetal); + setCreativeTab(GregTech_API.TAB_GREGTECH); + GregTech_API.registerMachineBlock(this, -1); + + GT_LanguageManager.addStringLocalization(getUnlocalizedName() + ".0.name", "Long Distance Fluid Pipeline Pipe"); + GT_LanguageManager.addStringLocalization(getUnlocalizedName() + ".1.name", "Long Distance Item Pipeline Pipe"); + GT_LanguageManager.addStringLocalization(getUnlocalizedName() + "." + 32767 + ".name", "Any Sub Block of this"); + + ItemList.Long_Distance_Pipeline_Fluid_Pipe.set(new ItemStack(this, 1, 0)); + ItemList.Long_Distance_Pipeline_Item_Pipe.set(new ItemStack(this, 1, 1)); + mIcons = new IIconContainer[] { Textures.BlockIcons.LONG_DISTANCE_PIPE_FLUID, + Textures.BlockIcons.LONG_DISTANCE_PIPE_ITEM }; + } + + @Override + public void onBlockAdded(World aWorld, int aX, int aY, int aZ) { + super.onBlockAdded(aWorld, aX, aY, aZ); + if (GregTech_API.isMachineBlock(this, aWorld.getBlockMetadata(aX, aY, aZ))) { + GregTech_API.causeMachineUpdate(aWorld, aX, aY, aZ); + } + } + + @Override + public void breakBlock(World aWorld, int aX, int aY, int aZ, Block aBlock, int aMetaData) { + GregTech_API.causeMachineUpdate(aWorld, aX, aY, aZ); + super.breakBlock(aWorld, aX, aY, aZ, aBlock, aMetaData); + } + + @Override + public String getHarvestTool(int aMeta) { + return "wrench"; + } + + @Override + public int getHarvestLevel(int aMeta) { + return 2; + } + + @Override + public float getBlockHardness(World aWorld, int aX, int aY, int aZ) { + return Blocks.stone.getBlockHardness(aWorld, aX, aY, aZ); + } + + @Override + public float getExplosionResistance(Entity aTNT) { + return Blocks.iron_block.getExplosionResistance(aTNT); + } + + @Override + public String getUnlocalizedName() { + return this.mUnlocalizedName; + } + + @Override + public String getLocalizedName() { + return StatCollector.translateToLocal(this.mUnlocalizedName + ".name"); + } + + @Override + public IIcon getIcon(int ordinalSide, int aMeta) { + return mIcons[aMeta % mIcons.length].getIcon(); + } + + @Override + @SideOnly(Side.CLIENT) + public void registerBlockIcons(IIconRegister aIconRegister) {} + + @Override + public boolean canCreatureSpawn(EnumCreatureType type, IBlockAccess world, int x, int y, int z) { + return false; + } + + @Override + public int damageDropped(int metadata) { + return metadata; + } + + @Override + public int getDamageValue(World aWorld, int aX, int aY, int aZ) { + return aWorld.getBlockMetadata(aX, aY, aZ); + } + + @Override + @SideOnly(Side.CLIENT) + public void getSubBlocks(Item aItem, CreativeTabs aCreativeTab, List<ItemStack> aList) { + for (int i = 0; i < 3; i++) { + ItemStack aStack = new ItemStack(aItem, 1, i); + if (!aStack.getDisplayName() + .contains(".name")) aList.add(aStack); + } + } +} diff --git a/src/main/java/gregtech/api/items/GT_BreederCell_Item.java b/src/main/java/gregtech/api/items/GT_BreederCell_Item.java new file mode 100644 index 0000000000..3d51415783 --- /dev/null +++ b/src/main/java/gregtech/api/items/GT_BreederCell_Item.java @@ -0,0 +1,147 @@ +package gregtech.api.items; + +import static gregtech.api.util.GT_Utility.formatNumbers; + +import java.util.List; +import java.util.function.Supplier; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.world.World; + +import gregtech.api.GregTech_API; +import gregtech.api.enums.GT_Values; +import gregtech.api.util.GT_Utility; +import ic2.api.reactor.IReactor; +import ic2.api.reactor.IReactorComponent; +import ic2.core.IC2Potion; + +/** + * A {@link ic2.core.item.reactor.ItemReactorLithiumCell}, but can be used to produce anything! + * + * @author glee8e + */ +public class GT_BreederCell_Item extends GT_Generic_Item implements IReactorComponent { + + protected final int mHeatBonusStep; + protected final int mHeatBonusMultiplier; + protected ItemStack mProduct; + protected boolean deflector = false; + protected boolean hidden = false; + protected boolean neiAdded = false; + + public GT_BreederCell_Item(String aUnlocalized, String aEnglish, String aEnglishTooltip, int aHeatBonusStep, + int aHeatBonusMultiplier, int aRequiredPulse, Supplier<ItemStack> aProduct) { + super(aUnlocalized, aEnglish, aEnglishTooltip); + this.mHeatBonusStep = aHeatBonusStep; + this.mHeatBonusMultiplier = aHeatBonusMultiplier; + this.setMaxDamage(aRequiredPulse); + setNoRepair(); + GregTech_API.sAfterGTPostload.add(() -> { + mProduct = aProduct.get(); + if (!hidden && !neiAdded) { + GT_Values.RA.addIC2ReactorBreederCell( + new ItemStack(this), + mProduct, + deflector, + mHeatBonusStep, + mHeatBonusMultiplier, + getMaxDamage()); + neiAdded = true; + } + }); + } + + public GT_BreederCell_Item setDeflector() { + deflector = true; + return this; + } + + public GT_BreederCell_Item setHidden() { + hidden = true; + return this; + } + + @Override + public void onUpdate(ItemStack stack, World world, Entity entity, int slotIndex, boolean isCurrentItem) { + if ((entity instanceof EntityLivingBase entityLiving)) { + if (!GT_Utility.isWearingFullRadioHazmat(entityLiving)) { + IC2Potion.radiation.applyTo(entityLiving, 20, 1); + } + } + } + + @Override + public void addAdditionalToolTips(List<String> aList, ItemStack aStack, EntityPlayer aPlayer) { + aList.add(transItem("019", "Bath with neutron in a hot reactor")); + int rDmg = aStack.getItemDamage() * 4 / getMaxDamage(); + EnumChatFormatting color2 = switch (rDmg) { + case 0 -> EnumChatFormatting.DARK_GRAY; + case 1, 2 -> EnumChatFormatting.GRAY; + default -> EnumChatFormatting.WHITE; + }; + aList.add( + String.format( + transItem("020", "Progress: %s/%s"), + "" + color2 + formatNumbers(aStack.getItemDamage()) + EnumChatFormatting.RESET, + "" + formatNumbers(getMaxDamage()))); + if (aStack.getItemDamage() > 0) aList.add(EnumChatFormatting.RED + transItem("021", "Radiation Hazard")); + } + + @Override + public int getItemStackLimit(ItemStack stack) { + return stack.getItemDamage() == 0 ? maxStackSize : 1; + } + + @Override + public void processChamber(IReactor reactor, ItemStack yourStack, int x, int y, boolean heatrun) {} + + @Override + public boolean acceptUraniumPulse(IReactor reactor, ItemStack yourStack, ItemStack pulsingStack, int youX, int youY, + int pulseX, int pulseY, boolean heatrun) { + if (heatrun) { + int myLevel = getNewDamage(reactor, yourStack); + if (myLevel >= getMaxDamage()) reactor.setItemAt(youX, youY, mProduct.copy()); + else yourStack.setItemDamage(myLevel); + } + + return deflector; + } + + protected int getNewDamage(IReactor reactor, ItemStack stack) { + return stack.getItemDamage() + 1 + reactor.getHeat() / mHeatBonusStep * mHeatBonusMultiplier; + } + + @Override + public boolean canStoreHeat(IReactor reactor, ItemStack yourStack, int x, int y) { + return false; + } + + @Override + public int getMaxHeat(IReactor reactor, ItemStack yourStack, int x, int y) { + return 0; + } + + @Override + public int getCurrentHeat(IReactor reactor, ItemStack yourStack, int x, int y) { + return 0; + } + + @Override + public int alterHeat(IReactor reactor, ItemStack yourStack, int x, int y, int heat) { + return heat; + } + + @Override + public float influenceExplosion(IReactor reactor, ItemStack yourStack) { + return 0.0F; + } + + @Override + public double getDurabilityForDisplay(ItemStack stack) { + return 1.0D - (double) stack.getItemDamageForDisplay() / (double) stack.getMaxDamage(); + } +} diff --git a/src/main/java/gregtech/api/items/GT_CoolantCellIC_Item.java b/src/main/java/gregtech/api/items/GT_CoolantCellIC_Item.java new file mode 100644 index 0000000000..eb3c6276cf --- /dev/null +++ b/src/main/java/gregtech/api/items/GT_CoolantCellIC_Item.java @@ -0,0 +1,67 @@ +package gregtech.api.items; + +import net.minecraft.item.ItemStack; + +import ic2.api.reactor.IReactor; +import ic2.api.reactor.IReactorComponent; + +public class GT_CoolantCellIC_Item extends GT_CoolantCell_Item implements IReactorComponent { + + public GT_CoolantCellIC_Item(String aUnlocalized, String aEnglish, int aMaxStore) { + super(aUnlocalized, aEnglish, aMaxStore); + } + + @Override + public void processChamber(IReactor aReactor, ItemStack aStack, int x, int y, boolean aHeatRun) {} + + @Override + public boolean acceptUraniumPulse(IReactor aReactor, ItemStack aStack, ItemStack pulsingStack, int youX, int youY, + int pulseX, int pulseY, boolean aHeatRun) { + return false; + } + + @Override + public boolean canStoreHeat(IReactor aReactor, ItemStack aStack, int x, int y) { + return !aReactor.isFluidCooled() || getControlTagOfStack(aStack) == 0; + } + + @Override + public int getMaxHeat(IReactor aReactor, ItemStack aStack, int x, int y) { + return this.heatStorage; + } + + @Override + public int getCurrentHeat(IReactor aReactor, ItemStack aStack, int x, int y) { + return getHeatOfStack(aStack); + } + + @Override + public float influenceExplosion(IReactor aReactor, ItemStack aStack) { + return 1.0F + this.heatStorage / 30000.0F; + } + + @Override + public int alterHeat(IReactor aReactor, ItemStack aStack, int x, int y, int aHeat) { + int tHeat = getHeatOfStack(aStack); + if ((tHeat == 0) && (getControlTagOfStack(aStack) != 0)) { + setControlTagOfStack(aStack, 0); + } + tHeat += aHeat; + if (tHeat > this.heatStorage) { + aReactor.setItemAt(x, y, null); + aHeat = this.heatStorage - tHeat + 1; + } else { + if (tHeat < 0) { + aHeat = tHeat; + tHeat = 0; + } else { + aHeat = 0; + } + if ((tHeat > 0) && (getControlTagOfStack(aStack) == 0) && (!aReactor.isFluidCooled())) { + setControlTagOfStack(aStack, 1); + } + setHeatForStack(aStack, tHeat); + } + return aHeat; + } +} diff --git a/src/main/java/gregtech/api/items/GT_CoolantCell_Item.java b/src/main/java/gregtech/api/items/GT_CoolantCell_Item.java new file mode 100644 index 0000000000..6ed7dae3b0 --- /dev/null +++ b/src/main/java/gregtech/api/items/GT_CoolantCell_Item.java @@ -0,0 +1,82 @@ +package gregtech.api.items; + +import java.util.List; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.StatCollector; + +import gregtech.api.GregTech_API; +import ic2.core.util.StackUtil; + +public class GT_CoolantCell_Item extends GT_Generic_Item { + + protected final int heatStorage; + + public GT_CoolantCell_Item(String aUnlocalized, String aEnglish, int aMaxStore) { + super(aUnlocalized, aEnglish, null); + this.setMaxStackSize(1); + this.setMaxDamage(100); + setNoRepair(); + this.heatStorage = aMaxStore; + this.setCreativeTab(GregTech_API.TAB_GREGTECH); + } + + protected static int getHeatOfStack(ItemStack aStack) { + NBTTagCompound tNBT = aStack.getTagCompound(); + if (tNBT == null) { + tNBT = new NBTTagCompound(); + aStack.setTagCompound(tNBT); + } + return tNBT.getInteger("heat"); + } + + protected void setHeatForStack(ItemStack aStack, int aHeat) { + NBTTagCompound tNBT = aStack.getTagCompound(); + if (tNBT == null) { + tNBT = new NBTTagCompound(); + aStack.setTagCompound(tNBT); + } + tNBT.setInteger("heat", aHeat); + if (this.heatStorage > 0) { + double heatRatio = (double) aHeat / (double) this.heatStorage; + int damage = (int) (aStack.getMaxDamage() * heatRatio); + if (damage >= aStack.getMaxDamage()) { + damage = aStack.getMaxDamage() - 1; + } + aStack.setItemDamage(damage); + } + } + + @Override + public void addAdditionalToolTips(List<String> aList, ItemStack aStack, EntityPlayer aPlayer) { + super.addAdditionalToolTips(aList, aStack, aPlayer); + int rHeat = getHeatOfStack(aStack) * 10 / this.heatStorage; + EnumChatFormatting color = switch (rHeat) { + case 0 -> EnumChatFormatting.BLUE; + case 1, 2 -> EnumChatFormatting.GREEN; + case 3, 4, 5, 6 -> EnumChatFormatting.YELLOW; + case 7, 8 -> EnumChatFormatting.RED; + default -> EnumChatFormatting.DARK_RED; + }; + aList.add( + EnumChatFormatting.WHITE + + String.format(transItem("000", "Stored Heat: %s"), "" + color + getHeatOfStack(aStack))); + if (getControlTagOfStack(aStack) == 1) { + aList.add(StatCollector.translateToLocal("ic2.reactoritem.heatwarning.line1")); + aList.add(StatCollector.translateToLocal("ic2.reactoritem.heatwarning.line2")); + } + } + + public int getControlTagOfStack(ItemStack stack) { + NBTTagCompound nbtData = StackUtil.getOrCreateNbtData(stack); + return nbtData.getInteger("tag"); + } + + public void setControlTagOfStack(ItemStack stack, int tag) { + NBTTagCompound nbtData = StackUtil.getOrCreateNbtData(stack); + nbtData.setInteger("tag", tag); + } +} diff --git a/src/main/java/gregtech/api/items/GT_EnergyArmor_Item.java b/src/main/java/gregtech/api/items/GT_EnergyArmor_Item.java new file mode 100644 index 0000000000..9c7b7c173c --- /dev/null +++ b/src/main/java/gregtech/api/items/GT_EnergyArmor_Item.java @@ -0,0 +1,340 @@ +package gregtech.api.items; + +import static gregtech.api.enums.Mods.GregTech; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import net.minecraft.client.renderer.texture.IIconRegister; +import net.minecraft.creativetab.CreativeTabs; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.Item; +import net.minecraft.item.ItemArmor; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.potion.Potion; +import net.minecraft.potion.PotionEffect; +import net.minecraft.util.DamageSource; +import net.minecraft.util.MathHelper; +import net.minecraft.world.World; +import net.minecraftforge.common.ISpecialArmor; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.entity.living.LivingFallEvent; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import gregtech.api.GregTech_API; +import gregtech.api.util.GT_LanguageManager; +import gregtech.api.util.GT_ModHandler; +import gregtech.api.util.GT_Utility; + +public class GT_EnergyArmor_Item extends ItemArmor implements ISpecialArmor { + + public static Map<EntityPlayer, Float> jumpChargeMap = new ConcurrentHashMap<>(); + public int mCharge, mTransfer, mTier, mDamageEnergyCost, mSpecials; + public boolean mChargeProvider; + public double mArmorAbsorbtionPercentage; + + public GT_EnergyArmor_Item(int aID, String aUnlocalized, String aEnglish, int aCharge, int aTransfer, int aTier, + int aDamageEnergyCost, int aSpecials, double aArmorAbsorbtionPercentage, boolean aChargeProvider, int aType, + int aArmorIndex) { + super(ArmorMaterial.DIAMOND, aArmorIndex, aType); + setMaxStackSize(1); + setMaxDamage(100); + setNoRepair(); + setUnlocalizedName(aUnlocalized); + GT_LanguageManager.addStringLocalization(getUnlocalizedName() + ".name", aEnglish); + mCharge = Math.max(1, aCharge); + mTransfer = Math.max(1, aTransfer); + mTier = Math.max(1, aTier); + mSpecials = aSpecials; + mChargeProvider = aChargeProvider; + mDamageEnergyCost = Math.max(0, aDamageEnergyCost); + mArmorAbsorbtionPercentage = aArmorAbsorbtionPercentage; + + setCreativeTab(GregTech_API.TAB_GREGTECH); + + MinecraftForge.EVENT_BUS.register(this); + } + + private static void setCharge(ItemStack aStack) { + NBTTagCompound tNBT = aStack.getTagCompound(); + if (tNBT == null) tNBT = new NBTTagCompound(); + tNBT.setInteger("charge", 1000000000); + aStack.setTagCompound(tNBT); + } + + @Override + public ItemStack onItemRightClick(ItemStack aStack, World aWorld, EntityPlayer aPlayer) { + ItemStack tStack = aPlayer.inventory.armorInventory[3 - armorType]; + if (tStack != null) { + for (int i = 0; i < 9; i++) { + if (aPlayer.inventory.mainInventory[i] == aStack) { + aPlayer.inventory.armorInventory[3 - armorType] = aPlayer.inventory.mainInventory[i]; + aPlayer.inventory.mainInventory[i] = tStack; + return tStack; + } + } + } + return super.onItemRightClick(aStack, aWorld, aPlayer); + } + + @Override + @SideOnly(Side.CLIENT) + public void registerIcons(IIconRegister aIconRegister) { + this.itemIcon = aIconRegister.registerIcon(GregTech.getResourcePath(getUnlocalizedName())); + } + + @Override + public void addInformation(ItemStack aStack, EntityPlayer aPlayer, List<String> aList, boolean aF3_H) { + aList.add("Tier: " + mTier); + if ((mSpecials & 1) != 0) aList.add("Rebreather"); + if ((mSpecials & 2) != 0) aList.add("Inertia Damper"); + if ((mSpecials & 4) != 0) aList.add("Food Replicator"); + if ((mSpecials & 8) != 0) aList.add("Medicine Module"); + if ((mSpecials & 16) != 0) aList.add("Lamp"); + if ((mSpecials & 32) != 0) aList.add("Solarpanel"); + if ((mSpecials & 64) != 0) aList.add("Extinguisher Module"); + if ((mSpecials & 128) != 0) aList.add("Jump Booster"); + if ((mSpecials & 256) != 0) aList.add("Speed Booster"); + if ((mSpecials & 512) != 0) aList.add("Invisibility Field"); + if ((mSpecials & 1024) != 0) aList.add("Infinite Charge"); + } + + @Override + public void onArmorTick(World aWorld, EntityPlayer aPlayer, ItemStack aStack) { + if (mSpecials == 0) return; + + if (!aPlayer.worldObj.isRemote && (mSpecials & 1) != 0) { + int airSupply = aPlayer.getAir(); + if (GT_ModHandler.canUseElectricItem(aStack, 1000) && airSupply < 50) { + aPlayer.setAir(airSupply + 250); + GT_ModHandler.useElectricItem(aStack, 1000, aPlayer); + } + } + + if (!aPlayer.worldObj.isRemote && (mSpecials & 4) != 0) { + if (GT_ModHandler.canUseElectricItem(aStack, 50000) && aPlayer.getFoodStats() + .needFood()) { + aPlayer.getFoodStats() + .addStats(1, 0.0F); + GT_ModHandler.useElectricItem(aStack, 50000, aPlayer); + } + } + + if ((mSpecials & 8) != 0) { + if (GT_ModHandler.canUseElectricItem(aStack, 10000) && aPlayer.isPotionActive(Potion.poison)) { + GT_Utility.removePotion(aPlayer, Potion.poison.id); + GT_ModHandler.useElectricItem(aStack, 10000, aPlayer); + } + if (GT_ModHandler.canUseElectricItem(aStack, 100000) && aPlayer.isPotionActive(Potion.wither)) { + GT_Utility.removePotion(aPlayer, Potion.wither.id); + GT_ModHandler.useElectricItem(aStack, 100000, aPlayer); + } + } + + if ((mSpecials & 64) != 0) { + aPlayer.setFire(0); + } + + if (!aPlayer.worldObj.isRemote && (mSpecials & 128) != 0) { + float jumpCharge = jumpChargeMap.getOrDefault(aPlayer, 1.0F); + + if (GT_ModHandler.canUseElectricItem(aStack, 1000) && aPlayer.onGround && jumpCharge < 1.0F) { + jumpCharge = 1.0F; + GT_ModHandler.useElectricItem(aStack, 1000, aPlayer); + } + + if (aPlayer.motionY >= 0.0D && jumpCharge > 0.0F && !aPlayer.isInWater()) { + if (GT_ModHandler.getJumpKeyDown(aPlayer) && GT_ModHandler.getBoostKeyDown(aPlayer)) { + if (jumpCharge == 1.0F) { + aPlayer.motionX *= 3.5D; + aPlayer.motionZ *= 3.5D; + } + + aPlayer.motionY += (jumpCharge * 0.3F); + jumpCharge = (float) (jumpCharge * 0.75D); + } else if (jumpCharge < 1.0F) { + jumpCharge = 0.0F; + } + } + + jumpChargeMap.put(aPlayer, jumpCharge); + } + + if ((mSpecials & 256) != 0) { + if (GT_ModHandler.canUseElectricItem(aStack, 100) && aPlayer.isSprinting() + && (aPlayer.onGround && Math.abs(aPlayer.motionX) + Math.abs(aPlayer.motionZ) > 0.10000000149011612D + || aPlayer.isInWater())) { + GT_ModHandler.useElectricItem(aStack, 100, aPlayer); + float bonus = 0.22F; + + if (aPlayer.isInWater()) { + GT_ModHandler.useElectricItem(aStack, 100, aPlayer); + bonus = 0.1F; + + if (aPlayer.motionY > 0) { + aPlayer.motionY += 0.10000000149011612D; + } + } + + aPlayer.moveFlying(0.0F, 1.0F, bonus); + } + } + + if ((mSpecials & 512) != 0) { + if (GT_ModHandler.canUseElectricItem(aStack, 10000)) { + GT_ModHandler.useElectricItem(aStack, 10000, aPlayer); + aPlayer.addPotionEffect(new PotionEffect(Potion.invisibility.getId(), 25, 1, true)); + } + } + + if (!aPlayer.worldObj.isRemote && (mSpecials & (16 | 32)) != 0) { + // if (GregTech_API.sWorldTickCounter%20==0) { + ItemStack tTargetChargeItem = aStack, tTargetDechargeItem = aStack; + + if (GT_ModHandler.chargeElectricItem(tTargetChargeItem, 1, Integer.MAX_VALUE, true, true) < 1) { + tTargetChargeItem = aPlayer.inventory.armorInventory[2]; + } + if (GT_ModHandler.dischargeElectricItem(tTargetDechargeItem, 10, Integer.MAX_VALUE, true, true, true) + < 10) { + tTargetDechargeItem = aPlayer.inventory.armorInventory[2]; + } + + if (tTargetChargeItem == null || !GT_ModHandler.isElectricItem(tTargetChargeItem)) { + tTargetChargeItem = null; + } + + if (aPlayer.worldObj.isDaytime() && aPlayer.worldObj.canBlockSeeTheSky( + MathHelper.floor_double(aPlayer.posX), + MathHelper.floor_double(aPlayer.posY + 1), + MathHelper.floor_double(aPlayer.posZ))) { + if ((mSpecials & 32) != 0 && tTargetChargeItem != null) { + GT_ModHandler.chargeElectricItem(tTargetChargeItem, 20, Integer.MAX_VALUE, true, false); + } + } else { + /* + * TODO: if ((mSpecials & 16) != 0 && tTargetDechargeItem != null && + * GT_ModHandler.canUseElectricItem(tTargetDechargeItem, 10)) { if (aPlayer.worldObj.getBlock + * ((int)aPlayer.posX, (int)aPlayer.posY+1, (int)aPlayer.posZ) == Blocks.air) aPlayer.worldObj.setBlock + * ((int)aPlayer.posX, (int)aPlayer.posY+1, (int)aPlayer.posZ, GregTech_API.sBlockList[3]); + * GT_ModHandler.useElectricItem(tTargetDechargeItem, 10, aPlayer); } + */ + // } + } + } + } + + @Override + public boolean getShareTag() { + return true; + } + + @Override + @SideOnly(Side.CLIENT) + public void getSubItems(Item aItem, CreativeTabs creativeTab, List<ItemStack> outputSubItems) { + ItemStack tCharged = new ItemStack(this, 1), tUncharged = new ItemStack(this, 1, getMaxDamage()); + GT_ModHandler.chargeElectricItem(tCharged, Integer.MAX_VALUE, Integer.MAX_VALUE, true, false); + outputSubItems.add(tCharged); + outputSubItems.add(tUncharged); + } + + public boolean canProvideEnergy(ItemStack aStack) { + if ((mSpecials & 1024) != 0) setCharge(aStack); + return mChargeProvider; + } + + public Item getChargedItem(ItemStack aStack) { + if ((mSpecials & 1024) != 0) setCharge(aStack); + return this; + } + + public Item getEmptyItem(ItemStack aStack) { + if ((mSpecials & 1024) != 0) setCharge(aStack); + return this; + } + + public int getMaxCharge(ItemStack aStack) { + if ((mSpecials & 1024) != 0) setCharge(aStack); + return mCharge; + } + + public int getTier(ItemStack aStack) { + if ((mSpecials & 1024) != 0) setCharge(aStack); + return mTier; + } + + public int getTransferLimit(ItemStack aStack) { + if ((mSpecials & 1024) != 0) setCharge(aStack); + return mTransfer; + } + + @Override + public int getItemEnchantability() { + return 0; + } + + @Override + public boolean isBookEnchantable(ItemStack ingredient, ItemStack bookEnchant) { + return false; + } + + @Override + public boolean getIsRepairable(ItemStack toBeRepaired, ItemStack repairWith) { + return false; + } + + // TODO: @ForgeSubscribe + public void onEntityLivingFallEvent(LivingFallEvent event) { + if (!event.entity.worldObj.isRemote && event.entity instanceof EntityPlayer player) { + for (int i = 0; i < 4; i++) { + ItemStack armor = player.inventory.armorInventory[i]; + if (armor != null && armor.getItem() == this && (mSpecials & 2) != 0) { + int distanceFactor = (int) event.distance - 3; + int energyCost = (this.mDamageEnergyCost * distanceFactor) / 4; + if (energyCost <= GT_ModHandler + .dischargeElectricItem(armor, Integer.MAX_VALUE, Integer.MAX_VALUE, true, true, true)) { + GT_ModHandler.dischargeElectricItem(armor, energyCost, Integer.MAX_VALUE, true, false, true); + event.setCanceled(true); + break; + } + } + } + } + } + + @Override + public ISpecialArmor.ArmorProperties getProperties(EntityLivingBase player, ItemStack armor, DamageSource source, + double damage, int slotIndex) { + return new ISpecialArmor.ArmorProperties( + (source == DamageSource.fall && (mSpecials & 2) != 0) ? 10 : 0, + getBaseAbsorptionRatio() * mArmorAbsorbtionPercentage, + mDamageEnergyCost > 0 ? 25 + * GT_ModHandler.dischargeElectricItem(armor, Integer.MAX_VALUE, Integer.MAX_VALUE, true, true, true) + / mDamageEnergyCost : 0); + } + + @Override + public int getArmorDisplay(EntityPlayer player, ItemStack armor, int slotIndex) { + return (int) Math.round(20.0D * getBaseAbsorptionRatio() * mArmorAbsorbtionPercentage); + } + + @Override + public void damageArmor(EntityLivingBase entity, ItemStack itemStack, DamageSource source, int damage, + int slotIndex) { + GT_ModHandler + .dischargeElectricItem(itemStack, damage * mDamageEnergyCost, Integer.MAX_VALUE, true, false, true); + } + + private double getBaseAbsorptionRatio() { + if (mArmorAbsorbtionPercentage <= 0) return 0.00; + return switch (this.armorType) { + case 0, 3 -> 0.15; + case 1 -> 0.40; + case 2 -> 0.30; + default -> 0.00; + }; + } +} diff --git a/src/main/java/gregtech/api/items/GT_Generic_Block.java b/src/main/java/gregtech/api/items/GT_Generic_Block.java new file mode 100644 index 0000000000..7aaef6d5ca --- /dev/null +++ b/src/main/java/gregtech/api/items/GT_Generic_Block.java @@ -0,0 +1,22 @@ +package gregtech.api.items; + +import static gregtech.api.enums.GT_Values.W; + +import net.minecraft.block.Block; +import net.minecraft.block.material.Material; +import net.minecraft.item.ItemBlock; + +import cpw.mods.fml.common.registry.GameRegistry; +import gregtech.api.util.GT_LanguageManager; + +public class GT_Generic_Block extends Block { + + protected final String mUnlocalizedName; + + protected GT_Generic_Block(Class<? extends ItemBlock> aItemClass, String aName, Material aMaterial) { + super(aMaterial); + setBlockName(mUnlocalizedName = aName); + GameRegistry.registerBlock(this, aItemClass, getUnlocalizedName()); + GT_LanguageManager.addStringLocalization(getUnlocalizedName() + "." + W + ".name", "Any Sub Block of this one"); + } +} diff --git a/src/main/java/gregtech/api/items/GT_Generic_Item.java b/src/main/java/gregtech/api/items/GT_Generic_Item.java new file mode 100644 index 0000000000..a1e01c92c7 --- /dev/null +++ b/src/main/java/gregtech/api/items/GT_Generic_Item.java @@ -0,0 +1,167 @@ +package gregtech.api.items; + +import static gregtech.api.enums.Mods.GregTech; + +import java.util.List; + +import net.minecraft.block.BlockDispenser; +import net.minecraft.client.renderer.texture.IIconRegister; +import net.minecraft.dispenser.BehaviorDefaultDispenseItem; +import net.minecraft.dispenser.BehaviorProjectileDispense; +import net.minecraft.dispenser.IBlockSource; +import net.minecraft.dispenser.IPosition; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.IProjectile; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.projectile.EntityArrow; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.IIcon; +import net.minecraft.world.World; + +import cpw.mods.fml.common.registry.GameRegistry; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import gregtech.api.GregTech_API; +import gregtech.api.enums.SubTag; +import gregtech.api.interfaces.IProjectileItem; +import gregtech.api.util.GT_Config; +import gregtech.api.util.GT_LanguageManager; +import gregtech.api.util.GT_ModHandler; +import gregtech.api.util.GT_Utility; + +/** + * Extended by most Items, also used as a fallback Item, to prevent the accidental deletion when Errors occur. + */ +public class GT_Generic_Item extends Item implements IProjectileItem { + + private final String mName, mTooltip; + protected IIcon mIcon; + + public GT_Generic_Item(String aUnlocalized, String aEnglish, String aEnglishTooltip) { + super(); + mName = "gt." + aUnlocalized; + GT_LanguageManager.addStringLocalization(mName + ".name", aEnglish); + if (GT_Utility.isStringValid(aEnglishTooltip)) + GT_LanguageManager.addStringLocalization(mTooltip = mName + ".tooltip_main", aEnglishTooltip); + else mTooltip = null; + setCreativeTab(GregTech_API.TAB_GREGTECH); + GameRegistry.registerItem(this, mName, GregTech.ID); + BlockDispenser.dispenseBehaviorRegistry.putObject(this, new GT_Item_Dispense()); + } + + @Override + public final Item setUnlocalizedName(String aName) { + return this; + } + + @Override + public final String getUnlocalizedName() { + return mName; + } + + @Override + public String getUnlocalizedName(ItemStack aStack) { + return getHasSubtypes() ? mName + "." + getDamage(aStack) : mName; + } + + @Override + @SideOnly(Side.CLIENT) + public void registerIcons(IIconRegister aIconRegister) { + mIcon = aIconRegister.registerIcon(GregTech.getResourcePath(GT_Config.troll ? "troll" : mName)); + } + + @Override + public boolean doesSneakBypassUse(World aWorld, int aX, int aY, int aZ, EntityPlayer aPlayer) { + return true; + } + + @Override + public IIcon getIconFromDamage(int aMetaData) { + return mIcon; + } + + public int getTier(ItemStack aStack) { + return 0; + } + + @Override + public void addInformation(ItemStack aStack, EntityPlayer aPlayer, List<String> aList, boolean aF3_H) { + if (getMaxDamage() > 0 && !getHasSubtypes()) + aList.add((aStack.getMaxDamage() - getDamage(aStack)) + " / " + aStack.getMaxDamage()); + if (mTooltip != null) aList.add(GT_LanguageManager.getTranslation(mTooltip)); + if (GT_ModHandler.isElectricItem(aStack)) aList.add("Tier: " + getTier(aStack)); + addAdditionalToolTips(aList, aStack, aPlayer); + } + + protected void addAdditionalToolTips(List<String> aList, ItemStack aStack, EntityPlayer aPlayer) { + // + } + + @Override + public void onCreated(ItemStack aStack, World aWorld, EntityPlayer aPlayer) { + isItemStackUsable(aStack); + } + + public boolean isItemStackUsable(ItemStack aStack) { + return true; + } + + public ItemStack onDispense(IBlockSource aSource, ItemStack aStack) { + EnumFacing enumfacing = BlockDispenser.func_149937_b(aSource.getBlockMetadata()); + IPosition iposition = BlockDispenser.func_149939_a(aSource); + ItemStack itemstack1 = aStack.splitStack(1); + BehaviorDefaultDispenseItem.doDispense(aSource.getWorld(), itemstack1, 6, enumfacing, iposition); + return aStack; + } + + @Override + public EntityArrow getProjectile(SubTag aProjectileType, ItemStack aStack, World aWorld, double aX, double aY, + double aZ) { + return null; + } + + @Override + public EntityArrow getProjectile(SubTag aProjectileType, ItemStack aStack, World aWorld, EntityLivingBase aEntity, + float aSpeed) { + return null; + } + + @Override + public boolean hasProjectile(SubTag aProjectileType, ItemStack aStack) { + return false; + } + + @Override + public ItemStack getContainerItem(ItemStack aStack) { + return null; + } + + @Override + public boolean hasContainerItem(ItemStack aStack) { + return getContainerItem(aStack) != null; + } + + @Deprecated + public String trans(String aKey, String aEnglish) { + return transItem(aKey, aEnglish); + } + + public String transItem(String aKey, String aEnglish) { + return GT_LanguageManager.addStringLocalization("Item_DESCRIPTION_Index_" + aKey, aEnglish); + } + + public static class GT_Item_Dispense extends BehaviorProjectileDispense { + + @Override + public ItemStack dispenseStack(IBlockSource aSource, ItemStack aStack) { + return ((GT_Generic_Item) aStack.getItem()).onDispense(aSource, aStack); + } + + @Override + protected IProjectile getProjectileEntity(World aWorld, IPosition aPosition) { + return null; + } + } +} diff --git a/src/main/java/gregtech/api/items/GT_MetaBase_Item.java b/src/main/java/gregtech/api/items/GT_MetaBase_Item.java new file mode 100644 index 0000000000..481c0b5a08 --- /dev/null +++ b/src/main/java/gregtech/api/items/GT_MetaBase_Item.java @@ -0,0 +1,622 @@ +package gregtech.api.items; + +import static gregtech.api.enums.GT_Values.D1; +import static gregtech.api.enums.GT_Values.V; +import static gregtech.api.util.GT_Utility.formatNumbers; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; + +import net.minecraft.dispenser.IBlockSource; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.projectile.EntityArrow; +import net.minecraft.inventory.Container; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.world.World; +import net.minecraftforge.common.util.ForgeDirection; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.IFluidContainerItem; + +import gregtech.api.enums.SubTag; +import gregtech.api.interfaces.IItemBehaviour; +import gregtech.api.util.GT_LanguageManager; +import gregtech.api.util.GT_Log; +import gregtech.api.util.GT_ModHandler; +import gregtech.api.util.GT_Utility; +import ic2.api.item.ElectricItem; +import ic2.api.item.IElectricItem; +import ic2.api.item.IElectricItemManager; +import ic2.api.item.ISpecialElectricItem; + +public abstract class GT_MetaBase_Item extends GT_Generic_Item + implements ISpecialElectricItem, IElectricItemManager, IFluidContainerItem { + + /* ---------- CONSTRUCTOR AND MEMBER VARIABLES ---------- */ + private final ConcurrentHashMap<Short, ArrayList<IItemBehaviour<GT_MetaBase_Item>>> mItemBehaviors = new ConcurrentHashMap<>(); + + /** + * Creates the Item using these Parameters. + * + * @param aUnlocalized The Unlocalized Name of this Item. + */ + public GT_MetaBase_Item(String aUnlocalized) { + super(aUnlocalized, "Generated Item", null); + setHasSubtypes(true); + setMaxDamage(0); + } + + /** + * Adds a special Item Behaviour to the Item. + * <p/> + * Note: the boolean Behaviours sometimes won't be executed if another boolean Behaviour returned true before. + * + * @param aMetaValue the Meta Value of the Item you want to add it to. [0 - 32765] + * @param aBehavior the Click Behavior you want to add. + * @return the Item itself for convenience in constructing. + */ + public final GT_MetaBase_Item addItemBehavior(int aMetaValue, IItemBehaviour<GT_MetaBase_Item> aBehavior) { + if (aMetaValue < 0 || aMetaValue >= 32766 || aBehavior == null) return this; + ArrayList<IItemBehaviour<GT_MetaBase_Item>> tList = mItemBehaviors + .computeIfAbsent((short) aMetaValue, k -> new ArrayList<>(1)); + tList.add(aBehavior); + return this; + } + + public abstract Long[] getElectricStats(ItemStack aStack); + + public abstract Long[] getFluidContainerStats(ItemStack aStack); + + @Override + public boolean hasProjectile(SubTag aProjectileType, ItemStack aStack) { + ArrayList<IItemBehaviour<GT_MetaBase_Item>> tList = mItemBehaviors.get((short) getDamage(aStack)); + if (tList != null) for (IItemBehaviour<GT_MetaBase_Item> tBehavior : tList) + if (tBehavior.hasProjectile(this, aProjectileType, aStack)) return true; + return super.hasProjectile(aProjectileType, aStack); + } + + @Override + public EntityArrow getProjectile(SubTag aProjectileType, ItemStack aStack, World aWorld, double aX, double aY, + double aZ) { + ArrayList<IItemBehaviour<GT_MetaBase_Item>> tList = mItemBehaviors.get((short) getDamage(aStack)); + if (tList != null) for (IItemBehaviour<GT_MetaBase_Item> tBehavior : tList) { + EntityArrow rArrow = tBehavior.getProjectile(this, aProjectileType, aStack, aWorld, aX, aY, aZ); + if (rArrow != null) return rArrow; + } + return super.getProjectile(aProjectileType, aStack, aWorld, aX, aY, aZ); + } + + @Override + public EntityArrow getProjectile(SubTag aProjectileType, ItemStack aStack, World aWorld, EntityLivingBase aEntity, + float aSpeed) { + ArrayList<IItemBehaviour<GT_MetaBase_Item>> tList = mItemBehaviors.get((short) getDamage(aStack)); + if (tList != null) for (IItemBehaviour<GT_MetaBase_Item> tBehavior : tList) { + EntityArrow rArrow = tBehavior.getProjectile(this, aProjectileType, aStack, aWorld, aEntity, aSpeed); + if (rArrow != null) return rArrow; + } + return super.getProjectile(aProjectileType, aStack, aWorld, aEntity, aSpeed); + } + + @Override + public ItemStack onDispense(IBlockSource aSource, ItemStack aStack) { + ArrayList<IItemBehaviour<GT_MetaBase_Item>> tList = mItemBehaviors.get((short) getDamage(aStack)); + if (tList != null) for (IItemBehaviour<GT_MetaBase_Item> tBehavior : tList) + if (tBehavior.canDispense(this, aSource, aStack)) return tBehavior.onDispense(this, aSource, aStack); + return super.onDispense(aSource, aStack); + } + + @Override + public boolean isItemStackUsable(ItemStack aStack) { + ArrayList<IItemBehaviour<GT_MetaBase_Item>> tList = mItemBehaviors.get((short) getDamage(aStack)); + if (tList != null) for (IItemBehaviour<GT_MetaBase_Item> tBehavior : tList) + if (!tBehavior.isItemStackUsable(this, aStack)) return false; + return super.isItemStackUsable(aStack); + } + + @Override + public boolean onLeftClickEntity(ItemStack aStack, EntityPlayer aPlayer, Entity aEntity) { + use(aStack, 0, aPlayer); + isItemStackUsable(aStack); + ArrayList<IItemBehaviour<GT_MetaBase_Item>> tList = mItemBehaviors.get((short) getDamage(aStack)); + try { + if (tList != null) for (IItemBehaviour<GT_MetaBase_Item> tBehavior : tList) + if (tBehavior.onLeftClickEntity(this, aStack, aPlayer, aEntity)) { + if (aStack.stackSize <= 0) aPlayer.destroyCurrentEquippedItem(); + return true; + } + if (aStack.stackSize <= 0) { + aPlayer.destroyCurrentEquippedItem(); + return false; + } + } catch (Throwable e) { + if (D1) e.printStackTrace(GT_Log.err); + } + return false; + } + + @Override + public boolean onItemUse(ItemStack aStack, EntityPlayer aPlayer, World aWorld, int aX, int aY, int aZ, + int ordinalSide, float hitX, float hitY, float hitZ) { + use(aStack, 0, aPlayer); + isItemStackUsable(aStack); + ArrayList<IItemBehaviour<GT_MetaBase_Item>> tList = mItemBehaviors.get((short) getDamage(aStack)); + try { + if (tList != null) for (IItemBehaviour<GT_MetaBase_Item> tBehavior : tList) + if (tBehavior.onItemUse(this, aStack, aPlayer, aWorld, aX, aY, aZ, ordinalSide, hitX, hitY, hitZ)) { + if (aStack.stackSize <= 0) aPlayer.destroyCurrentEquippedItem(); + return true; + } + if (aStack.stackSize <= 0) { + aPlayer.destroyCurrentEquippedItem(); + return false; + } + } catch (Throwable e) { + if (D1) e.printStackTrace(GT_Log.err); + } + return false; + } + + @Override + public boolean onItemUseFirst(ItemStack aStack, EntityPlayer aPlayer, World aWorld, int aX, int aY, int aZ, + int ordinalSide, float hitX, float hitY, float hitZ) { + use(aStack, 0, aPlayer); + isItemStackUsable(aStack); + ArrayList<IItemBehaviour<GT_MetaBase_Item>> tList = mItemBehaviors.get((short) getDamage(aStack)); + try { + if (tList != null) for (IItemBehaviour<GT_MetaBase_Item> tBehavior : tList) if (tBehavior.onItemUseFirst( + this, + aStack, + aPlayer, + aWorld, + aX, + aY, + aZ, + ForgeDirection.getOrientation(ordinalSide), + hitX, + hitY, + hitZ)) { + if (aStack.stackSize <= 0) aPlayer.destroyCurrentEquippedItem(); + return true; + } + if (aStack.stackSize <= 0) { + aPlayer.destroyCurrentEquippedItem(); + return false; + } + } catch (Throwable e) { + if (D1) e.printStackTrace(GT_Log.err); + } + return false; + } + + @Override + public ItemStack onItemRightClick(ItemStack aStack, World aWorld, EntityPlayer aPlayer) { + use(aStack, 0, aPlayer); + isItemStackUsable(aStack); + ArrayList<IItemBehaviour<GT_MetaBase_Item>> tList = mItemBehaviors.get((short) getDamage(aStack)); + try { + if (tList != null) for (IItemBehaviour<GT_MetaBase_Item> tBehavior : tList) + aStack = tBehavior.onItemRightClick(this, aStack, aWorld, aPlayer); + } catch (Throwable e) { + if (D1) e.printStackTrace(GT_Log.err); + } + return aStack; + } + + @Override + public final void addInformation(ItemStack aStack, EntityPlayer aPlayer, List<String> aList, boolean aF3_H) { + String tKey = getUnlocalizedName(aStack) + ".tooltip"; + String[] tStrings = GT_LanguageManager.getTranslation(tKey) + .split("/n "); + for (String tString : tStrings) + if (GT_Utility.isStringValid(tString) && !tKey.equals(tString)) aList.add(tString); + + Long[] tStats = getElectricStats(aStack); + if (tStats != null) { + if (tStats[3] > 0) { + aList.add( + EnumChatFormatting.AQUA + String.format( + transItem("009", "Contains %s EU Tier: %s"), + formatNumbers(tStats[3]), + "" + (tStats[2] >= 0 ? tStats[2] : 0)) + EnumChatFormatting.GRAY); + } else { + long tCharge = getRealCharge(aStack); + if (tStats[3] == -2 && tCharge <= 0) { + aList.add( + EnumChatFormatting.AQUA + transItem("010", "Empty. You should recycle it properly.") + + EnumChatFormatting.GRAY); + } else { + aList.add( + EnumChatFormatting.AQUA + + String.format( + transItem("011", "%s / %s EU - Voltage: %s"), + formatNumbers(tCharge), + formatNumbers(Math.abs(tStats[0])), + "" + V[(int) (tStats[2] >= 0 ? tStats[2] < V.length ? tStats[2] : V.length - 1 : 1)]) + + EnumChatFormatting.GRAY); + } + } + } + + tStats = getFluidContainerStats(aStack); + if (tStats != null && tStats[0] > 0) { + FluidStack tFluid = getFluidContent(aStack); + aList.add( + EnumChatFormatting.BLUE + ((tFluid == null ? transItem("012", "No Fluids Contained") + : GT_Utility.getFluidName(tFluid, true))) + EnumChatFormatting.GRAY); + aList.add( + EnumChatFormatting.BLUE + String.format( + transItem("013", "%sL / %sL"), + "" + (tFluid == null ? 0 : formatNumbers(tFluid.amount)), + "" + formatNumbers(tStats[0])) + EnumChatFormatting.GRAY); + } + + ArrayList<IItemBehaviour<GT_MetaBase_Item>> tList = mItemBehaviors.get((short) getDamage(aStack)); + if (tList != null) for (IItemBehaviour<GT_MetaBase_Item> tBehavior : tList) + aList = tBehavior.getAdditionalToolTips(this, aList, aStack); + + addAdditionalToolTips(aList, aStack, aPlayer); + } + + @Override + public void onUpdate(ItemStack aStack, World aWorld, Entity aPlayer, int aTimer, boolean aIsInHand) { + ArrayList<IItemBehaviour<GT_MetaBase_Item>> tList = mItemBehaviors.get((short) getDamage(aStack)); + if (tList != null) for (IItemBehaviour<GT_MetaBase_Item> tBehavior : tList) + tBehavior.onUpdate(this, aStack, aWorld, aPlayer, aTimer, aIsInHand); + } + + @Override + public final boolean canProvideEnergy(ItemStack aStack) { + Long[] tStats = getElectricStats(aStack); + if (tStats == null) return false; + return tStats[3] > 0 || (aStack.stackSize == 1 && (tStats[3] == -2 || tStats[3] == -3)); + } + + @Override + public final double getMaxCharge(ItemStack aStack) { + Long[] tStats = getElectricStats(aStack); + if (tStats == null) return 0; + return Math.abs(tStats[0]); + } + + @Override + public final double getTransferLimit(ItemStack aStack) { + Long[] tStats = getElectricStats(aStack); + if (tStats == null) return 0; + return Math.max(tStats[1], tStats[3]); + } + + @Override + public final double charge(ItemStack aStack, double aCharge, int aTier, boolean aIgnoreTransferLimit, + boolean aSimulate) { + Long[] tStats = getElectricStats(aStack); + if (tStats == null || tStats[2] > aTier + || !(tStats[3] == -1 || tStats[3] == -3 || (tStats[3] < 0 && aCharge == Integer.MAX_VALUE)) + || aStack.stackSize != 1) return 0; + long tTransfer = aIgnoreTransferLimit ? (long) aCharge : Math.min(tStats[1], (long) aCharge); + long tChargeBefore = getRealCharge(aStack), tNewCharge = Math.min( + Math.abs(tStats[0]), + Long.MAX_VALUE - tTransfer >= tChargeBefore ? tChargeBefore + tTransfer : Long.MAX_VALUE); + if (!aSimulate) setCharge(aStack, tNewCharge); + return tNewCharge - tChargeBefore; + } + + @Override + public final double discharge(ItemStack aStack, double aCharge, int aTier, boolean aIgnoreTransferLimit, + boolean aBatteryAlike, boolean aSimulate) { + Long[] tStats = getElectricStats(aStack); + if (tStats == null || tStats[2] > aTier) return 0; + if (aBatteryAlike && !canProvideEnergy(aStack)) return 0; + if (tStats[3] > 0) { + if (aCharge < tStats[3] || aStack.stackSize < 1) return 0; + if (!aSimulate) aStack.stackSize--; + return tStats[3]; + } + long tChargeBefore = getRealCharge(aStack), tNewCharge = Math + .max(0, tChargeBefore - (aIgnoreTransferLimit ? (long) aCharge : Math.min(tStats[1], (long) aCharge))); + if (!aSimulate) setCharge(aStack, tNewCharge); + return tChargeBefore - tNewCharge; + } + + @Override + public final double getCharge(ItemStack aStack) { + return getRealCharge(aStack); + } + + @Override + public final boolean canUse(ItemStack aStack, double aAmount) { + return getRealCharge(aStack) >= aAmount; + } + + @Override + public final boolean use(ItemStack aStack, double aAmount, EntityLivingBase aPlayer) { + chargeFromArmor(aStack, aPlayer); + if (aPlayer instanceof EntityPlayer && ((EntityPlayer) aPlayer).capabilities.isCreativeMode) return true; + double tTransfer = discharge(aStack, aAmount, Integer.MAX_VALUE, true, false, true); + if (Math.abs(tTransfer - aAmount) < .0000001) { + discharge(aStack, aAmount, Integer.MAX_VALUE, true, false, false); + chargeFromArmor(aStack, aPlayer); + return true; + } + discharge(aStack, aAmount, Integer.MAX_VALUE, true, false, false); + chargeFromArmor(aStack, aPlayer); + return false; + } + + @Override + public final void chargeFromArmor(ItemStack aStack, EntityLivingBase aPlayer) { + if (aPlayer == null || aPlayer.worldObj.isRemote) return; + for (int i = 1; i < 5; i++) { + ItemStack tArmor = aPlayer.getEquipmentInSlot(i); + if (GT_ModHandler.isElectricItem(tArmor)) { + IElectricItem tArmorItem = (IElectricItem) tArmor.getItem(); + if (tArmorItem.canProvideEnergy(tArmor) && tArmorItem.getTier(tArmor) >= getTier(aStack)) { + double tCharge = ElectricItem.manager.discharge( + tArmor, + charge(aStack, Integer.MAX_VALUE - 1, Integer.MAX_VALUE, true, true), + Integer.MAX_VALUE, + true, + true, + false); + if (tCharge > 0) { + charge(aStack, tCharge, Integer.MAX_VALUE, true, false); + if (aPlayer instanceof EntityPlayer) { + Container tContainer = ((EntityPlayer) aPlayer).openContainer; + if (tContainer != null) tContainer.detectAndSendChanges(); + } + } + } + } + } + } + + /* + * @Override public final int getMaxCharge(ItemStack aStack) { Long[] tStats = getElectricStats(aStack); if (tStats + * == null) return 0; return (int)Math.abs(tStats[0]); } + * @Override public final int getTransferLimit(ItemStack aStack) { Long[] tStats = getElectricStats(aStack); if + * (tStats == null) return 0; return (int)Math.max(tStats[1], tStats[3]); } + * @Override public final int charge(ItemStack aStack, int aCharge, int aTier, boolean aIgnoreTransferLimit, boolean + * aSimulate) { Long[] tStats = getElectricStats(aStack); if (tStats == null || tStats[2] > aTier || !(tStats[3] == + * -1 || tStats[3] == -3 || (tStats[3] < 0 && aCharge == Integer.MAX_VALUE)) || aStack.stackSize != 1) return 0; + * long tChargeBefore = getRealCharge(aStack), tNewCharge = + * aCharge==Integer.MAX_VALUE?Long.MAX_VALUE:Math.min(Math.abs(tStats[0]), tChargeBefore + + * (aIgnoreTransferLimit?aCharge:Math.min(tStats[1], aCharge))); if (!aSimulate) setCharge(aStack, tNewCharge); + * return (int)(tNewCharge-tChargeBefore); } + * @Override public final int discharge(ItemStack aStack, int aCharge, int aTier, boolean aIgnoreTransferLimit, + * boolean aSimulate) { Long[] tStats = getElectricStats(aStack); if (tStats == null || tStats[2] > aTier) return 0; + * if (tStats[3] > 0) { if (aCharge < tStats[3] || aStack.stackSize < 1) return 0; if (!aSimulate) + * aStack.stackSize--; return (int)(long)tStats[3]; } long tChargeBefore = getRealCharge(aStack), tNewCharge = + * Math.max(0, tChargeBefore - (aIgnoreTransferLimit?aCharge:Math.min(tStats[1], aCharge))); if (!aSimulate) + * setCharge(aStack, tNewCharge); return (int)(tChargeBefore-tNewCharge); } + * @Override public final int getCharge(ItemStack aStack) { return (int)Math.min(Integer.MAX_VALUE, + * getRealCharge(aStack)); } + * @Override public final boolean canUse(ItemStack aStack, int aAmount) { return getRealCharge(aStack) >= aAmount; } + * @Override public final boolean use(ItemStack aStack, int aAmount, EntityLivingBase aPlayer) { + * chargeFromArmor(aStack, aPlayer); if (aPlayer instanceof EntityPlayer && + * ((EntityPlayer)aPlayer).capabilities.isCreativeMode) return true; int tTransfer = discharge(aStack, aAmount, + * Integer.MAX_VALUE, true, true); if (tTransfer == aAmount) { discharge(aStack, aAmount, Integer.MAX_VALUE, true, + * false); chargeFromArmor(aStack, aPlayer); return true; } discharge(aStack, aAmount, Integer.MAX_VALUE, true, + * false); chargeFromArmor(aStack, aPlayer); return false; } + * @Override public final void chargeFromArmor(ItemStack aStack, EntityLivingBase aPlayer) { if (aPlayer == null || + * aPlayer.worldObj.isRemote) return; for (int i = 1; i < 5; i++) { ItemStack tArmor = + * aPlayer.getEquipmentInSlot(i); if (GT_ModHandler.isElectricItem(tArmor)) { IElectricItem tArmorItem = + * (IElectricItem)tArmor.getItem(); if (tArmorItem.canProvideEnergy(tArmor) && tArmorItem.getTier(tArmor) >= + * getTier(aStack)) { int tCharge = ElectricItem.manager.discharge(tArmor, charge(aStack, Integer.MAX_VALUE-1, + * Integer.MAX_VALUE, true, true), Integer.MAX_VALUE, true, false); if (tCharge > 0) { charge(aStack, tCharge, + * Integer.MAX_VALUE, true, false); if (aPlayer instanceof EntityPlayer) { Container tContainer = + * ((EntityPlayer)aPlayer).openContainer; if (tContainer != null) tContainer.detectAndSendChanges(); } } } } } } + */ + public final long getRealCharge(ItemStack aStack) { + Long[] tStats = getElectricStats(aStack); + if (tStats == null) return 0; + if (tStats[3] > 0) return (int) (long) tStats[3]; + NBTTagCompound tNBT = aStack.getTagCompound(); + return tNBT == null ? 0 : tNBT.getLong("GT.ItemCharge"); + } + + public final boolean setCharge(ItemStack aStack, long aCharge) { + Long[] tStats = getElectricStats(aStack); + if (tStats == null || tStats[3] > 0) return false; + NBTTagCompound tNBT = aStack.getTagCompound(); + if (tNBT == null) tNBT = new NBTTagCompound(); + tNBT.removeTag("GT.ItemCharge"); + aCharge = Math.min(tStats[0] < 0 ? Math.abs(tStats[0] / 2) : aCharge, Math.abs(tStats[0])); + if (aCharge > 0) { + aStack.setItemDamage(getChargedMetaData(aStack)); + tNBT.setLong("GT.ItemCharge", aCharge); + } else { + aStack.setItemDamage(getEmptyMetaData(aStack)); + } + if (tNBT.hasNoTags()) aStack.setTagCompound(null); + else aStack.setTagCompound(tNBT); + isItemStackUsable(aStack); + return true; + } + + public short getChargedMetaData(ItemStack aStack) { + return (short) aStack.getItemDamage(); + } + + public short getEmptyMetaData(ItemStack aStack) { + return (short) aStack.getItemDamage(); + } + + @Override + public FluidStack getFluid(ItemStack aStack) { + return getFluidContent(aStack); + } + + @Override + public int getCapacity(ItemStack aStack) { + Long[] tStats = getFluidContainerStats(aStack); + return tStats == null ? 0 : (int) Math.max(0, tStats[0]); + } + + @Override + public int fill(ItemStack aStack, FluidStack aFluid, boolean doFill) { + if (aStack == null || aStack.stackSize != 1) return 0; + + ItemStack tStack = GT_Utility.fillFluidContainer(aFluid, aStack, false, false); + if (tStack != null) { + aStack.setItemDamage(tStack.getItemDamage()); + aStack.func_150996_a(tStack.getItem()); + return GT_Utility.getFluidForFilledItem(tStack, false).amount; + } + + Long[] tStats = getFluidContainerStats(aStack); + if (tStats == null || tStats[0] <= 0 + || aFluid == null + || aFluid.getFluid() + .getID() <= 0 + || aFluid.amount <= 0) return 0; + + FluidStack tFluid = getFluidContent(aStack); + + if (tFluid == null || tFluid.getFluid() + .getID() <= 0) { + if (aFluid.amount <= tStats[0]) { + if (doFill) { + setFluidContent(aStack, aFluid); + } + return aFluid.amount; + } + if (doFill) { + tFluid = aFluid.copy(); + tFluid.amount = (int) (long) tStats[0]; + setFluidContent(aStack, tFluid); + } + return (int) (long) tStats[0]; + } + + if (!tFluid.isFluidEqual(aFluid)) return 0; + + int space = (int) (long) tStats[0] - tFluid.amount; + if (aFluid.amount <= space) { + if (doFill) { + tFluid.amount += aFluid.amount; + setFluidContent(aStack, tFluid); + } + return aFluid.amount; + } + if (doFill) { + tFluid.amount = (int) (long) tStats[0]; + setFluidContent(aStack, tFluid); + } + return space; + } + + @Override + public FluidStack drain(ItemStack aStack, int maxDrain, boolean doDrain) { + if (aStack == null || aStack.stackSize != 1) return null; + + FluidStack tFluid = GT_Utility.getFluidForFilledItem(aStack, false); + if (tFluid != null && maxDrain >= tFluid.amount) { + ItemStack tStack = GT_Utility.getContainerItem(aStack, false); + if (tStack == null) { + if (doDrain) aStack.stackSize = 0; + return tFluid; + } + if (doDrain) { + aStack.setItemDamage(tStack.getItemDamage()); + aStack.func_150996_a(tStack.getItem()); + } + return tFluid; + } + + Long[] tStats = getFluidContainerStats(aStack); + if (tStats == null || tStats[0] <= 0) return null; + + tFluid = getFluidContent(aStack); + if (tFluid == null) return null; + + int used = maxDrain; + if (tFluid.amount < used) used = tFluid.amount; + if (doDrain) { + tFluid.amount -= used; + setFluidContent(aStack, tFluid); + } + + FluidStack drained = tFluid.copy(); + drained.amount = used; + return drained; + } + + public FluidStack getFluidContent(ItemStack aStack) { + Long[] tStats = getFluidContainerStats(aStack); + if (tStats == null || tStats[0] <= 0) return GT_Utility.getFluidForFilledItem(aStack, false); + NBTTagCompound tNBT = aStack.getTagCompound(); + return tNBT == null ? null : FluidStack.loadFluidStackFromNBT(tNBT.getCompoundTag("GT.FluidContent")); + } + + public void setFluidContent(ItemStack aStack, FluidStack aFluid) { + NBTTagCompound tNBT = aStack.getTagCompound(); + if (tNBT == null) tNBT = new NBTTagCompound(); + else tNBT.removeTag("GT.FluidContent"); + if (aFluid != null && aFluid.amount > 0) + tNBT.setTag("GT.FluidContent", aFluid.writeToNBT(new NBTTagCompound())); + if (tNBT.hasNoTags()) aStack.setTagCompound(null); + else aStack.setTagCompound(tNBT); + isItemStackUsable(aStack); + } + + @Override + public int getItemStackLimit(ItemStack aStack) { + Long[] tStats = getElectricStats(aStack); + if (tStats != null && (tStats[3] == -1 || tStats[3] == -2 || tStats[3] == -3) && getRealCharge(aStack) > 0) + return 1; + tStats = getFluidContainerStats(aStack); + if (tStats != null) return (int) (long) tStats[1]; + if (getDamage(aStack) == 32763) return 1; + return 64; + } + + @Override + public final Item getChargedItem(ItemStack itemStack) { + return this; + } + + @Override + public final Item getEmptyItem(ItemStack itemStack) { + return this; + } + + @Override + public final int getTier(ItemStack aStack) { + Long[] tStats = getElectricStats(aStack); + return (int) (tStats == null ? Integer.MAX_VALUE : tStats[2]); + } + + @Override + public final String getToolTip(ItemStack aStack) { + return null; + } // This has its own ToolTip Handler, no need to let the IC2 Handler screw us up at this Point + + @Override + public final IElectricItemManager getManager(ItemStack aStack) { + return this; + } // We are our own Manager + + @Override + public final boolean getShareTag() { + return true; + } // just to be sure. + + @Override + public int getItemEnchantability() { + return 0; + } + + @Override + public boolean isBookEnchantable(ItemStack aStack, ItemStack aBook) { + return false; + } + + @Override + public boolean getIsRepairable(ItemStack aStack, ItemStack aMaterial) { + return false; + } +} diff --git a/src/main/java/gregtech/api/items/GT_MetaGenerated_Item.java b/src/main/java/gregtech/api/items/GT_MetaGenerated_Item.java new file mode 100644 index 0000000000..465c4bc9d6 --- /dev/null +++ b/src/main/java/gregtech/api/items/GT_MetaGenerated_Item.java @@ -0,0 +1,415 @@ +package gregtech.api.items; + +import static gregtech.api.enums.GT_Values.D1; +import static gregtech.api.enums.Mods.AppleCore; +import static gregtech.api.enums.Mods.GregTech; +import static gregtech.api.recipe.RecipeMaps.cannerRecipes; +import static gregtech.api.util.GT_RecipeBuilder.SECONDS; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.BitSet; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; + +import net.minecraft.client.renderer.texture.IIconRegister; +import net.minecraft.creativetab.CreativeTabs; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumAction; +import net.minecraft.item.Item; +import net.minecraft.item.ItemFood; +import net.minecraft.item.ItemStack; +import net.minecraft.util.IIcon; +import net.minecraft.world.World; + +import cpw.mods.fml.common.Optional; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import gregtech.api.GregTech_API; +import gregtech.api.enums.GT_Values; +import gregtech.api.enums.ItemList; +import gregtech.api.enums.Materials; +import gregtech.api.enums.Mods; +import gregtech.api.enums.SubTag; +import gregtech.api.enums.TC_Aspects.TC_AspectStack; +import gregtech.api.interfaces.IFoodStat; +import gregtech.api.interfaces.IGT_ItemWithMaterialRenderer; +import gregtech.api.interfaces.IIconContainer; +import gregtech.api.interfaces.IItemBehaviour; +import gregtech.api.interfaces.IItemContainer; +import gregtech.api.objects.ItemData; +import gregtech.api.util.GT_Config; +import gregtech.api.util.GT_LanguageManager; +import gregtech.api.util.GT_OreDictUnificator; +import gregtech.api.util.GT_Utility; +import gregtech.common.render.items.GT_GeneratedMaterial_Renderer; +import squeek.applecore.api.food.FoodValues; +import squeek.applecore.api.food.IEdible; +import squeek.applecore.api.food.ItemFoodProxy; + +/** + * @author Gregorius Techneticies + * <p/> + * One Item for everything! + * <p/> + * This brilliant Item Class is used for automatically generating all possible variations of Material Items, + * like Dusts, Ingots, Gems, Plates and similar. It saves me a ton of work, when adding Items, because I always + * have to make a new Item SubType for each OreDict Prefix, when adding a new Material. + * <p/> + * As you can see, up to 32766 Items can be generated using this Class. And the last 766 Items can be custom + * defined, just to save space and MetaData. + * <p/> + * These Items can also have special RightClick abilities, electric Charge or even be set to become a Food alike + * Item. + */ +@Optional.Interface(iface = "squeek.applecore.api.food.IEdible", modid = Mods.Names.APPLE_CORE) +public abstract class GT_MetaGenerated_Item extends GT_MetaBase_Item implements IGT_ItemWithMaterialRenderer, IEdible { + + /** + * All instances of this Item Class are listed here. This gets used to register the Renderer to all Items of this + * Type, if useStandardMetaItemRenderer() returns true. + * <p/> + * You can also use the unlocalized Name gotten from getUnlocalizedName() as Key if you want to get a specific Item. + */ + public static final ConcurrentHashMap<String, GT_MetaGenerated_Item> sInstances = new ConcurrentHashMap<>(); + + /* ---------- CONSTRUCTOR AND MEMBER VARIABLES ---------- */ + + public final short mOffset, mItemAmount; + public final BitSet mEnabledItems; + public final BitSet mVisibleItems; + public final IIcon[][] mIconList; + + public final ConcurrentHashMap<Short, IFoodStat> mFoodStats = new ConcurrentHashMap<>(); + public final ConcurrentHashMap<Short, Long[]> mElectricStats = new ConcurrentHashMap<>(); + public final ConcurrentHashMap<Short, Long[]> mFluidContainerStats = new ConcurrentHashMap<>(); + public final ConcurrentHashMap<Short, Short> mBurnValues = new ConcurrentHashMap<>(); + + /** + * Creates the Item using these Parameters. + * + * @param aUnlocalized The Unlocalized Name of this Item. + */ + public GT_MetaGenerated_Item(String aUnlocalized, short aOffset, short aItemAmount) { + super(aUnlocalized); + setCreativeTab(GregTech_API.TAB_GREGTECH_MATERIALS); + setHasSubtypes(true); + setMaxDamage(0); + mEnabledItems = new BitSet(aItemAmount); + mVisibleItems = new BitSet(aItemAmount); + + mOffset = (short) Math.min(32766, aOffset); + mItemAmount = (short) Math.min(aItemAmount, 32766 - mOffset); + mIconList = new IIcon[aItemAmount][1]; + + sInstances.put(getUnlocalizedName(), this); + } + + /** + * This adds a Custom Item to the ending Range. + * + * @param aID The Id of the assigned Item [0 - mItemAmount] (The MetaData gets auto-shifted by +mOffset) + * @param aEnglish The Default Localized Name of the created Item + * @param aToolTip The Default ToolTip of the created Item, you can also insert null for having no ToolTip + * @param aRandomData The OreDict Names you want to give the Item. Also used for TC Aspects and some other things. + * @return An ItemStack containing the newly created Item. + */ + public final ItemStack addItem(int aID, String aEnglish, String aToolTip, Object... aRandomData) { + if (aToolTip == null) aToolTip = ""; + if (aID >= 0 && aID < mItemAmount) { + ItemStack rStack = new ItemStack(this, 1, mOffset + aID); + if (mEnabledItems.get(aID)) { + throw new IllegalArgumentException( + String.format("ID %s is already reserved for %s!", aID, rStack.getDisplayName())); + } + mEnabledItems.set(aID); + mVisibleItems.set(aID); + GT_LanguageManager.addStringLocalization(getUnlocalizedName(rStack) + ".name", aEnglish); + GT_LanguageManager.addStringLocalization(getUnlocalizedName(rStack) + ".tooltip", aToolTip); + List<TC_AspectStack> tAspects = new ArrayList<>(); + // Important Stuff to do first + for (Object tRandomData : aRandomData) if (tRandomData instanceof SubTag) { + if (tRandomData == SubTag.INVISIBLE) { + mVisibleItems.set(aID, false); + continue; + } + if (tRandomData == SubTag.NO_UNIFICATION) { + GT_OreDictUnificator.addToBlacklist(rStack); + } + } + // now check for the rest + for (Object tRandomData : aRandomData) if (tRandomData != null) { + boolean tUseOreDict = true; + if (tRandomData instanceof IFoodStat) { + setFoodBehavior(mOffset + aID, (IFoodStat) tRandomData); + if (((IFoodStat) tRandomData).getFoodAction(this, rStack) == EnumAction.eat) { + int tFoodValue = ((IFoodStat) tRandomData).getFoodLevel(this, rStack, null); + if (tFoodValue > 0) { + GT_Values.RA.stdBuilder() + .itemInputs(rStack, ItemList.IC2_Food_Can_Empty.get(tFoodValue)) + .itemOutputs( + ((IFoodStat) tRandomData).isRotten(this, rStack, null) + ? ItemList.IC2_Food_Can_Spoiled.get(tFoodValue) + : ItemList.IC2_Food_Can_Filled.get(tFoodValue)) + .duration(tFoodValue * 5 * SECONDS) + .eut(1) + .addTo(cannerRecipes); + } + } + tUseOreDict = false; + } + if (tRandomData instanceof IItemBehaviour) { + // The cast below from is not safe. If you know how to make it safe, please do. + // noinspection unchecked + addItemBehavior(mOffset + aID, (IItemBehaviour<GT_MetaBase_Item>) tRandomData); + tUseOreDict = false; + } + if (tRandomData instanceof IItemContainer) { + ((IItemContainer) tRandomData).set(rStack); + tUseOreDict = false; + } + if (tRandomData instanceof SubTag) { + continue; + } + if (tRandomData instanceof TC_AspectStack) { + ((TC_AspectStack) tRandomData).addToAspectList(tAspects); + continue; + } + if (tRandomData instanceof ItemData) { + if (GT_Utility.isStringValid(tRandomData)) GT_OreDictUnificator.registerOre(tRandomData, rStack); + else GT_OreDictUnificator.addItemData(rStack, (ItemData) tRandomData); + continue; + } + if (tUseOreDict) { + GT_OreDictUnificator.registerOre(tRandomData, rStack); + } + } + if (GregTech_API.sThaumcraftCompat != null) + GregTech_API.sThaumcraftCompat.registerThaumcraftAspectsToItem(rStack, tAspects, false); + return rStack; + } + return null; + } + + /** + * Sets a Food Behavior for the Item. + * + * @param aMetaValue the Meta Value of the Item you want to set it to. [0 - 32765] + * @param aFoodBehavior the Food Behavior you want to add. + * @return the Item itself for convenience in constructing. + */ + public final GT_MetaGenerated_Item setFoodBehavior(int aMetaValue, IFoodStat aFoodBehavior) { + if (aMetaValue < 0 || aMetaValue >= mOffset + mEnabledItems.length()) return this; + if (aFoodBehavior == null) mFoodStats.remove((short) aMetaValue); + else mFoodStats.put((short) aMetaValue, aFoodBehavior); + return this; + } + + /** + * Sets the Furnace Burn Value for the Item. + * + * @param aMetaValue the Meta Value of the Item you want to set it to. [0 - 32765] + * @param aValue 200 = 1 Burn Process = 500 EU, max = 32767 (that is 81917.5 EU) + * @return the Item itself for convenience in constructing. + */ + public final GT_MetaGenerated_Item setBurnValue(int aMetaValue, int aValue) { + if (aMetaValue < 0 || aMetaValue >= mOffset + mEnabledItems.length() || aValue < 0) return this; + if (aValue == 0) mBurnValues.remove((short) aMetaValue); + else mBurnValues.put((short) aMetaValue, aValue > Short.MAX_VALUE ? Short.MAX_VALUE : (short) aValue); + return this; + } + + /** + * @param aMetaValue the Meta Value of the Item you want to set it to. [0 - 32765] + * @param aMaxCharge Maximum Charge. (if this is == 0 it will remove the Electric Behavior) + * @param aTransferLimit Transfer Limit. + * @param aTier The electric Tier. + * @param aSpecialData If this Item has a Fixed Charge, like a SingleUse Battery (if > 0). Use -1 if you want to + * make this Battery chargeable (the use and canUse Functions will still discharge if you just + * use this) Use -2 if you want to make this Battery dischargeable. Use -3 if you want to make + * this Battery charge/discharge-able. + * @return the Item itself for convenience in constructing. + */ + public final GT_MetaGenerated_Item setElectricStats(int aMetaValue, long aMaxCharge, long aTransferLimit, + long aTier, long aSpecialData, boolean aUseAnimations) { + if (aMetaValue < 0 || aMetaValue >= mOffset + mEnabledItems.length()) return this; + if (aMaxCharge == 0) mElectricStats.remove((short) aMetaValue); + else { + mElectricStats.put( + (short) aMetaValue, + new Long[] { aMaxCharge, Math.max(0, aTransferLimit), Math.max(-1, aTier), aSpecialData }); + if (aMetaValue >= mOffset && aUseAnimations) mIconList[aMetaValue - mOffset] = Arrays + .copyOf(mIconList[aMetaValue - mOffset], Math.max(9, mIconList[aMetaValue - mOffset].length)); + } + return this; + } + + /** + * + * @param aMetaValue the Meta Value of the Item you want to set it to. [0 - 32765] + * @param aCapacity fluid capacity in L or mb + * @param aStacksize item stack size + * @return the Item itself for convenience in constructing. + */ + public final GT_MetaGenerated_Item setFluidContainerStats(int aMetaValue, long aCapacity, long aStacksize) { + if (aMetaValue < 0 || aMetaValue >= mOffset + mEnabledItems.length()) return this; + if (aCapacity < 0) mElectricStats.remove((short) aMetaValue); + else mFluidContainerStats.put((short) aMetaValue, new Long[] { aCapacity, Math.max(1, aStacksize) }); + return this; + } + + /** + * @return if this MetaGenerated Item should use my Default Renderer System. + */ + public boolean useStandardMetaItemRenderer() { + return true; + } + + @Override + public short[] getRGBa(ItemStack aStack) { + return Materials._NULL.getRGBA(); + } + + /** + * @return the Icon the Material is going to be rendered with. + */ + public IIconContainer getIconContainer(int aMetaData) { + return null; + } + + @Override + public IIcon getIcon(int aMetaData, int pass) { + IIconContainer iconContainer = getIconContainer(aMetaData); + return iconContainer != null ? iconContainer.getIcon() : null; + } + + @Override + public IIcon getOverlayIcon(int aMetaData, int pass) { + IIconContainer iconContainer = getIconContainer(aMetaData); + return iconContainer != null ? iconContainer.getOverlayIcon() : null; + } + + @Override + public boolean shouldUseCustomRenderer(int aMetaData) { + return true; + } + + @Override + public GT_GeneratedMaterial_Renderer getMaterialRenderer(int aMetaData) { + return null; + } + + @Override + public boolean allowMaterialRenderer(int aMetaData) { + return aMetaData < this.mOffset; + } + + /* ---------- INTERNAL OVERRIDES ---------- */ + + @Override + public ItemStack onItemRightClick(ItemStack aStack, World aWorld, EntityPlayer aPlayer) { + IFoodStat tStat = mFoodStats.get((short) getDamage(aStack)); + if (tStat != null && aPlayer.canEat(tStat.alwaysEdible(this, aStack, aPlayer))) + aPlayer.setItemInUse(aStack, 32); + return super.onItemRightClick(aStack, aWorld, aPlayer); + } + + @Override + public int getMaxItemUseDuration(ItemStack aStack) { + return mFoodStats.get((short) getDamage(aStack)) == null ? 0 : 32; + } + + @Override + public EnumAction getItemUseAction(ItemStack aStack) { + IFoodStat tStat = mFoodStats.get((short) getDamage(aStack)); + return tStat == null ? EnumAction.none : tStat.getFoodAction(this, aStack); + } + + @Override + public final ItemStack onEaten(ItemStack aStack, World aWorld, EntityPlayer aPlayer) { + IFoodStat tStat = mFoodStats.get((short) getDamage(aStack)); + if (tStat != null) { + if (AppleCore.isModLoaded()) { + aPlayer.getFoodStats() + .func_151686_a(getFoodProxy(this), aStack); + } else { + aPlayer.getFoodStats() + .addStats(tStat.getFoodLevel(this, aStack, aPlayer), tStat.getSaturation(this, aStack, aPlayer)); + } + tStat.onEaten(this, aStack, aPlayer); + } + return aStack; + } + + @Optional.Method(modid = Mods.Names.APPLE_CORE) + private static ItemFood getFoodProxy(Object edible) { + return new ItemFoodProxy((IEdible) edible); + } + + @Override + @Optional.Method(modid = Mods.Names.APPLE_CORE) + public FoodValues getFoodValues(ItemStack aStack) { + IFoodStat tStat = mFoodStats.get((short) getDamage(aStack)); + return tStat == null ? null + : new FoodValues(tStat.getFoodLevel(this, aStack, null), tStat.getSaturation(this, aStack, null)); + } + + @Override + @SideOnly(Side.CLIENT) + public void getSubItems(Item aItem, CreativeTabs aCreativeTab, List<ItemStack> aList) { + int j = mEnabledItems.length(); + for (int i = 0; i < j; i++) if (mVisibleItems.get(i) || (D1 && mEnabledItems.get(i))) { + Long[] tStats = mElectricStats.get((short) (mOffset + i)); + if (tStats != null && tStats[3] < 0) { + ItemStack tStack = new ItemStack(this, 1, mOffset + i); + setCharge(tStack, Math.abs(tStats[0])); + isItemStackUsable(tStack); + aList.add(tStack); + } + if (tStats == null || tStats[3] != -2) { + ItemStack tStack = new ItemStack(this, 1, mOffset + i); + isItemStackUsable(tStack); + aList.add(tStack); + } + } + } + + @Override + @SideOnly(Side.CLIENT) + public final void registerIcons(IIconRegister aIconRegister) { + short j = (short) mEnabledItems.length(); + for (short i = 0; i < j; i++) if (mEnabledItems.get(i)) { + for (byte k = 1; k < mIconList[i].length; k++) { + mIconList[i][k] = aIconRegister.registerIcon( + GregTech.getResourcePath(GT_Config.troll ? "troll" : getUnlocalizedName() + "/" + i + "/" + k)); + } + mIconList[i][0] = aIconRegister + .registerIcon(GregTech.getResourcePath(GT_Config.troll ? "troll" : getUnlocalizedName() + "/" + i)); + } + } + + @Override + public final Long[] getElectricStats(ItemStack aStack) { + return mElectricStats.get((short) aStack.getItemDamage()); + } + + @Override + public final Long[] getFluidContainerStats(ItemStack aStack) { + return mFluidContainerStats.get((short) aStack.getItemDamage()); + } + + @Override + public int getItemEnchantability() { + return 0; + } + + @Override + public boolean isBookEnchantable(ItemStack aStack, ItemStack aBook) { + return false; + } + + @Override + public boolean getIsRepairable(ItemStack aStack, ItemStack aMaterial) { + return false; + } +} diff --git a/src/main/java/gregtech/api/items/GT_MetaGenerated_Item_X01.java b/src/main/java/gregtech/api/items/GT_MetaGenerated_Item_X01.java new file mode 100644 index 0000000000..db41a3c35b --- /dev/null +++ b/src/main/java/gregtech/api/items/GT_MetaGenerated_Item_X01.java @@ -0,0 +1,213 @@ +package gregtech.api.items; + +import static gregtech.api.enums.GT_Values.M; + +import java.util.List; + +import net.minecraft.creativetab.CreativeTabs; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.util.IIcon; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import gregtech.api.GregTech_API; +import gregtech.api.enums.Materials; +import gregtech.api.enums.OrePrefixes; +import gregtech.api.interfaces.IIconContainer; +import gregtech.api.util.GT_LanguageManager; +import gregtech.api.util.GT_OreDictUnificator; +import gregtech.api.util.GT_Utility; + +/** + * @author Gregorius Techneticies + * <p/> + * One Item for everything! + * <p/> + * This brilliant Item Class is used for automatically generating all possible variations of Material Items, + * like Dusts, Ingots, Gems, Plates and similar. It saves me a ton of work, when adding Items, because I always + * have to make a new Item SubType for each OreDict Prefix, when adding a new Material. + * <p/> + * As you can see, up to 32766 Items can be generated using this Class. And the last 766 Items can be custom + * defined, just to save space and MetaData. + * <p/> + * These Items can also have special RightClick abilities, electric Charge or even be set to become a Food alike + * Item. + */ +public abstract class GT_MetaGenerated_Item_X01 extends GT_MetaGenerated_Item { + + protected final OrePrefixes mPrefix; + protected final int mIconSetIndex; + + /** + * Creates the Item using these Parameters. This is for the new 1 Item = 1 Prefix System. + * + * @param aUnlocalized The Unlocalized Name of this Item. + * @param aGeneratedPrefix The OreDict Prefix you want to have generated. + * @param aIconSetIndex The TextureSet Index to be used. -1 for Defaulting to the Data contained in the Prefix. + * (this is only to be used for selecting the Icon in getIconContainer, nothing else) + */ + public GT_MetaGenerated_Item_X01(String aUnlocalized, OrePrefixes aGeneratedPrefix, int aIconSetIndex) { + super(aUnlocalized, (short) 32000, (short) 766); + mPrefix = aGeneratedPrefix; + mIconSetIndex = aIconSetIndex >= 0 ? aIconSetIndex + : aGeneratedPrefix.mTextureIndex >= 0 ? aGeneratedPrefix.mTextureIndex : 0; + + for (int i = 0; i < GregTech_API.sGeneratedMaterials.length; i++) { + OrePrefixes tPrefix = mPrefix; + if (tPrefix == null) continue; + Materials tMaterial = GregTech_API.sGeneratedMaterials[i]; + if (tMaterial == null) continue; + if (mPrefix.doGenerateItem(tMaterial)) { + ItemStack tStack = new ItemStack(this, 1, i); + GT_LanguageManager.addStringLocalization( + getUnlocalizedName(tStack) + ".name", + GT_LanguageManager.i18nPlaceholder ? getDefaultLocalizationFormat(tPrefix, tMaterial, i) + : getDefaultLocalization(tPrefix, tMaterial, i)); + GT_LanguageManager.addStringLocalization( + getUnlocalizedName(tStack) + ".tooltip", + tMaterial.getToolTip(tPrefix.mMaterialAmount / M)); + String tOreName = getOreDictString(tPrefix, tMaterial); + tPrefix = OrePrefixes.getOrePrefix(tOreName); + if (tPrefix != null && tPrefix.mIsUnificatable) { + GT_OreDictUnificator.set(tPrefix, OrePrefixes.getMaterial(tOreName, tPrefix), tStack); + } else { + GT_OreDictUnificator.registerOre(tOreName, tStack); + } + } + } + } + + /* ---------- OVERRIDEABLE FUNCTIONS ---------- */ + + /** + * @param aPrefix the OreDict Prefix + * @param aMaterial the Material + * @param aMetaData a Index from [0 - 31999] + * @return the Localized Name when default LangFiles are used. + */ + public String getDefaultLocalization(OrePrefixes aPrefix, Materials aMaterial, int aMetaData) { + return aPrefix.getDefaultLocalNameForItem(aMaterial); + } + + /** + * @param aPrefix the OreDict Prefix + * @param aMaterial the Material + * @param aMetaData a Index from [0 - 31999] + * @return the Localized Name Format when default LangFiles are used. + */ + public String getDefaultLocalizationFormat(OrePrefixes aPrefix, Materials aMaterial, int aMetaData) { + return aPrefix.getDefaultLocalNameFormatForItem(aMaterial); + } + + /** + * @param aPrefix always != null + * @param aMaterial always != null + * @param aDoShowAllItems this is the Configuration Setting of the User, if he wants to see all the Stuff like Tiny + * Dusts or Crushed Ores as well. + * @return if this Item should be visible in NEI or Creative + */ + public boolean doesShowInCreative(OrePrefixes aPrefix, Materials aMaterial, boolean aDoShowAllItems) { + return true; + } + + /** + * @return the name of the Item to be registered at the OreDict. + */ + public String getOreDictString(OrePrefixes aPrefix, Materials aMaterial) { + return aPrefix.get(aMaterial) + .toString(); + } + + public IIconContainer getIconContainer(int aMetaData, Materials aMaterial) { + return aMaterial.mIconSet.mTextures[mIconSetIndex]; + } + + /* ---------- INTERNAL OVERRIDES ---------- */ + + @Override + public String getItemStackDisplayName(ItemStack aStack) { + String aName = super.getItemStackDisplayName(aStack); + int aDamage = aStack.getItemDamage(); + if (aDamage < 32000 && aDamage >= 0) return Materials.getLocalizedNameForItem(aName, aDamage % 1000); + return aName; + } + + @Override + public ItemStack getContainerItem(ItemStack aStack) { + int aMetaData = aStack.getItemDamage(); + if (aMetaData < GregTech_API.sGeneratedMaterials.length && aMetaData >= 0) { + Materials aMaterial = GregTech_API.sGeneratedMaterials[aMetaData]; + if (aMaterial != null && aMaterial != Materials.Empty && aMaterial != Materials._NULL) { + return GT_Utility.copyAmount(1, mPrefix.mContainerItem); + } + } + return null; + } + + @Override + public short[] getRGBa(ItemStack aStack) { + int aMetaData = getDamage(aStack); + return aMetaData < GregTech_API.sGeneratedMaterials.length + && GregTech_API.sGeneratedMaterials[aMetaData] != null ? GregTech_API.sGeneratedMaterials[aMetaData].mRGBa + : Materials._NULL.mRGBa; + } + + @Override + public final IIconContainer getIconContainer(int aMetaData) { + return aMetaData < GregTech_API.sGeneratedMaterials.length + && GregTech_API.sGeneratedMaterials[aMetaData] != null + ? getIconContainer(aMetaData, GregTech_API.sGeneratedMaterials[aMetaData]) + : null; + } + + @Override + @SideOnly(Side.CLIENT) + public final void getSubItems(Item aItem, CreativeTabs aCreativeTab, List<ItemStack> aList) { + for (int i = 0; i < GregTech_API.sGeneratedMaterials.length; i++) + if (mPrefix.doGenerateItem(GregTech_API.sGeneratedMaterials[i]) && doesShowInCreative( + mPrefix, + GregTech_API.sGeneratedMaterials[i], + GregTech_API.sDoShowAllItemsInCreative)) { + ItemStack tStack = new ItemStack(this, 1, i); + isItemStackUsable(tStack); + aList.add(tStack); + } + super.getSubItems(aItem, aCreativeTab, aList); + } + + @Override + public final IIcon getIconFromDamage(int aMetaData) { + if (aMetaData < 0) return null; + if (aMetaData < GregTech_API.sGeneratedMaterials.length) { + Materials tMaterial = GregTech_API.sGeneratedMaterials[aMetaData]; + if (tMaterial == null) return null; + IIconContainer tIcon = getIconContainer(aMetaData, tMaterial); + if (tIcon != null) return tIcon.getIcon(); + return null; + } + return aMetaData >= mOffset && aMetaData - mOffset < mIconList.length ? mIconList[aMetaData - mOffset][0] + : null; + } + + @Override + public int getItemStackLimit(ItemStack aStack) { + return getDamage(aStack) < mOffset ? Math.min(super.getItemStackLimit(aStack), mPrefix.mDefaultStackSize) + : super.getItemStackLimit(aStack); + } + + @Override + public int getItemEnchantability() { + return 0; + } + + @Override + public boolean isBookEnchantable(ItemStack aStack, ItemStack aBook) { + return false; + } + + @Override + public boolean getIsRepairable(ItemStack aStack, ItemStack aMaterial) { + return false; + } +} diff --git a/src/main/java/gregtech/api/items/GT_MetaGenerated_Item_X32.java b/src/main/java/gregtech/api/items/GT_MetaGenerated_Item_X32.java new file mode 100644 index 0000000000..a06a4a7a63 --- /dev/null +++ b/src/main/java/gregtech/api/items/GT_MetaGenerated_Item_X32.java @@ -0,0 +1,224 @@ +package gregtech.api.items; + +import static gregtech.api.enums.GT_Values.M; + +import java.util.Arrays; +import java.util.List; + +import net.minecraft.creativetab.CreativeTabs; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.util.IIcon; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import gregtech.api.GregTech_API; +import gregtech.api.enums.Materials; +import gregtech.api.enums.OrePrefixes; +import gregtech.api.interfaces.IIconContainer; +import gregtech.api.util.GT_LanguageManager; +import gregtech.api.util.GT_ModHandler; +import gregtech.api.util.GT_OreDictUnificator; +import gregtech.api.util.GT_Utility; +import gregtech.common.render.items.GT_GeneratedMaterial_Renderer; + +/** + * @author Gregorius Techneticies + * <p/> + * One Item for everything! + * <p/> + * This brilliant Item Class is used for automatically generating all possible variations of Material Items, + * like Dusts, Ingots, Gems, Plates and similar. It saves me a ton of work, when adding Items, because I always + * have to make a new Item SubType for each OreDict Prefix, when adding a new Material. + * <p/> + * As you can see, up to 32766 Items can be generated using this Class. And the last 766 Items can be custom + * defined, just to save space and MetaData. + * <p/> + * These Items can also have special RightClick abilities, electric Charge or even be set to become a Food alike + * Item. + */ +public abstract class GT_MetaGenerated_Item_X32 extends GT_MetaGenerated_Item { + + protected final OrePrefixes[] mGeneratedPrefixList; + + /** + * Creates the Item using these Parameters. + * + * @param aUnlocalized The Unlocalized Name of this Item. + * @param aGeneratedPrefixList The OreDict Prefixes you want to have generated. + */ + public GT_MetaGenerated_Item_X32(String aUnlocalized, OrePrefixes... aGeneratedPrefixList) { + super(aUnlocalized, (short) 32000, (short) 766); + mGeneratedPrefixList = Arrays.copyOf(aGeneratedPrefixList, 32); + + for (int i = 0; i < 32000; i++) { + OrePrefixes tPrefix = mGeneratedPrefixList[i / 1000]; + if (tPrefix == null) continue; + Materials tMaterial = GregTech_API.sGeneratedMaterials[i % 1000]; + if (tMaterial == null) continue; + if (doesMaterialAllowGeneration(tPrefix, tMaterial)) { + ItemStack tStack = new ItemStack(this, 1, i); + GT_LanguageManager.addStringLocalization( + getUnlocalizedName(tStack) + ".name", + GT_LanguageManager.i18nPlaceholder ? getDefaultLocalizationFormat(tPrefix, tMaterial, i) + : getDefaultLocalization(tPrefix, tMaterial, i)); + GT_LanguageManager.addStringLocalization( + getUnlocalizedName(tStack) + ".tooltip", + tMaterial.getToolTip(tPrefix.mMaterialAmount / M)); + if (tPrefix.mIsUnificatable) { + GT_OreDictUnificator.set(tPrefix, tMaterial, tStack); + } else { + GT_OreDictUnificator.registerOre(tPrefix.get(tMaterial), tStack); + } + if ((tPrefix == OrePrefixes.stick || tPrefix == OrePrefixes.wireFine || tPrefix == OrePrefixes.ingot) + && (tMaterial == Materials.Lead || tMaterial == Materials.Tin + || tMaterial == Materials.SolderingAlloy)) { + GregTech_API.sSolderingMetalList.add(tStack); + GT_ModHandler.registerBoxableItemToToolBox(tStack); + } + } + } + } + + /* ---------- OVERRIDEABLE FUNCTIONS ---------- */ + + /** + * @return the Color Modulation the Material is going to be rendered with. + */ + @Override + public short[] getRGBa(ItemStack aStack) { + Materials tMaterial = GregTech_API.sGeneratedMaterials[getDamage(aStack) % 1000]; + return tMaterial == null ? Materials._NULL.mRGBa : tMaterial.mRGBa; + } + + /** + * @param aPrefix this can be null, you have to return false in that case + * @param aMaterial this can be null, you have to return false in that case + * @return if this Item should be generated and visible. + */ + public boolean doesMaterialAllowGeneration(OrePrefixes aPrefix, Materials aMaterial) { + // You have to check for at least these Conditions in every Case! So add a super Call like the following for + // this before executing your Code: + // if (!super.doesMaterialAllowGeneration(aPrefix, aMaterial)) return false; + return aPrefix != null && aPrefix.doGenerateItem(aMaterial); + } + + /* ---------- OVERRIDEABLE FUNCTIONS ---------- */ + + /** + * @param aPrefix the OreDict Prefix + * @param aMaterial the Material + * @param aMetaData a Index from [0 - 31999] + * @return the Localized Name when default LangFiles are used. + */ + public String getDefaultLocalization(OrePrefixes aPrefix, Materials aMaterial, int aMetaData) { + return aPrefix.getDefaultLocalNameForItem(aMaterial); + } + + /** + * @param aPrefix the OreDict Prefix + * @param aMaterial the Material + * @param aMetaData a Index from [0 - 31999] + * @return the Localized Name Format when default LangFiles are used. + */ + public String getDefaultLocalizationFormat(OrePrefixes aPrefix, Materials aMaterial, int aMetaData) { + return aPrefix.getDefaultLocalNameFormatForItem(aMaterial); + } + + /** + * @param aMetaData a Index from [0 - 31999] + * @param aMaterial the Material + * @return an Icon Container for the Item Display. + */ + public final IIconContainer getIconContainer(int aMetaData, Materials aMaterial) { + return mGeneratedPrefixList[aMetaData / 1000] != null + && mGeneratedPrefixList[aMetaData / 1000].mTextureIndex >= 0 + ? aMaterial.mIconSet.mTextures[mGeneratedPrefixList[aMetaData / 1000].mTextureIndex] + : null; + } + + /** + * @param aPrefix always != null + * @param aMaterial always != null + * @param aDoShowAllItems this is the Configuration Setting of the User, if he wants to see all the Stuff like Tiny + * Dusts or Crushed Ores as well. + * @return if this Item should be visible in NEI or Creative + */ + public boolean doesShowInCreative(OrePrefixes aPrefix, Materials aMaterial, boolean aDoShowAllItems) { + return true; + } + + /* ---------- INTERNAL OVERRIDES ---------- */ + + @Override + public String getItemStackDisplayName(ItemStack aStack) { + String aName = super.getItemStackDisplayName(aStack); + int aDamage = aStack.getItemDamage(); + if (aDamage < 32000 && aDamage >= 0) return Materials.getLocalizedNameForItem(aName, aDamage % 1000); + return aName; + } + + @Override + public ItemStack getContainerItem(ItemStack aStack) { + int aDamage = aStack.getItemDamage(); + if (aDamage < 32000 && aDamage >= 0) { + Materials aMaterial = GregTech_API.sGeneratedMaterials[aDamage % 1000]; + if (aMaterial != null && aMaterial != Materials.Empty && aMaterial != Materials._NULL) { + OrePrefixes aPrefix = mGeneratedPrefixList[aDamage / 1000]; + if (aPrefix != null) return GT_Utility.copyAmount(1, aPrefix.mContainerItem); + } + } + return null; + } + + @Override + public final IIconContainer getIconContainer(int aMetaData) { + return GregTech_API.sGeneratedMaterials[aMetaData % 1000] == null ? null + : getIconContainer(aMetaData, GregTech_API.sGeneratedMaterials[aMetaData % 1000]); + } + + @Override + public GT_GeneratedMaterial_Renderer getMaterialRenderer(int aMetaData) { + return GregTech_API.sGeneratedMaterials[aMetaData % 1000] == null ? null + : GregTech_API.sGeneratedMaterials[aMetaData % 1000].renderer; + } + + @Override + @SideOnly(Side.CLIENT) + public final void getSubItems(Item aItem, CreativeTabs aCreativeTab, List<ItemStack> aList) { + for (int i = 0; i < 32000; i++) { + OrePrefixes aPrefix = mGeneratedPrefixList[i / 1000]; + Materials aMaterial = GregTech_API.sGeneratedMaterials[i % 1000]; + if (aPrefix != null && aMaterial != null) { + if (doesMaterialAllowGeneration(aPrefix, aMaterial) + && doesShowInCreative(aPrefix, aMaterial, GregTech_API.sDoShowAllItemsInCreative)) { + ItemStack tStack = new ItemStack(this, 1, i); + isItemStackUsable(tStack); + aList.add(tStack); + } + } + } + super.getSubItems(aItem, aCreativeTab, aList); + } + + @Override + public final IIcon getIconFromDamage(int aMetaData) { + if (aMetaData < 0) return null; + if (aMetaData < 32000) { + Materials tMaterial = GregTech_API.sGeneratedMaterials[aMetaData % 1000]; + if (tMaterial == null) return null; + IIconContainer tIcon = getIconContainer(aMetaData, tMaterial); + if (tIcon != null) return tIcon.getIcon(); + return null; + } + return aMetaData - 32000 < mIconList.length ? mIconList[aMetaData - 32000][0] : null; + } + + @Override + public int getItemStackLimit(ItemStack aStack) { + int tDamage = getDamage(aStack); + if (tDamage < 32000 && mGeneratedPrefixList[tDamage / 1000] != null) + return Math.min(super.getItemStackLimit(aStack), mGeneratedPrefixList[tDamage / 1000].mDefaultStackSize); + return super.getItemStackLimit(aStack); + } +} diff --git a/src/main/java/gregtech/api/items/GT_MetaGenerated_Tool.java b/src/main/java/gregtech/api/items/GT_MetaGenerated_Tool.java new file mode 100644 index 0000000000..cec93169ec --- /dev/null +++ b/src/main/java/gregtech/api/items/GT_MetaGenerated_Tool.java @@ -0,0 +1,1013 @@ +package gregtech.api.items; + +import static gregtech.api.util.GT_Utility.formatNumbers; +import static gregtech.common.tileentities.machines.multi.GT_MetaTileEntity_LargeTurbine_Steam.calculateLooseFlow; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map.Entry; +import java.util.concurrent.ConcurrentHashMap; + +import net.minecraft.block.Block; +import net.minecraft.client.renderer.texture.IIconRegister; +import net.minecraft.creativetab.CreativeTabs; +import net.minecraft.enchantment.Enchantment; +import net.minecraft.enchantment.EnchantmentHelper; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.SharedMonsterAttributes; +import net.minecraft.entity.item.EntityItem; +import net.minecraft.entity.item.EntityMinecart; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumAction; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.potion.Potion; +import net.minecraft.stats.AchievementList; +import net.minecraft.stats.StatList; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.IIcon; +import net.minecraft.util.MathHelper; +import net.minecraft.util.StatCollector; +import net.minecraft.world.World; +import net.minecraftforge.common.IShearable; +import net.minecraftforge.event.entity.player.PlayerEvent; +import net.minecraftforge.event.world.BlockEvent; + +import appeng.api.implementations.items.IAEWrench; +import buildcraft.api.tools.IToolWrench; +import cpw.mods.fml.common.Mod; +import cpw.mods.fml.common.Optional; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import crazypants.enderio.api.tool.ITool; +import forestry.api.arboriculture.IToolGrafter; +import gregtech.GT_Mod; +import gregtech.api.GregTech_API; +import gregtech.api.enchants.Enchantment_Radioactivity; +import gregtech.api.enums.Materials; +import gregtech.api.enums.TC_Aspects.TC_AspectStack; +import gregtech.api.interfaces.IDamagableItem; +import gregtech.api.interfaces.IToolStats; +import gregtech.api.util.GT_LanguageManager; +import gregtech.api.util.GT_ModHandler; +import gregtech.api.util.GT_OreDictUnificator; +import gregtech.api.util.GT_Utility; +import gregtech.common.tools.GT_Tool_Turbine; +import mods.railcraft.api.core.items.IToolCrowbar; +import mrtjp.projectred.api.IScrewdriver; + +/** + * This is an example on how you can create a Tool ItemStack, in this case a Bismuth Wrench: + * GT_MetaGenerated_Tool.sInstances.get("gt.metatool.01").getToolWithStats(GT_MetaGenerated_Tool_01.WRENCH, 1, + * Materials.Bismuth, Materials.Bismuth, null); + */ +@Optional.InterfaceList( + value = { + @Optional.Interface(iface = "forestry.api.arboriculture.IToolGrafter", modid = "ForestryAPI|arboriculture"), + @Optional.Interface(iface = "mods.railcraft.api.core.items.IToolCrowbar", modid = "RailcraftAPI|items"), + @Optional.Interface(iface = "buildcraft.api.tools.IToolWrench", modid = "BuildCraftAPI|tools"), + @Optional.Interface(iface = "crazypants.enderio.api.tool.ITool", modid = "EnderIOAPI|Tools"), + @Optional.Interface(iface = "mrtjp.projectred.api.IScrewdriver", modid = "ProjRed|Core"), }) +public abstract class GT_MetaGenerated_Tool extends GT_MetaBase_Item + implements IDamagableItem, IToolGrafter, IToolCrowbar, IToolWrench, ITool, IScrewdriver, IAEWrench { + + /** + * All instances of this Item Class are listed here. This gets used to register the Renderer to all Items of this + * Type, if useStandardMetaItemRenderer() returns true. + * <p/> + * You can also use the unlocalized Name gotten from getUnlocalizedName() as Key if you want to get a specific Item. + */ + public static final ConcurrentHashMap<String, GT_MetaGenerated_Tool> sInstances = new ConcurrentHashMap<>(); + + /* ---------- CONSTRUCTOR AND MEMBER VARIABLES ---------- */ + + public final ConcurrentHashMap<Short, IToolStats> mToolStats = new ConcurrentHashMap<>(); + + /** + * Creates the Item using these Parameters. + * + * @param aUnlocalized The Unlocalized Name of this Item. + */ + public GT_MetaGenerated_Tool(String aUnlocalized) { + super(aUnlocalized); + setCreativeTab(GregTech_API.TAB_GREGTECH); + setMaxStackSize(1); + sInstances.put(getUnlocalizedName(), this); + } + + /* ---------- FOR ADDING CUSTOM ITEMS INTO THE REMAINING 766 RANGE ---------- */ + + public static final Materials getPrimaryMaterial(ItemStack aStack) { + NBTTagCompound aNBT = aStack.getTagCompound(); + if (aNBT != null) { + aNBT = aNBT.getCompoundTag("GT.ToolStats"); + if (aNBT != null) return Materials.getRealMaterial(aNBT.getString("PrimaryMaterial")); + } + return Materials._NULL; + } + + public static final Materials getSecondaryMaterial(ItemStack aStack) { + NBTTagCompound aNBT = aStack.getTagCompound(); + if (aNBT != null) { + aNBT = aNBT.getCompoundTag("GT.ToolStats"); + if (aNBT != null) return Materials.getRealMaterial(aNBT.getString("SecondaryMaterial")); + } + return Materials._NULL; + } + + /* ---------- INTERNAL OVERRIDES ---------- */ + + public static final long getToolMaxDamage(ItemStack aStack) { + NBTTagCompound aNBT = aStack.getTagCompound(); + if (aNBT != null) { + aNBT = aNBT.getCompoundTag("GT.ToolStats"); + if (aNBT != null) return aNBT.getLong("MaxDamage"); + } + return 0; + } + + public static final long getToolDamage(ItemStack aStack) { + NBTTagCompound aNBT = aStack.getTagCompound(); + if (aNBT != null) { + aNBT = aNBT.getCompoundTag("GT.ToolStats"); + if (aNBT != null) return aNBT.getLong("Damage"); + } + return 0; + } + + public static final boolean setToolDamage(ItemStack aStack, long aDamage) { + NBTTagCompound aNBT = aStack.getTagCompound(); + if (aNBT != null) { + aNBT = aNBT.getCompoundTag("GT.ToolStats"); + if (aNBT != null) { + aNBT.setLong("Damage", aDamage); + return true; + } + } + return false; + } + + public static final boolean setToolMode(ItemStack aStack, byte aMode) { + NBTTagCompound aNBT = aStack.getTagCompound(); + if (aNBT != null) { + aNBT = aNBT.getCompoundTag("GT.ToolStats"); + if (aNBT != null) { + aNBT.setByte("Mode", aMode); + return true; + } + } + return false; + } + + public static final byte getToolMode(ItemStack aStack) { + NBTTagCompound aNBT = aStack.getTagCompound(); + if (aNBT != null) { + aNBT = aNBT.getCompoundTag("GT.ToolStats"); + if (aNBT != null) return aNBT.getByte("Mode"); + } + return 0; + } + + /** + * This adds a Custom Item to the ending Range. + * + * @param aID The Id of the assigned Tool Class [0 - 32765] (only even Numbers allowed! Uneven + * ID's are empty electric Items) + * @param aEnglish The Default Localized Name of the created Item + * @param aToolTip The Default ToolTip of the created Item, you can also insert null for having no + * ToolTip + * @param aToolStats The Food Value of this Item. Can be null as well. + * @param aOreDictNamesAndAspects The OreDict Names you want to give the Item. Also used to assign Thaumcraft + * Aspects. + * @return An ItemStack containing the newly created Item, but without specific Stats. + */ + public final ItemStack addTool(int aID, String aEnglish, String aToolTip, IToolStats aToolStats, + Object... aOreDictNamesAndAspects) { + if (aToolTip == null) aToolTip = ""; + if (aID >= 0 && aID < 32766 && aID % 2 == 0) { + GT_LanguageManager.addStringLocalization(getUnlocalizedName() + "." + aID + ".name", aEnglish); + GT_LanguageManager.addStringLocalization(getUnlocalizedName() + "." + aID + ".tooltip", aToolTip); + GT_LanguageManager + .addStringLocalization(getUnlocalizedName() + "." + (aID + 1) + ".name", aEnglish + " (Empty)"); + GT_LanguageManager + .addStringLocalization(getUnlocalizedName() + "." + (aID + 1) + ".tooltip", "You need to recharge it"); + mToolStats.put((short) aID, aToolStats); + mToolStats.put((short) (aID + 1), aToolStats); + aToolStats.onStatsAddedToTool(this, aID); + ItemStack rStack = new ItemStack(this, 1, aID); + List<TC_AspectStack> tAspects = new ArrayList<>(); + for (Object tOreDictNameOrAspect : aOreDictNamesAndAspects) { + if (tOreDictNameOrAspect instanceof TC_AspectStack) + ((TC_AspectStack) tOreDictNameOrAspect).addToAspectList(tAspects); + else GT_OreDictUnificator.registerOre(tOreDictNameOrAspect, rStack); + } + if (GregTech_API.sThaumcraftCompat != null) + GregTech_API.sThaumcraftCompat.registerThaumcraftAspectsToItem(rStack, tAspects, false); + GT_ModHandler.registerBoxableItemToToolBox(rStack); + return rStack; + } + return null; + } + + /** + * This Function gets an ItemStack Version of this Tool + * + * @param aToolID the ID of the Tool Class + * @param aAmount Amount of Items (well normally you only need 1) + * @param aPrimaryMaterial Primary Material of this Tool + * @param aSecondaryMaterial Secondary (Rod/Handle) Material of this Tool + * @param aElectricArray The Electric Stats of this Tool (or null if not electric) + */ + public final ItemStack getToolWithStats(int aToolID, int aAmount, Materials aPrimaryMaterial, + Materials aSecondaryMaterial, long[] aElectricArray) { + ItemStack rStack = new ItemStack(this, aAmount, aToolID); + IToolStats tToolStats = getToolStats(rStack); + if (tToolStats != null) { + NBTTagCompound tMainNBT = new NBTTagCompound(), tToolNBT = new NBTTagCompound(); + tToolNBT.setByte("Mode", (byte) 0); + if (aPrimaryMaterial != null) { + tToolNBT.setString("PrimaryMaterial", aPrimaryMaterial.mName); + tToolNBT.setLong( + "MaxDamage", + 100L * (long) (aPrimaryMaterial.mDurability * tToolStats.getMaxDurabilityMultiplier())); + } + if (aSecondaryMaterial != null) tToolNBT.setString("SecondaryMaterial", aSecondaryMaterial.mName); + + if (aElectricArray != null) { + tToolNBT.setBoolean("Electric", true); + tToolNBT.setLong("MaxCharge", aElectricArray[0]); + tToolNBT.setLong("Voltage", aElectricArray[1]); + tToolNBT.setLong("Tier", aElectricArray[2]); + tToolNBT.setLong("SpecialData", aElectricArray[3]); + } + + tMainNBT.setTag("GT.ToolStats", tToolNBT); + rStack.setTagCompound(tMainNBT); + } + isItemStackUsable(rStack); + return rStack; + } + + /** + * Called by the Block Harvesting Event within the GT_Proxy + */ + @Mod.EventHandler + public void onHarvestBlockEvent(ArrayList<ItemStack> aDrops, ItemStack aStack, EntityPlayer aPlayer, Block aBlock, + int aX, int aY, int aZ, byte aMetaData, int aFortune, boolean aSilkTouch, BlockEvent.HarvestDropsEvent aEvent) { + IToolStats tStats = getToolStats(aStack); + if (isItemStackUsable(aStack) && getDigSpeed(aStack, aBlock, aMetaData) > 0.0F) doDamage( + aStack, + (long) tStats + .convertBlockDrops(aDrops, aStack, aPlayer, aBlock, aX, aY, aZ, aMetaData, aFortune, aSilkTouch, aEvent) + * tStats.getToolDamagePerDropConversion()); + } + + @Mod.EventHandler + public float onBlockBreakSpeedEvent(float aDefault, ItemStack aStack, EntityPlayer aPlayer, Block aBlock, int aX, + int aY, int aZ, byte aMetaData, PlayerEvent.BreakSpeed aEvent) { + IToolStats tStats = getToolStats(aStack); + return tStats == null ? aDefault + : tStats.getMiningSpeed(aBlock, aMetaData, aDefault, aPlayer, aPlayer.worldObj, aX, aY, aZ); + } + + @Override + public boolean onBlockStartBreak(ItemStack aStack, int aX, int aY, int aZ, EntityPlayer aPlayer) { + if (aPlayer.worldObj.isRemote) { + return false; + } + IToolStats tStats = getToolStats(aStack); + Block aBlock = aPlayer.worldObj.getBlock(aX, aY, aZ); + if (tStats.isChainsaw() && (aBlock instanceof IShearable target)) { + if ((target.isShearable(aStack, aPlayer.worldObj, aX, aY, aZ))) { + ArrayList<ItemStack> drops = target.onSheared( + aStack, + aPlayer.worldObj, + aX, + aY, + aZ, + EnchantmentHelper.getEnchantmentLevel(Enchantment.fortune.effectId, aStack)); + for (ItemStack stack : drops) { + float f = 0.7F; + double d = itemRand.nextFloat() * f + (1.0F - f) * 0.5D; + double d1 = itemRand.nextFloat() * f + (1.0F - f) * 0.5D; + double d2 = itemRand.nextFloat() * f + (1.0F - f) * 0.5D; + EntityItem entityitem = new EntityItem(aPlayer.worldObj, aX + d, aY + d1, aZ + d2, stack); + entityitem.delayBeforeCanPickup = 10; + aPlayer.worldObj.spawnEntityInWorld(entityitem); + } + aPlayer.addStat(net.minecraft.stats.StatList.mineBlockStatArray[Block.getIdFromBlock(aBlock)], 1); + onBlockDestroyed(aStack, aPlayer.worldObj, aBlock, aX, aY, aZ, aPlayer); + } + return false; + } + return super.onBlockStartBreak(aStack, aX, aY, aZ, aPlayer); + } + + @Override + public boolean onLeftClickEntity(ItemStack aStack, EntityPlayer aPlayer, Entity aEntity) { + IToolStats tStats = getToolStats(aStack); + if (tStats == null || !isItemStackUsable(aStack)) return true; + GT_Utility.doSoundAtClient(tStats.getEntityHitSound(), 1, 1.0F); + if (super.onLeftClickEntity(aStack, aPlayer, aEntity)) return true; + if (aEntity.canAttackWithItem() && !aEntity.hitByEntity(aPlayer)) { + float tMagicDamage = tStats.getMagicDamageAgainstEntity( + aEntity instanceof EntityLivingBase + ? EnchantmentHelper.getEnchantmentModifierLiving(aPlayer, (EntityLivingBase) aEntity) + : 0.0F, + aEntity, + aStack, + aPlayer), + tDamage = tStats.getNormalDamageAgainstEntity( + (float) aPlayer.getEntityAttribute(SharedMonsterAttributes.attackDamage) + .getAttributeValue() + getToolCombatDamage(aStack), + aEntity, + aStack, + aPlayer); + if (tDamage + tMagicDamage > 0.0F) { + boolean tCriticalHit = aPlayer.fallDistance > 0.0F && !aPlayer.onGround + && !aPlayer.isOnLadder() + && !aPlayer.isInWater() + && !aPlayer.isPotionActive(Potion.blindness) + && aPlayer.ridingEntity == null + && aEntity instanceof EntityLivingBase; + if (tCriticalHit && tDamage > 0.0F) tDamage *= 1.5F; + tDamage += tMagicDamage; + if (aEntity.attackEntityFrom(tStats.getDamageSource(aPlayer, aEntity), tDamage)) { + if (aEntity instanceof EntityLivingBase) + aEntity.setFire(EnchantmentHelper.getFireAspectModifier(aPlayer) * 4); + int tKnockcack = (aPlayer.isSprinting() ? 1 : 0) + (aEntity instanceof EntityLivingBase + ? EnchantmentHelper.getKnockbackModifier(aPlayer, (EntityLivingBase) aEntity) + : 0); + if (tKnockcack > 0) { + aEntity.addVelocity( + -MathHelper.sin(aPlayer.rotationYaw * (float) Math.PI / 180.0F) * tKnockcack * 0.5F, + 0.1D, + MathHelper.cos(aPlayer.rotationYaw * (float) Math.PI / 180.0F) * tKnockcack * 0.5F); + aPlayer.motionX *= 0.6D; + aPlayer.motionZ *= 0.6D; + aPlayer.setSprinting(false); + } + if (tCriticalHit) aPlayer.onCriticalHit(aEntity); + if (tMagicDamage > 0.0F) aPlayer.onEnchantmentCritical(aEntity); + if (tDamage >= 18.0F) aPlayer.triggerAchievement(AchievementList.overkill); + aPlayer.setLastAttacker(aEntity); + if (aEntity instanceof EntityLivingBase) + EnchantmentHelper.func_151384_a((EntityLivingBase) aEntity, aPlayer); + EnchantmentHelper.func_151385_b(aPlayer, aEntity); + if (aEntity instanceof EntityLivingBase) + aPlayer.addStat(StatList.damageDealtStat, Math.round(tDamage * 10.0F)); + aEntity.hurtResistantTime = Math + .max(1, tStats.getHurtResistanceTime(aEntity.hurtResistantTime, aEntity)); + aPlayer.addExhaustion(0.3F); + doDamage(aStack, tStats.getToolDamagePerEntityAttack()); + } + } + } + if (aStack.stackSize <= 0) aPlayer.destroyCurrentEquippedItem(); + return true; + } + + @Override + public ItemStack onItemRightClick(ItemStack aStack, World aWorld, EntityPlayer aPlayer) { + IToolStats tStats = getToolStats(aStack); + if (tStats != null && tStats.canBlock()) aPlayer.setItemInUse(aStack, 72000); + return super.onItemRightClick(aStack, aWorld, aPlayer); + } + + @Override + public final int getMaxItemUseDuration(ItemStack aStack) { + return 72000; + } + + @Override + public final EnumAction getItemUseAction(ItemStack aStack) { + IToolStats tStats = getToolStats(aStack); + if (tStats != null && tStats.canBlock()) return EnumAction.block; + return EnumAction.none; + } + + @Override + @SideOnly(Side.CLIENT) + public final void getSubItems(Item aItem, CreativeTabs aCreativeTab, List<ItemStack> aList) { + for (int i = 0; i < 32766; i += 2) { + if (getToolStats(new ItemStack(this, 1, i)) != null) { + ItemStack tStack = new ItemStack(this, 1, i); + isItemStackUsable(tStack); + aList.add(tStack); + aList.add(getToolWithStats(i, 1, Materials.Neutronium, Materials.Neutronium, null)); + } + } + } + + @Override + @SideOnly(Side.CLIENT) + public final void registerIcons(IIconRegister aIconRegister) { + // + } + + @Override + public final IIcon getIconFromDamage(int aMetaData) { + return null; + } + + @Override + public void addAdditionalToolTips(List<String> aList, ItemStack aStack, EntityPlayer aPlayer) { + long tMaxDamage = getToolMaxDamage(aStack); + Materials tMaterial = getPrimaryMaterial(aStack); + IToolStats tStats = getToolStats(aStack); + int tOffset = getElectricStats(aStack) != null ? 2 : 1; + if (tStats != null) { + if (tStats instanceof GT_Tool_Turbine) { + + // Durability -> toolMaxDamage + // % Efficiency -> toolCombatDamage -> toolQuality + // Optimal Flow -> toolSpeed + // EU/t -> toolCombatDamage, toolSpeed + // Overflow Tier -> toolQuality + float aBaseEff = (5f + getToolCombatDamage(aStack)) * 1000f; + + // It was noted by IntelliJ that replacing ((GT_MetaGenerated_Tool) aStack.getItem()) with + // GT_MetaGenerated_Tool can have side effects. This refactoring will need tests. + @SuppressWarnings("AccessStaticViaInstance") + float aOptFlow = (Math.max( + Float.MIN_NORMAL, + ((GT_MetaGenerated_Tool) aStack.getItem()).getToolStats(aStack) + .getSpeedMultiplier() + * ((GT_MetaGenerated_Tool) aStack.getItem()).getPrimaryMaterial(aStack).mToolSpeed + * 50F)); + aList.add( + tOffset + 0, + EnumChatFormatting.GRAY + String.format( + transItem("001", "Durability: %s/%s"), + "" + EnumChatFormatting.GREEN + formatNumbers(tMaxDamage - getToolDamage(aStack)) + " ", + " " + formatNumbers(tMaxDamage)) + EnumChatFormatting.GRAY); + aList.add( + tOffset + 1, + EnumChatFormatting.GRAY + String.format( + transItem("002", "%s lvl %s"), + tMaterial.mLocalizedName + EnumChatFormatting.YELLOW, + "" + getHarvestLevel(aStack, "")) + EnumChatFormatting.GRAY); + aList.add( + tOffset + 2, + EnumChatFormatting.WHITE + + String.format( + transItem("005", "Turbine Efficiency: %s"), + "" + EnumChatFormatting.BLUE + (50.0F + (10.0F * getToolCombatDamage(aStack)))) + + "%" + + EnumChatFormatting.GRAY); + aList.add( + tOffset + 3, + EnumChatFormatting.WHITE + String.format( + transItem("006", "Optimal Steam flow: %s L/t"), + "" + EnumChatFormatting.GOLD + + formatNumbers( + GT_Utility.safeInt( + (long) (Math.max( + Float.MIN_NORMAL, + tStats.getSpeedMultiplier() * getPrimaryMaterial(aStack).mToolSpeed + * (1000 * getPrimaryMaterial(aStack).mSteamMultiplier / 20))))) + + EnumChatFormatting.GRAY)); + aList.add( + tOffset + 4, + EnumChatFormatting.WHITE + String.format( + transItem("900", "Energy from Optimal Steam Flow: %s EU/t"), + "" + EnumChatFormatting.GOLD + + formatNumbers( + GT_Utility.safeInt( + (long) (Math.max( + Float.MIN_NORMAL, + tStats.getSpeedMultiplier() * getPrimaryMaterial(aStack).mToolSpeed + * (1000 * getPrimaryMaterial(aStack).mSteamMultiplier / 20)) + * (50.0F + (10.0F * getToolCombatDamage(aStack))) + / 200))) + + EnumChatFormatting.GRAY)); + { + float[] calculatedFlow = calculateLooseFlow(aOptFlow, aBaseEff); + float aOptFlowLoose = calculatedFlow[0]; + float aBaseEffLoose = calculatedFlow[1]; + + aList.add( + tOffset + 5, + EnumChatFormatting.AQUA + String.format( + transItem("500", "Turbine Efficiency (Loose): %s"), + "" + EnumChatFormatting.BLUE + (long) aBaseEffLoose / 100 + "%" + EnumChatFormatting.GRAY)); + aList.add( + tOffset + 6, + EnumChatFormatting.AQUA + String.format( + transItem("501", "Optimal Steam flow (Loose): %s L/t"), + "" + EnumChatFormatting.GOLD + + formatNumbers(((long) aOptFlowLoose * getPrimaryMaterial(aStack).mSteamMultiplier)) + + EnumChatFormatting.GRAY)); + aList.add( + tOffset + 7, + EnumChatFormatting.AQUA + String.format( + transItem("901", "Energy from Optimal Steam Flow (Loose): %s EU/t"), + "" + EnumChatFormatting.GOLD + + formatNumbers( + ((long) aOptFlowLoose * getPrimaryMaterial(aStack).mSteamMultiplier / 10000) + * ((long) aBaseEffLoose / 2)) + + EnumChatFormatting.GRAY)); + aList.add( + tOffset + 8, + EnumChatFormatting.GRAY + "(Superheated Steam EU values are 2x those of Steam)"); + } + aList.add( + tOffset + 9, + EnumChatFormatting.WHITE + String.format( + transItem("902", "Optimal SC Steam flow: %s L/t"), + "" + EnumChatFormatting.GOLD + + formatNumbers( + GT_Utility.safeInt( + (long) (Math.max( + Float.MIN_NORMAL, + tStats.getSpeedMultiplier() * getPrimaryMaterial(aStack).mToolSpeed + * (1000f / 20f))))) + + EnumChatFormatting.GRAY)); + aList.add( + tOffset + 10, + EnumChatFormatting.WHITE + String.format( + transItem("903", "Energy from Optimal SC Steam Flow: %s EU/t"), + "" + EnumChatFormatting.GOLD + + formatNumbers( + GT_Utility.safeInt( + (long) (Math.max( + Float.MIN_NORMAL, + tStats.getSpeedMultiplier() * getPrimaryMaterial(aStack).mToolSpeed + * (1000f / 20f)) + * (50.0F + (10.0F * getToolCombatDamage(aStack)))))) + + EnumChatFormatting.GRAY)); + aList.add( + tOffset + 11, + EnumChatFormatting.LIGHT_PURPLE + String.format( + transItem("007", "Energy from Optimal Gas Flow: %s EU/t"), + "" + EnumChatFormatting.GOLD + + formatNumbers( + GT_Utility.safeInt( + (long) (Math.max( + Float.MIN_NORMAL, + tStats.getSpeedMultiplier() * getPrimaryMaterial(aStack).mToolSpeed + * 50 + * getPrimaryMaterial(aStack).mGasMultiplier) + * (50.0F + (10.0F * getToolCombatDamage(aStack))) + / 100))) + + EnumChatFormatting.GRAY)); + aList.add( + tOffset + 12, + EnumChatFormatting.LIGHT_PURPLE + String.format( + transItem("008", "Energy from Optimal Plasma Flow: %s EU/t"), + "" + EnumChatFormatting.GOLD + + formatNumbers( + GT_Utility.safeInt( + (long) (Math.max( + Float.MIN_NORMAL, + tStats.getSpeedMultiplier() * getPrimaryMaterial(aStack).mToolSpeed + * 2000 + * getPrimaryMaterial(aStack).mPlasmaMultiplier) + * (50.0F + (10.0F * getToolCombatDamage(aStack))) + * (1.05 / 100)))) + + EnumChatFormatting.GRAY)); + aList.add( + tOffset + 14, + EnumChatFormatting.GRAY + "(EU/t values include efficiency and are not 100% accurate)"); + int toolQualityLevel = GT_MetaGenerated_Tool.getPrimaryMaterial(aStack).mToolQuality; + int overflowMultiplier = 0; + if (toolQualityLevel >= 6) { + overflowMultiplier = 3; + } else if (toolQualityLevel >= 3) { + overflowMultiplier = 2; + } else { + overflowMultiplier = 1; + } + aList.add( + tOffset + 13, + EnumChatFormatting.LIGHT_PURPLE + String.format( + transItem("502", "Overflow Efficiency Tier: %s"), + "" + EnumChatFormatting.GOLD + overflowMultiplier + EnumChatFormatting.GRAY)); + + } else { + aList.add( + tOffset, + EnumChatFormatting.WHITE + String.format( + transItem("001", "Durability: %s/%s"), + "" + EnumChatFormatting.GREEN + formatNumbers(tMaxDamage - getToolDamage(aStack)) + " ", + " " + formatNumbers(tMaxDamage)) + EnumChatFormatting.GRAY); + aList.add( + tOffset + 1, + EnumChatFormatting.WHITE + String.format( + transItem("002", "%s lvl %s"), + tMaterial.mLocalizedName + EnumChatFormatting.YELLOW, + "" + getHarvestLevel(aStack, "")) + EnumChatFormatting.GRAY); + aList.add( + tOffset + 2, + EnumChatFormatting.WHITE + String.format( + transItem("003", "Attack Damage: %s"), + "" + EnumChatFormatting.BLUE + getToolCombatDamage(aStack)) + EnumChatFormatting.GRAY); + aList.add( + tOffset + 3, + EnumChatFormatting.WHITE + + String.format( + transItem("004", "Mining Speed: %s"), + "" + EnumChatFormatting.GOLD + + Math.max( + Float.MIN_NORMAL, + tStats.getSpeedMultiplier() * getPrimaryMaterial(aStack).mToolSpeed)) + + EnumChatFormatting.GRAY); + NBTTagCompound aNBT = aStack.getTagCompound(); + if (aNBT != null) { + aNBT = aNBT.getCompoundTag("GT.ToolStats"); + if (aNBT != null && aNBT.hasKey("Heat")) { + int tHeat = aNBT.getInteger("Heat"); + long tWorldTime = aPlayer.getEntityWorld() + .getWorldTime(); + if (aNBT.hasKey("HeatTime")) { + long tHeatTime = aNBT.getLong("HeatTime"); + if (tWorldTime > (tHeatTime + 10)) { + tHeat = (int) (tHeat - ((tWorldTime - tHeatTime) / 10)); + if (tHeat < 300 && tHeat > -10000) tHeat = 300; + } + aNBT.setLong("HeatTime", tWorldTime); + if (tHeat > -10000) aNBT.setInteger("Heat", tHeat); + } + + aList.add( + tOffset + 3, + EnumChatFormatting.RED + "Heat: " + + aNBT.getInteger("Heat") + + " K" + + EnumChatFormatting.GRAY); + } + } + } + } + } + + @Override + public Long[] getFluidContainerStats(ItemStack aStack) { + return null; + } + + @Override + public Long[] getElectricStats(ItemStack aStack) { + NBTTagCompound aNBT = aStack.getTagCompound(); + if (aNBT != null) { + aNBT = aNBT.getCompoundTag("GT.ToolStats"); + if (aNBT != null && aNBT.getBoolean("Electric")) return new Long[] { aNBT.getLong("MaxCharge"), + aNBT.getLong("Voltage"), aNBT.getLong("Tier"), aNBT.getLong("SpecialData") }; + } + return null; + } + + public float getToolCombatDamage(ItemStack aStack) { + IToolStats tStats = getToolStats(aStack); + if (tStats == null) return 0; + return tStats.getBaseDamage() + getPrimaryMaterial(aStack).mToolQuality; + } + + @Override + public final boolean doDamageToItem(ItemStack aStack, int aVanillaDamage) { + return doDamage(aStack, aVanillaDamage * 100L); + } + + public final boolean doDamage(ItemStack aStack, long aAmount) { + if (!isItemStackUsable(aStack)) return false; + Long[] tElectric = getElectricStats(aStack); + if (tElectric == null) { + long tNewDamage = getToolDamage(aStack) + aAmount; + setToolDamage(aStack, tNewDamage); + if (tNewDamage >= getToolMaxDamage(aStack)) { + IToolStats tStats = getToolStats(aStack); + if (tStats == null || GT_Utility.setStack(aStack, tStats.getBrokenItem(aStack)) == null) { + if (tStats != null) GT_Utility.doSoundAtClient(tStats.getBreakingSound(), 1, 1.0F); + if (aStack.stackSize > 0) aStack.stackSize--; + } + } + return true; + } + if (use(aStack, (int) aAmount, null)) { + if (java.util.concurrent.ThreadLocalRandom.current() + .nextInt(0, 25) == 0) { + long tNewDamage = getToolDamage(aStack) + aAmount; + setToolDamage(aStack, tNewDamage); + if (tNewDamage >= getToolMaxDamage(aStack)) { + IToolStats tStats = getToolStats(aStack); + if (tStats == null || GT_Utility.setStack(aStack, tStats.getBrokenItem(aStack)) == null) { + if (tStats != null) GT_Utility.doSoundAtClient(tStats.getBreakingSound(), 1, 1.0F); + if (aStack.stackSize > 0) aStack.stackSize--; + } + } + } + return true; + } + return false; + } + + @Override + public float getDigSpeed(ItemStack aStack, Block aBlock, int aMetaData) { + if (!isItemStackUsable(aStack)) return 0.0F; + IToolStats tStats = getToolStats(aStack); + if (tStats == null || Math.max(0, getHarvestLevel(aStack, "")) < aBlock.getHarvestLevel(aMetaData)) return 0.0F; + return tStats.isMinableBlock(aBlock, (byte) aMetaData) + ? Math.max(Float.MIN_NORMAL, tStats.getSpeedMultiplier() * getPrimaryMaterial(aStack).mToolSpeed) + : 0.0F; + } + + @Override + public final boolean canHarvestBlock(Block aBlock, ItemStack aStack) { + return getDigSpeed(aStack, aBlock, (byte) 0) > 0.0F; + } + + @Override + public final int getHarvestLevel(ItemStack aStack, String aToolClass) { + IToolStats tStats = getToolStats(aStack); + return tStats == null ? -1 : tStats.getBaseQuality() + getPrimaryMaterial(aStack).mToolQuality; + } + + @Override + public boolean onBlockDestroyed(ItemStack aStack, World aWorld, Block aBlock, int aX, int aY, int aZ, + EntityLivingBase aPlayer) { + if (!isItemStackUsable(aStack)) return false; + IToolStats tStats = getToolStats(aStack); + if (tStats == null) return false; + GT_Utility.doSoundAtClient(tStats.getMiningSound(), 1, 1.0F); + doDamage( + aStack, + (int) Math.max(1, aBlock.getBlockHardness(aWorld, aX, aY, aZ) * tStats.getToolDamagePerBlockBreak())); + return getDigSpeed(aStack, aBlock, aWorld.getBlockMetadata(aX, aY, aZ)) > 0.0F; + } + + @Override + public final ItemStack getContainerItem(ItemStack aStack) { + return getContainerItem(aStack, true); + } + + @Override + public final boolean hasContainerItem(ItemStack aStack) { + return getContainerItem(aStack, false) != null; + } + + private ItemStack getContainerItem(ItemStack aStack, boolean playSound) { + if (!isItemStackUsable(aStack)) return null; + aStack = GT_Utility.copyAmount(1, aStack); + IToolStats tStats = getToolStats(aStack); + if (tStats == null) return null; + doDamage(aStack, tStats.getToolDamagePerContainerCraft()); + aStack = aStack.stackSize > 0 ? aStack : null; + if (playSound && GT_Mod.gregtechproxy.mTicksUntilNextCraftSound <= 0) { + GT_Mod.gregtechproxy.mTicksUntilNextCraftSound = 10; + String sound = (aStack == null) ? tStats.getBreakingSound() : tStats.getCraftingSound(); + GT_Utility.doSoundAtClient(sound, 1, 1.0F); + } + return aStack; + } + + public IToolStats getToolStats(ItemStack aStack) { + isItemStackUsable(aStack); + return getToolStatsInternal(aStack); + } + + public byte getToolMaxMode(ItemStack aStack) { + IToolStats stats = getToolStats(aStack); + if (stats != null) { + return stats.getMaxMode(); + } + return 1; + } + + private IToolStats getToolStatsInternal(ItemStack aStack) { + return aStack == null ? null : mToolStats.get((short) aStack.getItemDamage()); + } + + @Override + public float getSaplingModifier(ItemStack aStack, World aWorld, EntityPlayer aPlayer, int aX, int aY, int aZ) { + IToolStats tStats = getToolStats(aStack); + return tStats != null && tStats.isGrafter() ? Math.min(100.0F, (1 + getHarvestLevel(aStack, "")) * 20.0F) + : 0.0F; + } + + @Override + public boolean canWhack(EntityPlayer aPlayer, ItemStack aStack, int aX, int aY, int aZ) { + if (!isItemStackUsable(aStack)) return false; + IToolStats tStats = getToolStats(aStack); + return tStats != null && tStats.isCrowbar(); + } + + @Override + public void onWhack(EntityPlayer aPlayer, ItemStack aStack, int aX, int aY, int aZ) { + IToolStats tStats = getToolStats(aStack); + if (tStats != null) doDamage(aStack, tStats.getToolDamagePerEntityAttack()); + } + + @Override + public boolean canWrench(EntityPlayer player, int x, int y, int z) { + if (player == null) return false; + return canWrench(player.getHeldItem(), player, x, y, z); + } + + @Override + public boolean canWrench(ItemStack wrench, EntityPlayer player, int x, int y, int z) { + if (wrench == null) return false; + if (!isItemStackUsable(wrench)) return false; + IToolStats tStats = getToolStats(player.getCurrentEquippedItem()); + return tStats != null && tStats.isWrench(); + } + + @Override + public void wrenchUsed(EntityPlayer player, int x, int y, int z) { + if (player == null) return; + if (player.getCurrentEquippedItem() == null) return; + IToolStats tStats = getToolStats(player.getCurrentEquippedItem()); + if (tStats != null) doDamage(player.getCurrentEquippedItem(), tStats.getToolDamagePerEntityAttack()); + } + + @Override + public boolean canUse(ItemStack stack, EntityPlayer player, int x, int y, int z) { + return canWrench(player, x, y, z); + } + + // ProjectRed screwdriver + @Override + public boolean canUse(EntityPlayer player, ItemStack stack) { + if (player == null) return false; + if (GT_Utility.isStackInvalid(stack) || !isItemStackUsable(stack)) return false; + IToolStats tStats = getToolStats(stack); + return tStats != null && tStats.isScrewdriver(); + } + + @Override + public void damageScrewdriver(EntityPlayer player, ItemStack stack) { + if (player == null) return; + if (GT_Utility.isStackInvalid(stack) || !isItemStackUsable(stack)) return; + IToolStats tStats = getToolStats(stack); + if (tStats != null) doDamage(stack, tStats.getToolDamagePerEntityAttack()); + } + + @Override + public void used(ItemStack stack, EntityPlayer player, int x, int y, int z) { + wrenchUsed(player, x, y, z); + } + + @Override + public boolean shouldHideFacades(ItemStack stack, EntityPlayer player) { + if (player == null) return false; + if (player.getCurrentEquippedItem() == null) return false; + if (!isItemStackUsable(player.getCurrentEquippedItem())) return false; + IToolStats tStats = getToolStats(player.getCurrentEquippedItem()); + return tStats.isWrench(); + } + + @Override + public boolean canLink(EntityPlayer aPlayer, ItemStack aStack, EntityMinecart cart) { + if (!isItemStackUsable(aStack)) return false; + IToolStats tStats = getToolStats(aStack); + return tStats != null && tStats.isCrowbar(); + } + + @Override + public void onLink(EntityPlayer aPlayer, ItemStack aStack, EntityMinecart cart) { + IToolStats tStats = getToolStats(aStack); + if (tStats != null) doDamage(aStack, tStats.getToolDamagePerEntityAttack()); + } + + @Override + public boolean canBoost(EntityPlayer aPlayer, ItemStack aStack, EntityMinecart cart) { + if (!isItemStackUsable(aStack)) return false; + IToolStats tStats = getToolStats(aStack); + return tStats != null && tStats.isCrowbar(); + } + + @Override + public void onBoost(EntityPlayer aPlayer, ItemStack aStack, EntityMinecart cart) { + IToolStats tStats = getToolStats(aStack); + if (tStats != null) doDamage(aStack, tStats.getToolDamagePerEntityAttack()); + } + + @Override + public void onCreated(ItemStack aStack, World aWorld, EntityPlayer aPlayer) { + IToolStats tStats = getToolStats(aStack); + if (tStats != null && aPlayer != null) tStats.onToolCrafted(aStack, aPlayer); + super.onCreated(aStack, aWorld, aPlayer); + } + + @Override + public final boolean doesContainerItemLeaveCraftingGrid(ItemStack aStack) { + return false; + } + + @Override + public final int getItemStackLimit(ItemStack aStack) { + return 1; + } + + @Override + public boolean isFull3D() { + return true; + } + + @Override + public boolean isItemStackUsable(ItemStack aStack) { + IToolStats tStats = getToolStatsInternal(aStack); + if (aStack.getItemDamage() % 2 != 0 || tStats == null) { + NBTTagCompound aNBT = aStack.getTagCompound(); + if (aNBT != null) aNBT.removeTag("ench"); + return false; + } + Materials aMaterial = getPrimaryMaterial(aStack); + HashMap<Integer, Integer> tMap = new HashMap<>(), tResult = new HashMap<>(); + if (aMaterial.mEnchantmentTools != null) { + tMap.put(aMaterial.mEnchantmentTools.effectId, (int) aMaterial.mEnchantmentToolsLevel); + if (aMaterial.mEnchantmentTools == Enchantment.fortune) + tMap.put(Enchantment.looting.effectId, (int) aMaterial.mEnchantmentToolsLevel); + if (aMaterial.mEnchantmentTools == Enchantment.knockback) + tMap.put(Enchantment.power.effectId, (int) aMaterial.mEnchantmentToolsLevel); + if (aMaterial.mEnchantmentTools == Enchantment.fireAspect) + tMap.put(Enchantment.flame.effectId, (int) aMaterial.mEnchantmentToolsLevel); + } + Enchantment[] tEnchants = tStats.getEnchantments(aStack); + int[] tLevels = tStats.getEnchantmentLevels(aStack); + for (int i = 0; i < tEnchants.length; i++) if (tLevels[i] > 0) { + Integer tLevel = tMap.get(tEnchants[i].effectId); + tMap.put( + tEnchants[i].effectId, + tLevel == null ? tLevels[i] : tLevel == tLevels[i] ? tLevel + 1 : Math.max(tLevel, tLevels[i])); + } + for (Entry<Integer, Integer> tEntry : tMap.entrySet()) { + if (tEntry.getKey() == 33 || (tEntry.getKey() == 20 && tEntry.getValue() > 2) + || tEntry.getKey() == Enchantment_Radioactivity.INSTANCE.effectId) + tResult.put(tEntry.getKey(), tEntry.getValue()); + else switch (Enchantment.enchantmentsList[tEntry.getKey()].type) { + case weapon: + if (tStats.isWeapon()) tResult.put(tEntry.getKey(), tEntry.getValue()); + break; + case all: + tResult.put(tEntry.getKey(), tEntry.getValue()); + break; + case armor: + case armor_feet: + case armor_head: + case armor_legs: + case armor_torso: + break; + case bow: + if (tStats.isRangedWeapon()) tResult.put(tEntry.getKey(), tEntry.getValue()); + break; + case breakable: + break; + case fishing_rod: + break; + case digger: + if (tStats.isMiningTool()) tResult.put(tEntry.getKey(), tEntry.getValue()); + break; + } + } + EnchantmentHelper.setEnchantments(tResult, aStack); + return true; + } + + @Override + public String getItemStackDisplayName(ItemStack aStack) { + + String result = super.getItemStackDisplayName(aStack); + IToolStats toolStats = getToolStats(aStack); + if (toolStats != null) { + String toolName = toolStats.getToolTypeName(); + if (toolName == null) return result; + + String key = "gt." + toolName + ".mode." + getToolMode(aStack); + if (StatCollector.canTranslate(key)) { + result += " (" + StatCollector.translateToLocal(key) + ")"; + } + } + return result; + + } + + @Override + public short getChargedMetaData(ItemStack aStack) { + return (short) (aStack.getItemDamage() - (aStack.getItemDamage() % 2)); + } + + @Override + public short getEmptyMetaData(ItemStack aStack) { + NBTTagCompound aNBT = aStack.getTagCompound(); + if (aNBT != null) aNBT.removeTag("ench"); + return (short) (aStack.getItemDamage() + 1 - (aStack.getItemDamage() % 2)); + } + + @Override + public int getItemEnchantability() { + return 0; + } + + @Override + public boolean isBookEnchantable(ItemStack aStack, ItemStack aBook) { + return false; + } + + @Override + public boolean getIsRepairable(ItemStack aStack, ItemStack aMaterial) { + return false; + } +} diff --git a/src/main/java/gregtech/api/items/GT_RadioactiveCellIC_Item.java b/src/main/java/gregtech/api/items/GT_RadioactiveCellIC_Item.java new file mode 100644 index 0000000000..8698bac886 --- /dev/null +++ b/src/main/java/gregtech/api/items/GT_RadioactiveCellIC_Item.java @@ -0,0 +1,196 @@ +package gregtech.api.items; + +import java.util.ArrayList; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.item.ItemStack; +import net.minecraft.world.World; + +import gregtech.api.enums.GT_Values; +import gregtech.api.util.GT_Utility; +import ic2.api.reactor.IReactor; +import ic2.api.reactor.IReactorComponent; +import ic2.core.IC2Potion; + +public class GT_RadioactiveCellIC_Item extends GT_RadioactiveCell_Item implements IReactorComponent { + + private static final int MYSTERIOUS_MULTIPLIER_HEAT = 4; + public final int numberOfCells; + public final float sEnergy; + public final int sRadiation; + public final float sHeat; + public final ItemStack sDepleted; + public final boolean sMox; + + public GT_RadioactiveCellIC_Item(String aUnlocalized, String aEnglish, int aCellcount, int maxDamage, float aEnergy, + int aRadiation, float aHeat, ItemStack aDepleted, boolean aMox) { + super(aUnlocalized, aEnglish, aCellcount); + setMaxStackSize(64); + this.maxDmg = maxDamage; + this.numberOfCells = aCellcount; + this.sEnergy = aEnergy; + this.sRadiation = aRadiation; + this.sHeat = aHeat; + this.sDepleted = aDepleted; + this.sMox = aMox; + if (aDepleted != null && aEnergy > 0 && aHeat > 0) { + // avoid adding depleted cells to recipe map + GT_Values.RA.addIC2ReactorFuelCell( + new ItemStack(this), + aDepleted, + aMox, + aHeat * MYSTERIOUS_MULTIPLIER_HEAT, + aEnergy, + aCellcount); + } + } + + private static int checkPulseable(IReactor reactor, int x, int y, ItemStack me, int mex, int mey, boolean heatrun) { + ItemStack other = reactor.getItemAt(x, y); + if ((other != null) && ((other.getItem() instanceof IReactorComponent)) + && (((IReactorComponent) other.getItem()) + .acceptUraniumPulse(reactor, other, me, x, y, mex, mey, heatrun))) { + return 1; + } + return 0; + } + + @Override + public void processChamber(IReactor reactor, ItemStack yourStack, int x, int y, boolean heatrun) { + if (!reactor.produceEnergy()) { + return; + } + for (int iteration = 0; iteration < this.numberOfCells; iteration++) { + int pulses = 1 + this.numberOfCells / 2; + if (!heatrun) { + for (int i = 0; i < pulses; i++) { + acceptUraniumPulse(reactor, yourStack, yourStack, x, y, x, y, heatrun); + } + checkPulseable(reactor, x - 1, y, yourStack, x, y, heatrun); + checkPulseable(reactor, x + 1, y, yourStack, x, y, heatrun); + checkPulseable(reactor, x, y - 1, yourStack, x, y, heatrun); + checkPulseable(reactor, x, y + 1, yourStack, x, y, heatrun); + } else { + pulses += checkPulseable(reactor, x - 1, y, yourStack, x, y, heatrun) + + checkPulseable(reactor, x + 1, y, yourStack, x, y, heatrun) + + checkPulseable(reactor, x, y - 1, yourStack, x, y, heatrun) + + checkPulseable(reactor, x, y + 1, yourStack, x, y, heatrun); + + // int heat = sumUp(pulses) * 4; + + int heat = triangularNumber(pulses) * MYSTERIOUS_MULTIPLIER_HEAT; + + heat = getFinalHeat(reactor, yourStack, x, y, heat); + + ArrayList<ItemStackCoord> heatAcceptors = new ArrayList<>(); + checkHeatAcceptor(reactor, x - 1, y, heatAcceptors); + checkHeatAcceptor(reactor, x + 1, y, heatAcceptors); + checkHeatAcceptor(reactor, x, y - 1, heatAcceptors); + checkHeatAcceptor(reactor, x, y + 1, heatAcceptors); + heat = Math.round(heat * sHeat); + while ((heatAcceptors.size() > 0) && (heat > 0)) { + + int dheat = heat / heatAcceptors.size(); + heat -= dheat; + dheat = ((IReactorComponent) heatAcceptors.get(0).stack.getItem()).alterHeat( + reactor, + heatAcceptors.get(0).stack, + heatAcceptors.get(0).x, + heatAcceptors.get(0).y, + dheat); + heat += dheat; + heatAcceptors.remove(0); + } + if (heat > 0) { + reactor.addHeat(heat); + } + } + } + if (getDamageOfStack(yourStack) >= getMaxDamageEx() - 1) { + reactor.setItemAt(x, y, sDepleted.copy()); + } else if (heatrun) { + damageItemStack(yourStack, 1); + } + } + + protected int getFinalHeat(IReactor reactor, ItemStack stack, int x, int y, int heat) { + if (sMox && reactor.isFluidCooled()) { + float breedereffectiveness = (float) reactor.getHeat() / (float) reactor.getMaxHeat(); + if (breedereffectiveness > 0.5D) { + heat *= 2; + } + } + return heat; + } + + private void checkHeatAcceptor(IReactor reactor, int x, int y, ArrayList<ItemStackCoord> heatAcceptors) { + ItemStack thing = reactor.getItemAt(x, y); + if ((thing != null) && ((thing.getItem() instanceof IReactorComponent)) + && (((IReactorComponent) thing.getItem()).canStoreHeat(reactor, thing, x, y))) { + heatAcceptors.add(new ItemStackCoord(thing, x, y)); + } + } + + @Override + public boolean acceptUraniumPulse(IReactor reactor, ItemStack yourStack, ItemStack pulsingStack, int youX, int youY, + int pulseX, int pulseY, boolean heatrun) { + if (!heatrun) { + if (sMox) { + float breedereffectiveness = (float) reactor.getHeat() / (float) reactor.getMaxHeat(); + float ReaktorOutput = 1.5F * breedereffectiveness + 1.0F; + reactor.addOutput(ReaktorOutput * this.sEnergy); + } else { + reactor.addOutput(1.0F * this.sEnergy); + } + } + return true; + } + + @Override + public boolean canStoreHeat(IReactor reactor, ItemStack yourStack, int x, int y) { + return false; + } + + @Override + public int getMaxHeat(IReactor reactor, ItemStack yourStack, int x, int y) { + return 0; + } + + @Override + public int getCurrentHeat(IReactor reactor, ItemStack yourStack, int x, int y) { + return 0; + } + + @Override + public int alterHeat(IReactor reactor, ItemStack yourStack, int x, int y, int heat) { + return heat; + } + + @Override + public float influenceExplosion(IReactor reactor, ItemStack yourStack) { + return 2 * this.numberOfCells; + } + + @Override + public void onUpdate(ItemStack stack, World world, Entity entity, int slotIndex, boolean isCurrentItem) { + if (this.sRadiation > 0 && (entity instanceof EntityLivingBase entityLiving)) { + if (!GT_Utility.isWearingFullRadioHazmat(entityLiving)) { + IC2Potion.radiation.applyTo(entityLiving, sRadiation * 20, sRadiation * 10); + } + } + } + + private static class ItemStackCoord { + + public ItemStack stack; + public int x; + public int y; + + public ItemStackCoord(ItemStack stack1, int x1, int y1) { + this.stack = stack1; + this.x = x1; + this.y = y1; + } + } +} diff --git a/src/main/java/gregtech/api/items/GT_RadioactiveCell_Item.java b/src/main/java/gregtech/api/items/GT_RadioactiveCell_Item.java new file mode 100644 index 0000000000..c927737889 --- /dev/null +++ b/src/main/java/gregtech/api/items/GT_RadioactiveCell_Item.java @@ -0,0 +1,159 @@ +package gregtech.api.items; + +import static gregtech.api.util.GT_Utility.formatNumbers; + +import java.util.List; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.EnumChatFormatting; + +import gregtech.common.items.GT_DepletetCell_Item; +import ic2.api.item.IBoxable; +import ic2.core.util.StackUtil; + +public class GT_RadioactiveCell_Item extends GT_Generic_Item implements IBoxable { + + protected int cellCount; + protected int maxDmg; + protected int dura; + + public GT_RadioactiveCell_Item(String aUnlocalized, String aEnglish, int aCellcount) { + super(aUnlocalized, aEnglish, null); + this.setMaxStackSize(64); + this.setMaxDamage(100); + setNoRepair(); + this.cellCount = Math.max(1, aCellcount); + } + + public static int getDurabilityOfStack(ItemStack aStack) { + NBTTagCompound tNBT = aStack.getTagCompound(); + if (tNBT == null) { + tNBT = new NBTTagCompound(); + aStack.setTagCompound(tNBT); + } + return tNBT.getInteger("advDmg"); + } + + protected static int sumUp(int a) { + int b = 0; + for (int c = 1; c <= a; c++) { + b += c; + } + return b; + } + + protected static int triangularNumber(int x) { + return (x * x + x) / 2; + } + + protected boolean outputPulseForStack(ItemStack aStack) { + NBTTagCompound tNBT = aStack.getTagCompound(); + if (tNBT == null) { + tNBT = new NBTTagCompound(); + aStack.setTagCompound(tNBT); + } + tNBT.setInteger("output", tNBT.getInteger("output") + 1); + return false; // (this.pulserate > 0) || (tNBT.getInteger("output") % -this.pulserate == 0); + } + + protected boolean incrementPulseForStack(ItemStack aStack) { + NBTTagCompound tNBT = aStack.getTagCompound(); + if (tNBT == null) { + tNBT = new NBTTagCompound(); + aStack.setTagCompound(tNBT); + } + tNBT.setInteger("pulse", tNBT.getInteger("pulse") + 1); + return false; // (this.pulserate > 0) || (tNBT.getInteger("pulse") % -this.pulserate == 0); + } + + protected void setDurabilityForStack(ItemStack aStack, int aDurability) { + NBTTagCompound tNBT = aStack.getTagCompound(); + if (tNBT == null) { + tNBT = new NBTTagCompound(); + aStack.setTagCompound(tNBT); + } + tNBT.setInteger("durability", aDurability); + } + + public int getMaxNuclearDurability() { + return 0; // return this.maxDelay; + } + + public int func_77619_b() { + return 0; + } + + @Override + public boolean isBookEnchantable(ItemStack ingredient, ItemStack bookEnchant) { + return false; + } + + // getIsRepairable + public boolean func_82789_a(ItemStack toBeRepaired, ItemStack repairWith) { + return false; + } + + public void setDamageForStack(ItemStack stack, int advDmg) { + NBTTagCompound nbtData = StackUtil.getOrCreateNbtData(stack); + nbtData.setInteger("advDmg", advDmg); + if (this.maxDmg > 0) { + double p = (double) advDmg / (double) this.maxDmg; + int newDmg = (int) (stack.getMaxDamage() * p); + if (newDmg >= stack.getMaxDamage()) { + newDmg = stack.getMaxDamage() - 1; + } + stack.setItemDamage(newDmg); + this.dura = newDmg; + } + } + + public int getDamageOfStack(ItemStack stack) { + NBTTagCompound nbtData = StackUtil.getOrCreateNbtData(stack); + this.dura = nbtData.getInteger("advDmg"); + return this.dura; + } + + public int getControlTagOfStack(ItemStack stack) { + NBTTagCompound nbtData = StackUtil.getOrCreateNbtData(stack); + return nbtData.getInteger("tag"); + } + + public void setControlTagOfStack(ItemStack stack, int tag) { + NBTTagCompound nbtData = StackUtil.getOrCreateNbtData(stack); + nbtData.setInteger("tag", tag); + } + + public int getMaxDamageEx() { + return this.maxDmg; + } + + public void damageItemStack(ItemStack stack, int Dmg) { + setDamageForStack(stack, getDamageOfStack(stack) + Dmg); + } + + @Override + public void addAdditionalToolTips(List<String> aList, ItemStack aStack, EntityPlayer aPlayer) { + super.addAdditionalToolTips(aList, aStack, aPlayer); + // aList.add("Time left: " + (this.maxDelay - getDurabilityOfStack(aStack)) + " secs"); + int rDmg = getDurabilityOfStack(aStack) * 6 / this.maxDmg; + EnumChatFormatting color2 = switch (rDmg) { + case 0, 1 -> EnumChatFormatting.WHITE; + case 2, 3, 4 -> EnumChatFormatting.GRAY; + default -> EnumChatFormatting.DARK_GRAY; + }; + EnumChatFormatting color1 = this instanceof GT_DepletetCell_Item ? color2 = EnumChatFormatting.DARK_GRAY + : EnumChatFormatting.WHITE; + aList.add( + color1 + String.format( + transItem("001", "Durability: %s/%s"), + "" + color2 + formatNumbers(this.maxDmg - getDurabilityOfStack(aStack)) + color1, + "" + formatNumbers(this.maxDmg))); + } + + @Override + public boolean canBeStoredInToolbox(ItemStack itemstack) { + return true; + } +} diff --git a/src/main/java/gregtech/api/items/GT_Tool_Item.java b/src/main/java/gregtech/api/items/GT_Tool_Item.java new file mode 100644 index 0000000000..9f78bdc3fc --- /dev/null +++ b/src/main/java/gregtech/api/items/GT_Tool_Item.java @@ -0,0 +1,41 @@ +package gregtech.api.items; + +import net.minecraft.item.ItemStack; + +import gregtech.api.util.GT_ModHandler; + +/** + * This is just a basic Tool, which has normal durability and could break Blocks. + */ +public class GT_Tool_Item extends GT_Generic_Item { + + public GT_Tool_Item(String aUnlocalized, String aEnglish, String aTooltip, int aMaxDamage, int aEntityDamage, + boolean aSwingIfUsed) { + this(aUnlocalized, aEnglish, aTooltip, aMaxDamage, aEntityDamage, aSwingIfUsed, -1, -1); + } + + public GT_Tool_Item(String aUnlocalized, String aEnglish, String aTooltip, int aMaxDamage, int aEntityDamage, + boolean aSwingIfUsed, int aChargedGTID, int aDisChargedGTID) { + this( + aUnlocalized, + aEnglish, + aTooltip, + aMaxDamage, + aEntityDamage, + aSwingIfUsed, + aChargedGTID, + aDisChargedGTID, + 0, + 0.0F); + } + + public GT_Tool_Item(String aUnlocalized, String aEnglish, String aTooltip, int aMaxDamage, int aEntityDamage, + boolean aSwingIfUsed, int aChargedGTID, int aDisChargedGTID, int aToolQuality, float aToolStrength) { + super(aUnlocalized, aEnglish, aTooltip); + setMaxDamage(aMaxDamage); + setMaxStackSize(1); + setNoRepair(); + setFull3D(); + GT_ModHandler.registerBoxableItemToToolBox(new ItemStack(this)); + } +} diff --git a/src/main/java/gregtech/api/logic/AbstractProcessingLogic.java b/src/main/java/gregtech/api/logic/AbstractProcessingLogic.java new file mode 100644 index 0000000000..ae78bbacc2 --- /dev/null +++ b/src/main/java/gregtech/api/logic/AbstractProcessingLogic.java @@ -0,0 +1,346 @@ +package gregtech.api.logic; + +import java.util.function.Supplier; + +import javax.annotation.Nonnull; + +import net.minecraft.item.ItemStack; +import net.minecraftforge.fluids.FluidStack; + +import gregtech.api.interfaces.tileentity.IVoidable; +import gregtech.api.recipe.RecipeMap; +import gregtech.api.recipe.check.CheckRecipeResult; +import gregtech.api.recipe.check.CheckRecipeResultRegistry; +import gregtech.api.util.GT_OverclockCalculator; +import gregtech.api.util.GT_ParallelHelper; +import gregtech.api.util.GT_Recipe; + +/** + * Logic class to calculate result of recipe check from inputs. + */ +@SuppressWarnings({ "unused", "UnusedReturnValue" }) +public abstract class AbstractProcessingLogic<P extends AbstractProcessingLogic<P>> { + + protected IVoidable machine; + protected Supplier<RecipeMap<?>> recipeMapSupplier; + protected GT_Recipe lastRecipe; + protected RecipeMap<?> lastRecipeMap; + protected ItemStack[] outputItems; + protected FluidStack[] outputFluids; + protected long calculatedEut; + protected int duration; + protected long availableVoltage; + protected long availableAmperage; + protected int overClockTimeReduction = 1; + protected int overClockPowerIncrease = 2; + protected boolean protectItems; + protected boolean protectFluids; + protected int maxParallel = 1; + protected Supplier<Integer> maxParallelSupplier; + protected int calculatedParallels = 0; + protected int batchSize = 1; + protected float euModifier = 1.0f; + protected float speedBoost = 1.0f; + protected boolean amperageOC = true; + protected boolean isCleanroom; + + // #region Setters + + /** + * Overwrites item output result of the calculation. + */ + public P setOutputItems(ItemStack... itemOutputs) { + this.outputItems = itemOutputs; + return getThis(); + } + + /** + * Overwrites fluid output result of the calculation. + */ + public P setOutputFluids(FluidStack... fluidOutputs) { + this.outputFluids = fluidOutputs; + return getThis(); + } + + public P setIsCleanroom(boolean isCleanroom) { + this.isCleanroom = isCleanroom; + return getThis(); + } + + /** + * Sets max amount of parallel. + */ + public P setMaxParallel(int maxParallel) { + this.maxParallel = maxParallel; + return getThis(); + } + + /** + * Sets method to get max amount of parallel. + */ + public P setMaxParallelSupplier(Supplier<Integer> supplier) { + this.maxParallelSupplier = supplier; + return getThis(); + } + + /** + * Sets batch size for batch mode. + */ + public P setBatchSize(int size) { + this.batchSize = size; + return getThis(); + } + + public P setRecipeMap(RecipeMap<?> recipeMap) { + return setRecipeMapSupplier(() -> recipeMap); + } + + public P setRecipeMapSupplier(Supplier<RecipeMap<?>> supplier) { + this.recipeMapSupplier = supplier; + return getThis(); + } + + public P setEuModifier(float modifier) { + this.euModifier = modifier; + return getThis(); + } + + public P setSpeedBonus(float speedModifier) { + this.speedBoost = speedModifier; + return getThis(); + } + + /** + * Sets machine used for void protection logic. + */ + public P setMachine(IVoidable machine) { + this.machine = machine; + return getThis(); + } + + /** + * Overwrites duration result of the calculation. + */ + public P setDuration(int duration) { + this.duration = duration; + return getThis(); + } + + /** + * Overwrites EU/t result of the calculation. + */ + public P setCalculatedEut(long calculatedEut) { + this.calculatedEut = calculatedEut; + return getThis(); + } + + /** + * Sets voltage of the machine. It doesn't need to be actual voltage (excluding amperage) of the machine; + * For example, most of the multiblock machines set maximum possible input power (including amperage) as voltage + * and 1 as amperage. That way recipemap search will be executed with overclocked voltage. + */ + public P setAvailableVoltage(long voltage) { + availableVoltage = voltage; + return getThis(); + } + + /** + * Sets amperage of the machine. This amperage doesn't involve in EU/t when searching recipemap. + * Useful for preventing tier skip but still considering amperage for parallel. + */ + public P setAvailableAmperage(long amperage) { + availableAmperage = amperage; + return getThis(); + } + + public P setVoidProtection(boolean protectItems, boolean protectFluids) { + this.protectItems = protectItems; + this.protectFluids = protectFluids; + return getThis(); + } + + /** + * Sets custom overclock ratio. 2/4 by default. + * Parameters represent number of bit shift, so 1 -> 2x, 2 -> 4x. + */ + public P setOverclock(int timeReduction, int powerIncrease) { + this.overClockTimeReduction = timeReduction; + this.overClockPowerIncrease = powerIncrease; + return getThis(); + } + + /** + * Sets overclock ratio to 4/4. + */ + public P enablePerfectOverclock() { + return this.setOverclock(2, 2); + } + + /** + * Sets whether the multi should use amperage to OC or not + */ + public P setAmperageOC(boolean amperageOC) { + this.amperageOC = amperageOC; + return getThis(); + } + + /** + * Clears calculated results (and provided machine inputs) to prepare for the next machine operation. + */ + public P clear() { + this.calculatedEut = 0; + this.duration = 0; + this.calculatedParallels = 0; + return getThis(); + } + + // #endregion + + // #region Logic + + /** + * Executes the recipe check: Find recipe from recipemap, Calculate parallel, overclock and outputs. + */ + @Nonnull + public abstract CheckRecipeResult process(); + + /** + * Refreshes recipemap to use. Remember to call this before {@link #process} to make sure correct recipemap is used. + * + * @return Recipemap to use now + */ + protected RecipeMap<?> preProcess() { + RecipeMap<?> recipeMap; + if (recipeMapSupplier == null) { + recipeMap = null; + } else { + recipeMap = recipeMapSupplier.get(); + } + if (lastRecipeMap != recipeMap) { + lastRecipe = null; + lastRecipeMap = recipeMap; + } + + if (maxParallelSupplier != null) { + maxParallel = maxParallelSupplier.get(); + } + + return recipeMap; + } + + /** + * Check has been succeeded, so it applies the recipe and calculated parameters. + * At this point, inputs have been already consumed. + */ + @Nonnull + protected CheckRecipeResult applyRecipe(@Nonnull GT_Recipe recipe, @Nonnull GT_ParallelHelper helper, + @Nonnull GT_OverclockCalculator calculator, @Nonnull CheckRecipeResult result) { + if (recipe.mCanBeBuffered) { + lastRecipe = recipe; + } else { + lastRecipe = null; + } + calculatedParallels = helper.getCurrentParallel(); + + if (calculator.getConsumption() == Long.MAX_VALUE) { + return CheckRecipeResultRegistry.POWER_OVERFLOW; + } + if (calculator.getDuration() == Integer.MAX_VALUE) { + return CheckRecipeResultRegistry.DURATION_OVERFLOW; + } + + calculatedEut = calculator.getConsumption(); + + double finalDuration = calculateDuration(recipe, helper, calculator); + if (finalDuration >= Integer.MAX_VALUE) { + return CheckRecipeResultRegistry.DURATION_OVERFLOW; + } + duration = (int) finalDuration; + + CheckRecipeResult hookResult = onRecipeStart(recipe); + if (!hookResult.wasSuccessful()) { + return hookResult; + } + + outputItems = helper.getItemOutputs(); + outputFluids = helper.getFluidOutputs(); + + return result; + } + + /** + * Override to tweak final duration that will be set as a result of this logic class. + */ + protected double calculateDuration(@Nonnull GT_Recipe recipe, @Nonnull GT_ParallelHelper helper, + @Nonnull GT_OverclockCalculator calculator) { + return calculator.getDuration() * helper.getDurationMultiplierDouble(); + } + + /** + * Override to do additional check for found recipe if needed. + */ + @Nonnull + protected CheckRecipeResult validateRecipe(@Nonnull GT_Recipe recipe) { + return CheckRecipeResultRegistry.SUCCESSFUL; + } + + /** + * Override to perform additional logic when recipe starts. + * + * This is called when the recipe processing logic has finished all + * checks, consumed all inputs, but has not yet set the outputs to + * be produced. Returning a result other than SUCCESSFUL will void + * all inputs! + */ + @Nonnull + protected CheckRecipeResult onRecipeStart(@Nonnull GT_Recipe recipe) { + return CheckRecipeResultRegistry.SUCCESSFUL; + } + + /** + * Override to tweak overclock logic if needed. + */ + @Nonnull + protected GT_OverclockCalculator createOverclockCalculator(@Nonnull GT_Recipe recipe) { + return new GT_OverclockCalculator().setRecipeEUt(recipe.mEUt) + .setAmperage(availableAmperage) + .setEUt(availableVoltage) + .setDuration(recipe.mDuration) + .setSpeedBoost(speedBoost) + .setEUtDiscount(euModifier) + .setAmperageOC(amperageOC) + .setDurationDecreasePerOC(overClockTimeReduction) + .setEUtIncreasePerOC(overClockPowerIncrease); + } + // #endregion + + // #region Getters + + public ItemStack[] getOutputItems() { + return outputItems; + } + + public FluidStack[] getOutputFluids() { + return outputFluids; + } + + public int getDuration() { + return duration; + } + + public long getCalculatedEut() { + return calculatedEut; + } + + public int getCurrentParallels() { + return calculatedParallels; + } + + @SuppressWarnings("unchecked") + @Nonnull + public P getThis() { + return (P) this; + } + + // #endregion +} diff --git a/src/main/java/gregtech/api/logic/ComplexParallelProcessingLogic.java b/src/main/java/gregtech/api/logic/ComplexParallelProcessingLogic.java new file mode 100644 index 0000000000..72a74ebd04 --- /dev/null +++ b/src/main/java/gregtech/api/logic/ComplexParallelProcessingLogic.java @@ -0,0 +1,121 @@ +package gregtech.api.logic; + +import java.util.stream.LongStream; + +import net.minecraft.item.ItemStack; +import net.minecraftforge.fluids.FluidStack; + +public class ComplexParallelProcessingLogic<P extends ComplexParallelProcessingLogic<P>> + extends MuTEProcessingLogic<P> { + + protected int maxComplexParallels; + protected ItemStack[][] outputItems; + protected FluidStack[][] outputFluids; + protected long[] calculatedEutValues; + protected int[] durations; + protected int[] progresses; + + public P setMaxComplexParallel(int maxComplexParallels) { + this.maxComplexParallels = maxComplexParallels; + reinitializeProcessingArrays(); + return getThis(); + } + + public ItemStack[] getOutputItems(int index) { + if (index >= 0 && index < maxComplexParallels) { + return outputItems[index]; + } + return null; + } + + public FluidStack[] getOutputFluids(int index) { + if (index >= 0 && index < maxComplexParallels) { + return outputFluids[index]; + } + return null; + } + + @Override + public boolean canWork() { + for (int i = 0; i < maxComplexParallels; i++) { + if (progresses[i] >= durations[i]) { + return machineHost.isAllowedToWork(); + } + } + return false; + } + + @Override + public long getCalculatedEut() { + return LongStream.of(this.calculatedEutValues) + .sum(); + } + + public int getDuration(int index) { + return durations[index]; + } + + public int getProgress(int index) { + return progresses[index]; + } + + @Override + public void progress() { + for (int i = 0; i < maxComplexParallels; i++) { + if (progresses[i] == durations[i]) { + progresses[i] = 0; + durations[i] = 0; + output(i); + continue; + } + progresses[i] = progresses[i] + 1; + } + } + + @Override + public void startCheck() { + for (int i = 0; i < maxComplexParallels; i++) { + if (durations[i] > 0) continue; + recipeResult = process(); + calculatedEutValues[i] = calculatedEut; + durations[i] = duration; + progresses[i] = 0; + outputItems[i] = getOutputItems(); + outputFluids[i] = getOutputFluids(); + } + } + + protected void output(int index) { + setOutputItems(getOutputItems(index)); + setOutputFluids(getOutputFluids(index)); + output(); + } + + protected void reinitializeProcessingArrays() { + ItemStack[][] oldOutputItems = outputItems; + FluidStack[][] oldOutputFluids = outputFluids; + long[] oldCalculatedEutValues = calculatedEutValues; + int[] oldDurations = durations; + int[] oldProgresses = progresses; + outputItems = new ItemStack[maxComplexParallels][]; + outputFluids = new FluidStack[maxComplexParallels][]; + calculatedEutValues = new long[maxComplexParallels]; + durations = new int[maxComplexParallels]; + progresses = new int[maxComplexParallels]; + for (int i = 0; i < oldOutputItems.length; i++) { + outputItems[i] = oldOutputItems[i]; + } + for (int i = 0; i < oldOutputFluids.length; i++) { + outputFluids[i] = oldOutputFluids[i]; + } + for (int i = 0; i < oldCalculatedEutValues.length; i++) { + calculatedEutValues[i] = oldCalculatedEutValues[i]; + } + for (int i = 0; i < oldDurations[i]; i++) { + durations[i] = oldDurations[i]; + } + for (int i = 0; i < oldProgresses.length; i++) { + progresses[i] = oldProgresses[i]; + } + } +} diff --git a/src/main/java/gregtech/api/logic/ControllerFluidLogic.java b/src/main/java/gregtech/api/logic/ControllerFluidLogic.java new file mode 100644 index 0000000000..211c1c2982 --- /dev/null +++ b/src/main/java/gregtech/api/logic/ControllerFluidLogic.java @@ -0,0 +1,152 @@ +package gregtech.api.logic; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraftforge.common.util.Constants; + +import org.apache.commons.lang3.tuple.Pair; + +/** + * Controller logic for Fluid inventories + * + * @author BlueWeabo + */ +public class ControllerFluidLogic { + + private final Map<UUID, FluidInventoryLogic> inventories = new HashMap<>(); + private final Set<Pair<UUID, FluidInventoryLogic>> unallocatedInventories = new HashSet<>(); + + public void addInventory(@Nonnull UUID id, @Nonnull FluidInventoryLogic inventory) { + Pair<UUID, FluidInventoryLogic> found = checkIfInventoryExistsAsUnallocated(inventory); + if (inventory.isUpgradeInventory() && found != null) { + unallocatedInventories.remove(found); + inventories.put(id, found.getRight()); + return; + } + inventories.put(id, inventory); + } + + @Nonnull + public UUID addInventory(@Nonnull FluidInventoryLogic inventory) { + Pair<UUID, FluidInventoryLogic> found = checkIfInventoryExistsAsUnallocated(inventory); + if (inventory.isUpgradeInventory() && found != null) { + unallocatedInventories.remove(found); + inventories.put(found.getLeft(), found.getRight()); + return Objects.requireNonNull(found.getLeft()); + } + UUID generatedUUID = Objects.requireNonNull(UUID.randomUUID()); + inventories.put(generatedUUID, inventory); + return generatedUUID; + } + + @Nullable + private Pair<UUID, FluidInventoryLogic> checkIfInventoryExistsAsUnallocated( + @Nonnull FluidInventoryLogic inventory) { + if (unallocatedInventories.size() == 0) { + return null; + } + return unallocatedInventories.stream() + .filter( + unallocated -> unallocated.getRight() + .getTier() == inventory.getTier()) + .findFirst() + .get(); + } + + /** + * Removes the inventory with said id and gives it back to be processed if needed. + */ + @Nonnull + public FluidInventoryLogic removeInventory(@Nonnull UUID id) { + return Objects.requireNonNull(inventories.remove(id)); + } + + @Nonnull + public FluidInventoryLogic getAllInventoryLogics() { + return new FluidInventoryLogic( + inventories.values() + .stream() + .map(inv -> inv.getInventory()) + .collect(Collectors.toList())); + } + + @Nonnull + public FluidInventoryLogic getInventoryLogic(@Nullable UUID id) { + if (id == null) return getAllInventoryLogics(); + return Objects.requireNonNull(inventories.getOrDefault(id, getAllInventoryLogics())); + } + + @Nonnull + public Set<Entry<UUID, FluidInventoryLogic>> getAllInventoryLogicsAsEntrySet() { + return Objects.requireNonNull(inventories.entrySet()); + } + + @Nonnull + public String getInventoryDisplayName(@Nullable UUID id) { + if (id == null) return ""; + FluidInventoryLogic logic = inventories.get(id); + if (logic == null) return ""; + String displayName = logic.getDisplayName(); + if (displayName == null) return Objects.requireNonNull(id.toString()); + return displayName; + } + + public void setInventoryDisplayName(@Nullable UUID id, @Nullable String displayName) { + if (id == null) return; + FluidInventoryLogic logic = inventories.get(id); + if (logic == null) return; + logic.setDisplayName(displayName); + } + + @Nonnull + public NBTTagCompound saveToNBT() { + NBTTagCompound nbt = new NBTTagCompound(); + NBTTagList inventoriesNBT = new NBTTagList(); + inventories.forEach((uuid, inventory) -> { + NBTTagCompound inventoryNBT = new NBTTagCompound(); + inventoryNBT.setTag("inventory", inventory.saveToNBT()); + inventoryNBT.setString("uuid", uuid.toString()); + inventoryNBT.setInteger( + "invSize", + inventory.getInventory() + .getTanks()); + inventoryNBT.setLong( + "tankCapacity", + inventory.getInventory() + .getTankCapacity(0)); + inventoriesNBT.appendTag(inventoryNBT); + }); + nbt.setTag("inventories", inventoriesNBT); + return nbt; + } + + public void loadFromNBT(@Nonnull NBTTagCompound nbt) { + NBTTagList inventoriesNBT = nbt.getTagList("inventories", Constants.NBT.TAG_COMPOUND); + if (inventoriesNBT == null) return; + for (int i = 0; i < inventoriesNBT.tagCount(); i++) { + NBTTagCompound inventoryNBT = inventoriesNBT.getCompoundTagAt(i); + UUID uuid = UUID.fromString(inventoryNBT.getString("uuid")); + FluidInventoryLogic inventory = new FluidInventoryLogic( + inventoryNBT.getInteger("invSize"), + inventoryNBT.getLong("tankCapacity")); + inventory.loadFromNBT(inventoryNBT.getCompoundTag("inventory")); + if (inventory.isUpgradeInventory()) { + unallocatedInventories.add(Pair.of(uuid, inventory)); + } else { + inventories.put(uuid, inventory); + } + } + } +} diff --git a/src/main/java/gregtech/api/logic/ControllerItemLogic.java b/src/main/java/gregtech/api/logic/ControllerItemLogic.java new file mode 100644 index 0000000000..2863c2f49c --- /dev/null +++ b/src/main/java/gregtech/api/logic/ControllerItemLogic.java @@ -0,0 +1,148 @@ +package gregtech.api.logic; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraftforge.common.util.Constants; + +import org.apache.commons.lang3.tuple.Pair; + +/** + * Logic of the Item logic for the controller. This is controlling all of the inventories. + * + * @author BlueWeabo + */ +public class ControllerItemLogic { + + private final Map<UUID, ItemInventoryLogic> inventories = new HashMap<>(); + private final Set<Pair<UUID, ItemInventoryLogic>> unallocatedInventories = new HashSet<>(); + + public void addInventory(@Nonnull UUID id, @Nonnull ItemInventoryLogic inventory) { + Pair<UUID, ItemInventoryLogic> found = checkIfInventoryExistsAsUnallocated(inventory); + if (inventory.isUpgradeInventory() && found != null) { + unallocatedInventories.remove(found); + inventories.put(id, found.getRight()); + return; + } + inventories.put(id, inventory); + } + + @Nonnull + public UUID addInventory(@Nonnull ItemInventoryLogic inventory) { + Pair<UUID, ItemInventoryLogic> found = checkIfInventoryExistsAsUnallocated(inventory); + if (inventory.isUpgradeInventory() && found != null) { + unallocatedInventories.remove(found); + inventories.put(found.getLeft(), found.getRight()); + return Objects.requireNonNull(found.getLeft()); + } + UUID generatedUUID = Objects.requireNonNull(UUID.randomUUID()); + inventories.put(generatedUUID, inventory); + return generatedUUID; + } + + @Nullable + private Pair<UUID, ItemInventoryLogic> checkIfInventoryExistsAsUnallocated(@Nonnull ItemInventoryLogic inventory) { + if (unallocatedInventories.size() == 0) { + return null; + } + return unallocatedInventories.stream() + .filter( + unallocated -> unallocated.getRight() + .getTier() == inventory.getTier() + && unallocated.getRight() + .getSlots() == inventory.getSlots()) + .findFirst() + .get(); + } + + /** + * Removes the inventory with said id and gives it back to be processed if needed. + */ + @Nonnull + public ItemInventoryLogic removeInventory(@Nonnull UUID id) { + return Objects.requireNonNull(inventories.remove(id)); + } + + @Nonnull + public ItemInventoryLogic getAllInventoryLogics() { + return new ItemInventoryLogic( + inventories.values() + .stream() + .map(inv -> inv.getInventory()) + .collect(Collectors.toList())); + } + + @Nonnull + public ItemInventoryLogic getInventoryLogic(@Nullable UUID id) { + if (id == null) return getAllInventoryLogics(); + return Objects.requireNonNull(inventories.getOrDefault(id, getAllInventoryLogics())); + } + + @Nonnull + public Set<Entry<UUID, ItemInventoryLogic>> getAllInventoryLogicsAsEntrySet() { + return Objects.requireNonNull(inventories.entrySet()); + } + + @Nullable + public String getInventoryDisplayName(@Nullable UUID id) { + if (id == null) return ""; + ItemInventoryLogic logic = inventories.get(id); + if (logic == null) return ""; + String displayName = logic.getDisplayName(); + if (displayName == null) return Objects.requireNonNull(id.toString()); + return displayName; + } + + public void setInventoryDisplayName(@Nullable UUID id, @Nullable String displayName) { + if (id == null) return; + ItemInventoryLogic logic = inventories.get(id); + if (logic == null) return; + logic.setDisplayName(displayName); + } + + @Nonnull + public NBTTagCompound saveToNBT() { + NBTTagCompound nbt = new NBTTagCompound(); + NBTTagList inventoriesNBT = new NBTTagList(); + inventories.forEach((uuid, inventory) -> { + NBTTagCompound inventoryNBT = new NBTTagCompound(); + inventoryNBT.setTag("inventory", inventory.saveToNBT()); + inventoryNBT.setString("uuid", uuid.toString()); + inventoryNBT.setInteger( + "invSize", + inventory.getInventory() + .getSlots()); + inventoriesNBT.appendTag(inventoryNBT); + }); + nbt.setTag("inventories", inventoriesNBT); + return nbt; + } + + public void loadFromNBT(@Nonnull NBTTagCompound nbt) { + NBTTagList inventoriesNBT = nbt.getTagList("inventories", Constants.NBT.TAG_COMPOUND); + if (inventoriesNBT == null) return; + for (int i = 0; i < inventoriesNBT.tagCount(); i++) { + NBTTagCompound inventoryNBT = inventoriesNBT.getCompoundTagAt(i); + UUID uuid = UUID.fromString(inventoryNBT.getString("uuid")); + ItemInventoryLogic inventory = new ItemInventoryLogic(inventoryNBT.getInteger("invSize")); + NBTTagCompound internalInventoryNBT = inventoryNBT.getCompoundTag("inventory"); + if (internalInventoryNBT != null) inventory.loadFromNBT(internalInventoryNBT); + if (inventory.isUpgradeInventory()) { + unallocatedInventories.add(Pair.of(uuid, inventory)); + } else { + inventories.put(uuid, inventory); + } + } + } +} diff --git a/src/main/java/gregtech/api/logic/FluidInventoryLogic.java b/src/main/java/gregtech/api/logic/FluidInventoryLogic.java new file mode 100644 index 0000000000..88c0c954ec --- /dev/null +++ b/src/main/java/gregtech/api/logic/FluidInventoryLogic.java @@ -0,0 +1,269 @@ +package gregtech.api.logic; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Collectors; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraftforge.common.util.Constants; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidStack; + +import com.gtnewhorizons.modularui.api.fluids.FluidTanksHandler; +import com.gtnewhorizons.modularui.api.fluids.IFluidTankLong; +import com.gtnewhorizons.modularui.api.fluids.IFluidTanksHandler; +import com.gtnewhorizons.modularui.api.fluids.ListFluidHandler; +import com.gtnewhorizons.modularui.api.math.Size; +import com.gtnewhorizons.modularui.api.widget.Widget; +import com.gtnewhorizons.modularui.common.widget.FluidSlotWidget; +import com.gtnewhorizons.modularui.common.widget.Scrollable; + +/** + * Generic Fluid logic for MuTEs. + * + * @author BlueWeabo + */ +public class FluidInventoryLogic { + + private static final int DEFAULT_COLUMNS_PER_ROW = 4; + private static final int POSITION_INTERVAL = 18; + private static final Size SIZE = new Size(18, 18); + + protected String displayName = ""; + @Nonnull + protected final IFluidTanksHandler inventory; + protected final Map<Fluid, IFluidTankLong> fluidToTankMap; + protected int tier = 0; + protected boolean isUpgradeInventory = false; + + public FluidInventoryLogic(int numberOfSlots, long capacityOfEachTank) { + this(new FluidTanksHandler(numberOfSlots, capacityOfEachTank), 0, false); + } + + public FluidInventoryLogic(int numberOfSlots, long capacityOfEachTank, int tier) { + this(new FluidTanksHandler(numberOfSlots, capacityOfEachTank), tier, false); + } + + public FluidInventoryLogic(int numberOfSlots, long capacityOfEachTank, int tier, boolean isUpgradeInventory) { + this(new FluidTanksHandler(numberOfSlots, capacityOfEachTank), tier, isUpgradeInventory); + } + + public FluidInventoryLogic(@Nonnull IFluidTanksHandler inventory, int tier, boolean isUpgradeInventory) { + this.inventory = inventory; + fluidToTankMap = new HashMap<>(inventory.getTanks()); + this.tier = tier; + this.isUpgradeInventory = isUpgradeInventory; + } + + public FluidInventoryLogic(Collection<IFluidTanksHandler> inventories) { + this(new ListFluidHandler(inventories), -1, false); + } + + @Nullable + public String getDisplayName() { + return displayName; + } + + public int getTier() { + return tier; + } + + public boolean isUpgradeInventory() { + return isUpgradeInventory; + } + + public void setDisplayName(@Nullable String displayName) { + this.displayName = displayName; + } + + /** + * + * @return The Fluid Inventory Logic as an NBTTagList to be saved in another nbt as how one wants. + */ + @Nonnull + public NBTTagCompound saveToNBT() { + final NBTTagCompound nbt = new NBTTagCompound(); + final NBTTagList tList = new NBTTagList(); + for (int tankNumber = 0; tankNumber < inventory.getTanks(); tankNumber++) { + final IFluidTankLong tank = inventory.getFluidTank(tankNumber); + if (tank == null) continue; + + final NBTTagCompound tag = new NBTTagCompound(); + tag.setByte("s", (byte) tankNumber); + tank.saveToNBT(tag); + tList.appendTag(tag); + } + nbt.setTag("inventory", tList); + nbt.setInteger("tier", tier); + if (displayName != null) { + nbt.setString("displayName", displayName); + } + nbt.setBoolean("isUpgradeInventory", isUpgradeInventory); + return nbt; + } + + /** + * Loads the Item Inventory Logic from an NBTTagList. + */ + public void loadFromNBT(@Nonnull NBTTagCompound nbt) { + NBTTagList nbtList = nbt.getTagList("inventory", Constants.NBT.TAG_COMPOUND); + for (int i = 0; i < nbtList.tagCount(); i++) { + final NBTTagCompound tankNBT = nbtList.getCompoundTagAt(i); + final int tank = tankNBT.getShort("s"); + if (tank >= 0 && tank < inventory.getTanks()) inventory.getFluidTank(tank) + .loadFromNBT(tankNBT); + if (inventory.getFluidInTank(tank) != null) { + fluidToTankMap.put(inventory.getFluidInTank(tank), inventory.getFluidTank(tank)); + } + } + tier = nbt.getInteger("tier"); + if (nbt.hasKey("displayName")) { + displayName = nbt.getString("displayName"); + } + isUpgradeInventory = nbt.getBoolean("isUpgradeInventory"); + } + + @Nonnull + public IFluidTanksHandler getInventory() { + return inventory; + } + + @Nonnull + public FluidStack[] getStoredFluids() { + final FluidStack[] fluids = inventory.getFluids() + .stream() + .filter(fluid -> fluid != null) + .collect(Collectors.toList()) + .toArray(new FluidStack[0]); + if (fluids == null) { + return new FluidStack[0]; + } + return fluids; + } + + public boolean isFluidValid(@Nullable Fluid fluid) { + return fluid != null; + } + + /** + * @param fluid What we are trying to input + * @param amount amount of fluid we are trying to put + * @return amount of fluid filled into the tank + */ + public long fill(@Nullable Fluid fluid, long amount, boolean simulate) { + if (!isFluidValid(fluid)) return 0; + IFluidTankLong tank = fluidToTankMap.get(fluid); + if (tank != null) { + return tank.fill(fluid, amount, !simulate); + } + int tankNumber = 0; + tank = inventory.getFluidTank(tankNumber++); + while (tank.getStoredFluid() != fluid && tank.getStoredFluid() != null) { + tank = inventory.getFluidTank(tankNumber++); + } + fluidToTankMap.put(fluid, tank); + return tank.fill(fluid, amount, !simulate); + } + + @Nullable + public FluidStack fill(@Nullable FluidStack fluid) { + if (fluid == null) return null; + for (int i = 0; i < inventory.getTanks(); i++) { + fill(fluid.getFluid(), fluid.amount, false); + } + return fluid; + } + + /** + * Try and drain the first fluid found for that amount. Used by GT_Cover_Pump + * + * @param amount Fluid to drain from the tank + * @return A fluidstack with the possible amount drained + */ + @Nullable + public FluidStack drain(long amount, boolean simulate) { + for (int i = 0; i < inventory.getTanks(); i++) { + Fluid fluid = inventory.getFluidInTank(i); + FluidStack drained = drain(fluid, amount, simulate); + if (drained != null) return drained; + } + + return null; + } + + @Nullable + public FluidStack drain(Fluid fluid, long amount, boolean simulate) { + if (!isFluidValid(fluid)) return null; + IFluidTankLong tank = fluidToTankMap.get(fluid); + if (tank != null) { + return tank.drain(amount, !simulate); + } + int tankNumber = 0; + tank = inventory.getFluidTank(tankNumber++); + while (tank.getStoredFluid() != fluid) { + tank = inventory.getFluidTank(tankNumber++); + } + fluidToTankMap.put(fluid, tank); + return tank.drain(amount, !simulate); + } + + public void update() { + for (int i = 0; i < inventory.getTanks(); i++) { + IFluidTankLong tank = inventory.getFluidTank(i); + if (tank.getFluidAmountLong() > 0) continue; + tank.setFluid(null, 0); + } + } + + public long calculateAmountOfTimesFluidCanBeTaken(Fluid fluid, long amountToTake) { + if (!isFluidValid(fluid)) return 0; + IFluidTankLong tank = fluidToTankMap.get(fluid); + if (tank == null) return 0; + return tank.getFluidAmountLong() / amountToTake; + } + + @Nonnull + public Map<Fluid, Long> getMapOfStoredFluids() { + Map<Fluid, Long> map = new HashMap<>(); + for (int i = 0; i < inventory.getTanks(); i++) { + IFluidTankLong tank = inventory.getFluidTank(i); + if (tank == null) continue; + Fluid fluid = tank.getStoredFluid(); + if (fluid == null) continue; + map.put(fluid, map.getOrDefault(fluid, 0L) + tank.getFluidAmountLong()); + } + return map; + } + + /** + * Return a scrollable widget with only the inventory. + */ + @Nonnull + public Widget getGuiPart() { + return getGUIPart(DEFAULT_COLUMNS_PER_ROW); + } + + /** + * Return a scrollable widget with only the inventory. + */ + @Nonnull + public Widget getGUIPart(int columnsPerRow) { + final Scrollable scrollable = new Scrollable(); + scrollable.setVerticalScroll(); + for (int rows = 0; rows * 4 < inventory.getTanks(); rows++) { + final int columnsToMake = Math.min(inventory.getTanks() - rows * 4, 4); + for (int column = 0; column < columnsToMake; column++) { + final FluidSlotWidget fluidSlot = new FluidSlotWidget(inventory, rows * 4 + column); + scrollable.widget( + fluidSlot.setPos(column * POSITION_INTERVAL, rows * POSITION_INTERVAL) + .setSize(SIZE)); + } + } + return scrollable; + } +} diff --git a/src/main/java/gregtech/api/logic/ItemInventoryLogic.java b/src/main/java/gregtech/api/logic/ItemInventoryLogic.java new file mode 100644 index 0000000000..69005216b8 --- /dev/null +++ b/src/main/java/gregtech/api/logic/ItemInventoryLogic.java @@ -0,0 +1,314 @@ +package gregtech.api.logic; + +import java.util.Collection; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraftforge.common.util.Constants; + +import com.gtnewhorizons.modularui.api.forge.IItemHandlerModifiable; +import com.gtnewhorizons.modularui.api.forge.ItemStackHandler; +import com.gtnewhorizons.modularui.api.forge.ListItemHandler; +import com.gtnewhorizons.modularui.api.math.Size; +import com.gtnewhorizons.modularui.api.widget.Widget; +import com.gtnewhorizons.modularui.common.widget.Scrollable; +import com.gtnewhorizons.modularui.common.widget.SlotWidget; + +import gregtech.api.util.GT_Utility; +import gregtech.api.util.item.ItemHolder; + +/** + * Generic Item logic for MuTEs. + * + * @author BlueWeabo + */ +public class ItemInventoryLogic { + + private static final int DEFAULT_COLUMNS_PER_ROW = 4; + private static final int POSITION_INTERVAL = 18; + private static final Size SIZE = new Size(18, 18); + + protected String displayName; + @Nonnull + protected final IItemHandlerModifiable inventory; + protected UUID connectedFluidInventory; + protected int tier; + protected boolean isUpgradeInventory; + protected Map<ItemHolder, Long> cachedItemMap; + protected boolean inRecipeCheck; + + public ItemInventoryLogic(int numberOfSlots) { + this(numberOfSlots, 0); + } + + public ItemInventoryLogic(int numberOfSlots, int tier) { + this(new ItemStackHandler(numberOfSlots), tier, false); + } + + public ItemInventoryLogic(int numberOfSlots, int tier, boolean isUpgradeInventory) { + this(new ItemStackHandler(numberOfSlots), tier, isUpgradeInventory); + } + + public ItemInventoryLogic(@Nonnull IItemHandlerModifiable inventory, int tier, boolean isUpgradeInventory) { + this.inventory = inventory; + this.tier = tier; + this.isUpgradeInventory = isUpgradeInventory; + } + + public ItemInventoryLogic(Collection<IItemHandlerModifiable> inventories) { + this(new ListItemHandler(inventories), -1, false); + } + + @Nullable + public String getDisplayName() { + return displayName; + } + + public int getTier() { + return tier; + } + + public boolean isUpgradeInventory() { + return isUpgradeInventory; + } + + public int getSlots() { + return getInventory().getSlots(); + } + + public void setDisplayName(@Nullable String displayName) { + this.displayName = displayName; + } + + @Nullable + public UUID getConnectedFluidInventoryID() { + return connectedFluidInventory; + } + + public void setConnectedFluidInventoryID(@Nullable UUID connectedFluidTank) { + this.connectedFluidInventory = connectedFluidTank; + } + + /** + * + * @return The Item Inventory Logic as an NBTTagCompound to be saved in another nbt as how one wants. + */ + @Nonnull + public NBTTagCompound saveToNBT() { + final NBTTagCompound nbt = new NBTTagCompound(); + final NBTTagList tList = new NBTTagList(); + for (int slot = 0; slot < inventory.getSlots(); slot++) { + final ItemStack tStack = inventory.getStackInSlot(slot); + if (tStack == null) continue; + + final NBTTagCompound tag = new NBTTagCompound(); + tag.setByte("s", (byte) slot); + tStack.writeToNBT(tag); + tList.appendTag(tag); + } + nbt.setTag("inventory", tList); + nbt.setInteger("tier", tier); + if (displayName != null) { + nbt.setString("displayName", displayName); + } + nbt.setBoolean("isUpgradeInventory", isUpgradeInventory); + if (connectedFluidInventory != null) { + nbt.setString("connectedFluidInventory", connectedFluidInventory.toString()); + } + return nbt; + } + + /** + * Loads the Item Inventory Logic from an NBTTagCompound. + */ + public void loadFromNBT(@Nonnull NBTTagCompound nbt) { + tier = nbt.getInteger("tier"); + if (nbt.hasKey("displayName")) { + displayName = nbt.getString("displayName"); + } + + isUpgradeInventory = nbt.getBoolean("isUpgradeInventory"); + if (nbt.hasKey("connectedFluidInventory")) { + connectedFluidInventory = UUID.fromString(nbt.getString("connectedFluidInventory")); + } + + NBTTagList nbtList = nbt.getTagList("inventory", Constants.NBT.TAG_COMPOUND); + if (nbtList == null) return; + + for (int i = 0; i < nbtList.tagCount(); i++) { + final NBTTagCompound tNBT = nbtList.getCompoundTagAt(i); + final int tSlot = tNBT.getShort("s"); + if (tSlot >= 0 && tSlot < inventory.getSlots()) { + inventory.setStackInSlot(tSlot, GT_Utility.loadItem(tNBT)); + } + } + } + + @Nonnull + public IItemHandlerModifiable getInventory() { + return inventory; + } + + @Nonnull + public ItemStack[] getStoredItems() { + final ItemStack[] items = inventory.getStacks() + .stream() + .filter(item -> item != null) + .collect(Collectors.toList()) + .toArray(new ItemStack[0]); + if (items == null) { + return new ItemStack[0]; + } + return items; + } + + public boolean isStackValid(ItemStack item) { + return true; + } + + @Nullable + public ItemStack insertItem(ItemStack item) { + if (!isStackValid(item)) return item; + for (int i = 0; i < inventory.getSlots() && item != null && item.stackSize > 0; i++) { + item = inventory.insertItem(i, item, false); + } + return item; + } + + @Nullable + public ItemStack extractItem(int slot, int amount) { + return inventory.extractItem(slot, amount, false); + } + + public boolean subtractItemAmount(@Nonnull ItemHolder item, long amount, boolean simulate) { + Map<ItemHolder, Long> itemMap = getMapOfStoredItems(); + if (!itemMap.containsKey(item)) { + return false; + } + + if (itemMap.get(item) < amount) { + return false; + } + + if (simulate) { + return true; + } + + itemMap.put(item, itemMap.get(item) - amount); + return true; + } + + @Nullable + public ItemStack getItemInSlot(int slot) { + return inventory.getStackInSlot(slot); + } + + public void sort() { + Map<ItemHolder, Long> itemMap = getMapOfStoredItems(); + List<ItemHolder> sortedItems = itemMap.keySet() + .stream() + .sorted( + Comparator.comparing( + a -> a.getItem() + .getUnlocalizedName() + a.getMeta())) + .collect(Collectors.toList()); + putInItemsFromMap(itemMap, sortedItems); + } + + public void update(boolean shouldSort) { + if (shouldSort) { + sort(); + } + + for (int i = 0; i < inventory.getSlots(); i++) { + ItemStack item = inventory.getStackInSlot(i); + if (item == null) continue; + if (item.stackSize > 0) continue; + inventory.setStackInSlot(i, null); + } + } + + /** + * Return a scrollable widget with only the inventory. + */ + @Nonnull + public Widget getGuiPart() { + return getGUIPart(DEFAULT_COLUMNS_PER_ROW); + } + + /** + * Return a scrollable widget with only the inventory. + */ + @Nonnull + public Widget getGUIPart(int columnsPerRow) { + final Scrollable scrollable = new Scrollable(); + scrollable.setVerticalScroll(); + for (int rows = 0; rows * columnsPerRow < Math.min(inventory.getSlots(), 128); rows++) { + final int columnsToMake = Math + .min(Math.min(inventory.getSlots(), 128) - rows * columnsPerRow, columnsPerRow); + for (int column = 0; column < columnsToMake; column++) { + scrollable.widget( + new SlotWidget(inventory, rows * columnsPerRow + column) + .setPos(column * POSITION_INTERVAL, rows * POSITION_INTERVAL) + .setSize(SIZE)); + } + } + return scrollable; + } + + public void startRecipeCheck() { + cachedItemMap = getMapOfStoredItems(); + inRecipeCheck = true; + } + + public void stopRecipeCheck() { + inRecipeCheck = false; + putInItemsFromMap(cachedItemMap, null); + cachedItemMap = null; + } + + @Nonnull + public Map<ItemHolder, Long> getMapOfStoredItems() { + if (inRecipeCheck) return cachedItemMap; + Map<ItemHolder, Long> items = new HashMap<>(); + for (int i = 0; i < inventory.getSlots(); i++) { + ItemStack item = extractItem(i, Integer.MAX_VALUE); + if (item == null) continue; + ItemHolder itemHolder = new ItemHolder(item); + items.put(itemHolder, items.getOrDefault(itemHolder, 0L) + item.stackSize); + } + return items; + } + + protected void putInItemsFromMap(@Nonnull Map<ItemHolder, Long> itemMap, @Nullable List<ItemHolder> sortedList) { + for (ItemHolder itemHolder : (sortedList == null ? itemMap.keySet() : sortedList)) { + long itemAmount = itemMap.get(itemHolder); + ItemStack item = new ItemStack(itemHolder.getItem(), 0, itemHolder.getMeta()); + item.setTagCompound(itemHolder.getNBT()); + while (itemAmount > 0) { + item.stackSize = (int) Math.min(item.getMaxStackSize(), itemAmount); + itemAmount -= item.stackSize; + insertItem(item); + } + } + } + + public long calculateAmountOfTimesItemCanBeTaken(ItemHolder item, long amount) { + return getMapOfStoredItems().getOrDefault(item, 0L) / amount; + } + + public Set<ItemHolder> getSetOfStoredItems() { + return getMapOfStoredItems().keySet(); + } +} diff --git a/src/main/java/gregtech/api/logic/ModelRenderLogic.java b/src/main/java/gregtech/api/logic/ModelRenderLogic.java new file mode 100644 index 0000000000..d9f2fdcf27 --- /dev/null +++ b/src/main/java/gregtech/api/logic/ModelRenderLogic.java @@ -0,0 +1,5 @@ +package gregtech.api.logic; + +public abstract class ModelRenderLogic { + +} diff --git a/src/main/java/gregtech/api/logic/MuTEProcessingLogic.java b/src/main/java/gregtech/api/logic/MuTEProcessingLogic.java new file mode 100644 index 0000000000..da53c8875d --- /dev/null +++ b/src/main/java/gregtech/api/logic/MuTEProcessingLogic.java @@ -0,0 +1,256 @@ +package gregtech.api.logic; + +import static net.minecraftforge.common.util.Constants.NBT.TAG_COMPOUND; + +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.UUID; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraftforge.fluids.FluidStack; + +import com.gtnewhorizons.modularui.api.screen.ModularWindow; +import com.gtnewhorizons.modularui.api.widget.Widget; +import com.gtnewhorizons.modularui.common.widget.Scrollable; + +import gregtech.api.enums.InventoryType; +import gregtech.api.logic.interfaces.ProcessingLogicHost; +import gregtech.api.recipe.RecipeMap; +import gregtech.api.recipe.check.CheckRecipeResult; +import gregtech.api.recipe.check.CheckRecipeResultRegistry; +import gregtech.api.util.GT_OverclockCalculator; +import gregtech.api.util.GT_ParallelHelper; +import gregtech.api.util.GT_Recipe; +import gregtech.api.util.GT_Utility; + +/** + * Processing logic class, dedicated for MultiTileEntities. + */ +public class MuTEProcessingLogic<P extends MuTEProcessingLogic<P>> extends AbstractProcessingLogic<P> { + + protected boolean hasWork; + protected int progress; + protected ProcessingLogicHost<P> machineHost; + @Nonnull + protected CheckRecipeResult recipeResult = CheckRecipeResultRegistry.NONE; + @Nullable + protected UUID itemOutputID; + @Nullable + protected UUID fluidOutputID; + + public P setMachineHost(@Nonnull ProcessingLogicHost<P> machineHost) { + this.machineHost = machineHost; + return getThis(); + } + + // #region Logic + + @Nonnull + @Override + public CheckRecipeResult process() { + RecipeMap<?> recipeMap = preProcess(); + + ItemInventoryLogic itemInput = null; + FluidInventoryLogic fluidInput = null; + if (machineHost.isInputSeparated()) { + for (Map.Entry<UUID, ItemInventoryLogic> itemEntry : machineHost + .getAllItemInventoryLogics(InventoryType.Input)) { + itemOutputID = Objects.requireNonNull(itemEntry.getKey()); + itemInput = Objects.requireNonNull(itemEntry.getValue()); + fluidInput = Objects.requireNonNull( + machineHost.getFluidLogic(InventoryType.Input, itemInput.getConnectedFluidInventoryID())); + fluidOutputID = itemInput.getConnectedFluidInventoryID(); + } + } else { + itemInput = Objects.requireNonNull(machineHost.getItemLogic(InventoryType.Input, null)); + fluidInput = Objects.requireNonNull(machineHost.getFluidLogic(InventoryType.Input, null)); + } + + CheckRecipeResult recipeValidatorResult = null; + if (recipeValidatorResult != null) { + return recipeValidatorResult; + } + + return processRecipe(null, Objects.requireNonNull(itemInput), Objects.requireNonNull(fluidInput)); + } + + @Nonnull + protected CheckRecipeResult processRecipe(@Nonnull List<GT_Recipe> recipes, @Nonnull ItemInventoryLogic itemInput, + @Nonnull FluidInventoryLogic fluidInput) { + CheckRecipeResult result = CheckRecipeResultRegistry.INTERNAL_ERROR; + for (GT_Recipe recipe : recipes) { + Objects.requireNonNull(recipe); + GT_ParallelHelper helper = createParallelHelper(recipe, itemInput, fluidInput); + GT_OverclockCalculator calculator = createOverclockCalculator(recipe); + helper.setCalculator(calculator); + helper.build(); + result = helper.getResult(); + if (result.wasSuccessful()) { + return applyRecipe(recipe, helper, calculator, result); + } + } + return result; + } + + /** + * Override if you don't work with regular gt recipe maps + */ + @Nonnull + protected Object findRecipe(@Nullable RecipeMap<?> map, @Nonnull ItemInventoryLogic itemInput, + @Nonnull FluidInventoryLogic fluidInput) { + if (map == null) { + return false; + } + + return true; + } + + @Nonnull + protected GT_ParallelHelper createParallelHelper(@Nonnull GT_Recipe recipe, @Nonnull ItemInventoryLogic itemInput, + @Nonnull FluidInventoryLogic fluidInput) { + return new GT_ParallelHelper().setRecipe(recipe) + .setItemInputInventory(itemInput) + .setFluidInputInventory(fluidInput) + .setAvailableEUt(availableVoltage * availableAmperage) + .setMaxParallel(maxParallel) + .setEUtModifier(euModifier) + .enableBatchMode(batchSize) + .setConsumption(true) + .setOutputCalculation(true) + .setMuTEMode(true); + } + + // #endregion + + // #region Getters + + @Nonnull + public CheckRecipeResult getResult() { + return recipeResult; + } + + public int getProgress() { + return progress; + } + + // #endregion + + // #region Other + + public void startCheck() { + recipeResult = process(); + } + + public void progress() { + if (!hasWork) return; + if (progress == duration) { + progress = 0; + duration = 0; + calculatedEut = 0; + output(); + return; + } + progress++; + } + + protected void output() { + ItemInventoryLogic itemOutput = machineHost.getItemLogic(InventoryType.Output, itemOutputID); + FluidInventoryLogic fluidOutput = machineHost.getFluidLogic(InventoryType.Output, fluidOutputID); + if (itemOutput == null || fluidOutput == null) return; + for (ItemStack item : outputItems) { + if (item == null) continue; + itemOutput.insertItem(item); + } + for (FluidStack fluid : outputFluids) { + if (fluid == null) continue; + fluidOutput.fill(fluid.getFluid(), fluid.amount, false); + } + outputItems = new ItemStack[0]; + outputFluids = new FluidStack[0]; + } + + public boolean canWork() { + return !hasWork && machineHost.isAllowedToWork(); + } + + /** + * By how much to increase the progress? + * + * @param progressAmount in ticks + */ + public void increaseProgress(int progressAmount) { + progress += progressAmount; + } + + public NBTTagCompound saveToNBT() { + NBTTagCompound logicNBT = new NBTTagCompound(); + logicNBT.setLong("eutConsumption", calculatedEut); + logicNBT.setInteger("duration", duration); + logicNBT.setInteger("progress", progress); + logicNBT.setBoolean("hasWork", hasWork); + if (outputItems != null) { + NBTTagList itemOutputsNBT = new NBTTagList(); + for (ItemStack item : outputItems) { + itemOutputsNBT.appendTag(GT_Utility.saveItem(item)); + } + logicNBT.setTag("itemOutputs", itemOutputsNBT); + } + if (outputFluids != null) { + NBTTagList fluidOutputsNBT = new NBTTagList(); + for (FluidStack fluid : outputFluids) { + fluidOutputsNBT.appendTag(fluid.writeToNBT(new NBTTagCompound())); + } + logicNBT.setTag("fluidOutputs", fluidOutputsNBT); + } + if (itemOutputID != null) { + logicNBT.setString("itemOutputID", itemOutputID.toString()); + } + if (fluidOutputID != null) { + logicNBT.setString("fluidOutputID", fluidOutputID.toString()); + } + return logicNBT; + } + + public void loadFromNBT(@Nonnull NBTTagCompound logicNBT) { + calculatedEut = logicNBT.getLong("eutConsumption"); + duration = logicNBT.getInteger("duration"); + progress = logicNBT.getInteger("progress"); + hasWork = logicNBT.getBoolean("hasWork"); + if (logicNBT.hasKey("itemOutputs")) { + NBTTagList itemOutputsNBT = logicNBT.getTagList("itemOutputs", TAG_COMPOUND); + outputItems = new ItemStack[itemOutputsNBT.tagCount()]; + for (int i = 0; i < itemOutputsNBT.tagCount(); i++) { + outputItems[i] = GT_Utility.loadItem(itemOutputsNBT.getCompoundTagAt(i)); + } + } + if (logicNBT.hasKey("fluidOutputs")) { + NBTTagList fluidOutputsNBT = logicNBT.getTagList("fluidOutputs", TAG_COMPOUND); + outputFluids = new FluidStack[fluidOutputsNBT.tagCount()]; + for (int i = 0; i < fluidOutputsNBT.tagCount(); i++) { + outputFluids[i] = FluidStack.loadFluidStackFromNBT(fluidOutputsNBT.getCompoundTagAt(i)); + } + } + if (logicNBT.hasKey("itemOutputID")) { + itemOutputID = UUID.fromString(logicNBT.getString("itemOutputID")); + } + if (logicNBT.hasKey("fluidOutputID")) { + fluidOutputID = UUID.fromString(logicNBT.getString("fluidOutputID")); + } + } + + /** + * Returns a gui part, which will be displayed in a separate tab on the machine's gui. + */ + @Nonnull + public Widget getGUIPart(ModularWindow.Builder builder) { + return new Scrollable(); + } + + // #endregion +} diff --git a/src/main/java/gregtech/api/logic/NullPowerLogic.java b/src/main/java/gregtech/api/logic/NullPowerLogic.java new file mode 100644 index 0000000000..0017f0e647 --- /dev/null +++ b/src/main/java/gregtech/api/logic/NullPowerLogic.java @@ -0,0 +1,5 @@ +package gregtech.api.logic; + +public class NullPowerLogic extends PowerLogic { + +} diff --git a/src/main/java/gregtech/api/logic/PowerLogic.java b/src/main/java/gregtech/api/logic/PowerLogic.java new file mode 100644 index 0000000000..ad19987a76 --- /dev/null +++ b/src/main/java/gregtech/api/logic/PowerLogic.java @@ -0,0 +1,254 @@ +package gregtech.api.logic; + +import static gregtech.common.misc.WirelessNetworkManager.addEUToGlobalEnergyMap; + +import java.util.UUID; + +import javax.annotation.Nonnull; + +import net.minecraft.nbt.NBTTagCompound; + +import gregtech.api.enums.GT_Values.NBT; + +/** + * Power logic for machines. This is used to store all the important variables for a machine to have energy and use it + * in any way. + * + * @author BlueWeabo, Maxim + */ +public class PowerLogic { + + public static final int NONE = 0; + public static final int RECEIVER = 1; + public static final int EMITTER = 2; + public static final int BOTH = RECEIVER | EMITTER; + private static float wirelessChargeFactor = 0.5F; + private long storedEnergy = 0; + private long energyCapacity = 0; + private long voltage = 0; + private long amperage = 0; + private int type = 0; + private boolean canUseLaser = false; + private boolean canUseWireless = false; + private UUID owner; + + public PowerLogic() {} + + /** + * Sets the max voltage the logic can accept + */ + @Nonnull + public PowerLogic setMaxVoltage(long voltage) { + this.voltage = voltage; + return this; + } + + /** + * Sets the maximum amount of energy the machine can store inside of it + */ + @Nonnull + public PowerLogic setEnergyCapacity(long energyCapacity) { + this.energyCapacity = energyCapacity; + return this; + } + + /** + * Sets the maximum amount of amps a machine can receive from an emitter + */ + @Nonnull + public PowerLogic setMaxAmperage(long amperage) { + this.amperage = amperage; + return this; + } + + /** + * Sets the type of power logic this is. Whether it will receive EU or emit it to others, or do both + */ + @Nonnull + public PowerLogic setType(int type) { + this.type = type; + return this; + } + + /** + * If this power logic can use lasers to be used for it + */ + @Nonnull + public PowerLogic setCanUseLaser(boolean canUse) { + canUseLaser = canUse; + return this; + } + + /** + * If the power logic should use wireless EU first before using its internal buffer + */ + @Nonnull + public PowerLogic setCanUseWireless(boolean canUse, UUID owner) { + canUseWireless = canUse; + this.owner = owner; + return this; + } + + /** + * Adding energy directly to the buffer, but only if it has the capacity. + */ + public boolean addEnergyUnsafe(long totalEUAdded) { + if (storedEnergy + totalEUAdded >= energyCapacity) { + return false; + } + + storedEnergy += totalEUAdded; + return true; + } + + /** + * Adding energy to the buffer if the voltage given isn't higher than the voltage of the logic + */ + public boolean addEnergy(long voltage, long amperage) { + if (voltage > this.voltage) { + return false; + } + + return addEnergyUnsafe(voltage * amperage); + } + + /** + * Same as {@link #addEnergy(long, long)}, but only 1 amp of it + */ + public boolean addEnergy(long voltage) { + return addEnergy(voltage, 1); + } + + /** + * Injecting energy in the multiblock ampere per ampere until full or until we have added the maximum possible + * amperes for this tick + * + * @param voltage At what voltage are the amps? + * @param availableAmperage How much amperage do we have available + * @return Amount of amperes used + */ + public long injectEnergy(long voltage, long availableAmperage) { + if (canUseWireless) return 0; + long usedAmperes = 0; + while (addEnergy(voltage, 1) && usedAmperes < amperage) { + usedAmperes++; + } + + return usedAmperes; + } + + /** + * Remove energy from the logic only if it has enough to be removed. + */ + public boolean removeEnergyUnsafe(long totalEURemoved) { + if (canUseWireless) { + if (storedEnergy < energyCapacity * wirelessChargeFactor) { + if (addEUToGlobalEnergyMap(owner, -(energyCapacity - storedEnergy))) { + storedEnergy = energyCapacity; + } + } + } + if (storedEnergy - totalEURemoved < 0) { + return false; + } + + storedEnergy -= totalEURemoved; + return true; + } + + /** + * Remove the given voltage for the amount of amperage if the removed isn't higher than the logic's voltage + */ + public boolean removeEnergy(long voltage, long amperage) { + if (voltage > this.voltage) { + return false; + } + + return removeEnergyUnsafe(voltage * amperage); + } + + /** + * Same as {@link #removeEnergy(long, long)}, but with only 1 amperage + */ + public boolean removeEnergy(long voltage) { + return removeEnergy(voltage, 1); + } + + /** + * @return The maximum energy that can be stored. + */ + public long getCapacity() { + return energyCapacity; + } + + /** + * @return The maximum voltage that is available + */ + public long getVoltage() { + return voltage; + } + + /** + * @return The current energy stored + */ + public long getStoredEnergy() { + return storedEnergy; + } + + /** + * @return The current maximum Amperage + */ + public long getMaxAmperage() { + return amperage; + } + + /** + * Is the logic a receiver to receive energy + */ + public boolean isEnergyReceiver() { + return (type & RECEIVER) > 0; + } + + /** + * Is the logic a emitter to emit energy + */ + public boolean isEnergyEmitter() { + return (type & EMITTER) > 0; + } + + /** + * Saves the power logic to its own nbt tag before saving it to the given one. + * + * @param nbt Tag where you want to save the power logic tag to. + */ + public void saveToNBT(NBTTagCompound nbt) { + NBTTagCompound powerLogic = new NBTTagCompound(); + powerLogic.setLong(NBT.POWER_LOGIC_ENERGY_CAPACITY, energyCapacity); + powerLogic.setLong(NBT.POWER_LOGIC_STORED_ENERGY, storedEnergy); + powerLogic.setLong(NBT.POWER_LOGIC_AMPERAGE, amperage); + powerLogic.setLong(NBT.POWER_LOGIC_VOLTAGE, voltage); + powerLogic.setInteger(NBT.POWER_LOGIC_TYPE, type); + nbt.setTag(NBT.POWER_LOGIC, powerLogic); + } + + /** + * Loads the power logic from its own nbt after getting it from the given one + * + * @param nbt Tag where the power logic tag was saved to + */ + public void loadFromNBT(NBTTagCompound nbt) { + NBTTagCompound powerLogic = nbt.getCompoundTag(NBT.POWER_LOGIC); + energyCapacity = powerLogic.getLong(NBT.POWER_LOGIC_ENERGY_CAPACITY); + storedEnergy = powerLogic.getLong(NBT.POWER_LOGIC_STORED_ENERGY); + amperage = powerLogic.getLong(NBT.POWER_LOGIC_AMPERAGE); + voltage = powerLogic.getLong(NBT.POWER_LOGIC_VOLTAGE); + type = powerLogic.getInteger(NBT.POWER_LOGIC_TYPE); + } + + /** + * Can we use lasers for inputting EU + */ + public boolean canUseLaser() { + return canUseLaser; + } +} diff --git a/src/main/java/gregtech/api/logic/ProcessingLogic.java b/src/main/java/gregtech/api/logic/ProcessingLogic.java new file mode 100644 index 0000000000..4d203ed80f --- /dev/null +++ b/src/main/java/gregtech/api/logic/ProcessingLogic.java @@ -0,0 +1,228 @@ +package gregtech.api.logic; + +import java.util.List; +import java.util.stream.Stream; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import net.minecraft.item.ItemStack; +import net.minecraftforge.fluids.FluidStack; + +import gregtech.api.interfaces.tileentity.IRecipeLockable; +import gregtech.api.recipe.RecipeMap; +import gregtech.api.recipe.check.CheckRecipeResult; +import gregtech.api.recipe.check.CheckRecipeResultRegistry; +import gregtech.api.recipe.check.SingleRecipeCheck; +import gregtech.api.util.GT_OverclockCalculator; +import gregtech.api.util.GT_ParallelHelper; +import gregtech.api.util.GT_Recipe; + +/** + * Logic class to calculate result of recipe check from inputs, based on recipemap. + */ +@SuppressWarnings({ "unused", "UnusedReturnValue" }) +public class ProcessingLogic extends AbstractProcessingLogic<ProcessingLogic> { + + protected IRecipeLockable recipeLockableMachine; + protected ItemStack specialSlotItem; + protected ItemStack[] inputItems; + protected FluidStack[] inputFluids; + protected boolean isRecipeLocked; + + public ProcessingLogic() {} + + // #region Setters + + @Nonnull + public ProcessingLogic setInputItems(ItemStack... itemInputs) { + this.inputItems = itemInputs; + return getThis(); + } + + @Nonnull + public ProcessingLogic setInputItems(List<ItemStack> itemOutputs) { + this.inputItems = itemOutputs.toArray(new ItemStack[0]); + return getThis(); + } + + @Nonnull + public ProcessingLogic setInputFluids(FluidStack... fluidInputs) { + this.inputFluids = fluidInputs; + return getThis(); + } + + @Nonnull + public ProcessingLogic setInputFluids(List<FluidStack> fluidInputs) { + this.inputFluids = fluidInputs.toArray(new FluidStack[0]); + return getThis(); + } + + public ProcessingLogic setSpecialSlotItem(ItemStack specialSlotItem) { + this.specialSlotItem = specialSlotItem; + return getThis(); + } + + /** + * Enables single recipe locking mode. + */ + public ProcessingLogic setRecipeLocking(IRecipeLockable recipeLockableMachine, boolean isRecipeLocked) { + this.recipeLockableMachine = recipeLockableMachine; + this.isRecipeLocked = isRecipeLocked; + return getThis(); + } + + /** + * Clears calculated results and provided machine inputs to prepare for the next machine operation. + */ + + public ProcessingLogic clear() { + this.inputItems = null; + this.inputFluids = null; + this.specialSlotItem = null; + this.outputItems = null; + this.outputFluids = null; + this.calculatedEut = 0; + this.duration = 0; + this.calculatedParallels = 0; + return getThis(); + } + + // #endregion + + // #region Logic + + /** + * Executes the recipe check: Find recipe from recipemap, Calculate parallel, overclock and outputs. + */ + @Nonnull + public CheckRecipeResult process() { + RecipeMap<?> recipeMap = preProcess(); + + if (inputItems == null) { + inputItems = new ItemStack[0]; + } + if (inputFluids == null) { + inputFluids = new FluidStack[0]; + } + + if (isRecipeLocked && recipeLockableMachine != null && recipeLockableMachine.getSingleRecipeCheck() != null) { + // Recipe checker is already built, we'll use it + SingleRecipeCheck singleRecipeCheck = recipeLockableMachine.getSingleRecipeCheck(); + // Validate recipe here, otherwise machine will show "not enough output space" + // even if recipe cannot be found + if (singleRecipeCheck.checkRecipeInputs(false, 1, inputItems, inputFluids) == 0) { + return CheckRecipeResultRegistry.NO_RECIPE; + } + + return validateAndCalculateRecipe( + recipeLockableMachine.getSingleRecipeCheck() + .getRecipe()).checkRecipeResult; + } + Stream<GT_Recipe> matchedRecipes = findRecipeMatches(recipeMap); + Iterable<GT_Recipe> recipeIterable = matchedRecipes::iterator; + CheckRecipeResult checkRecipeResult = CheckRecipeResultRegistry.NO_RECIPE; + for (GT_Recipe matchedRecipe : recipeIterable) { + CalculationResult foundResult = validateAndCalculateRecipe(matchedRecipe); + if (foundResult.successfullyConsumedInputs) { + // Successfully found and set recipe, so return it + return foundResult.checkRecipeResult; + } + if (foundResult.checkRecipeResult != CheckRecipeResultRegistry.NO_RECIPE) { + // Recipe failed in interesting way, so remember that and continue searching + checkRecipeResult = foundResult.checkRecipeResult; + } + } + return checkRecipeResult; + } + + /** + * Checks if supplied recipe is valid for process. This involves voltage check, output full check. If successful, + * additionally performs input consumption, output calculation with parallel, and overclock calculation. + * + * @param recipe The recipe which will be checked and processed + */ + @Nonnull + private CalculationResult validateAndCalculateRecipe(@Nonnull GT_Recipe recipe) { + CheckRecipeResult result = validateRecipe(recipe); + if (!result.wasSuccessful()) { + return CalculationResult.ofFailure(result); + } + + GT_ParallelHelper helper = createParallelHelper(recipe); + GT_OverclockCalculator calculator = createOverclockCalculator(recipe); + helper.setCalculator(calculator); + helper.build(); + + if (!helper.getResult() + .wasSuccessful()) { + return CalculationResult.ofFailure(helper.getResult()); + } + + return CalculationResult.ofSuccess(applyRecipe(recipe, helper, calculator, result)); + } + + /** + * Finds a list of matched recipes. At this point no additional check to the matched recipe has been done. + * <p> + * Override {@link #validateRecipe} to have custom check. + * <p> + * Override this method if it doesn't work with normal recipemaps. + */ + @Nonnull + protected Stream<GT_Recipe> findRecipeMatches(@Nullable RecipeMap<?> map) { + if (map == null) { + return Stream.empty(); + } + return map.findRecipeQuery() + .items(inputItems) + .fluids(inputFluids) + .specialSlot(specialSlotItem) + .cachedRecipe(lastRecipe) + .findAll(); + } + + /** + * Override to tweak parallel logic if needed. + */ + @Nonnull + protected GT_ParallelHelper createParallelHelper(@Nonnull GT_Recipe recipe) { + return new GT_ParallelHelper().setRecipe(recipe) + .setItemInputs(inputItems) + .setFluidInputs(inputFluids) + .setAvailableEUt(availableVoltage * availableAmperage) + .setMachine(machine, protectItems, protectFluids) + .setRecipeLocked(recipeLockableMachine, isRecipeLocked) + .setMaxParallel(maxParallel) + .setEUtModifier(euModifier) + .enableBatchMode(batchSize) + .setConsumption(true) + .setOutputCalculation(true); + } + + // #endregion + + /** + * Represents the status of check recipe calculation. {@link #successfullyConsumedInputs} does not necessarily mean + * {@link #checkRecipeResult} being successful, when duration or power is overflowed. Being failure means + * recipe cannot meet requirements and recipe search should be continued if possible. + */ + protected final static class CalculationResult { + + public final boolean successfullyConsumedInputs; + public final CheckRecipeResult checkRecipeResult; + + public static CalculationResult ofSuccess(CheckRecipeResult checkRecipeResult) { + return new CalculationResult(true, checkRecipeResult); + } + + public static CalculationResult ofFailure(CheckRecipeResult checkRecipeResult) { + return new CalculationResult(false, checkRecipeResult); + } + + private CalculationResult(boolean successfullyConsumedInputs, CheckRecipeResult checkRecipeResult) { + this.successfullyConsumedInputs = successfullyConsumedInputs; + this.checkRecipeResult = checkRecipeResult; + } + } +} diff --git a/src/main/java/gregtech/api/logic/interfaces/FluidInventoryLogicHost.java b/src/main/java/gregtech/api/logic/interfaces/FluidInventoryLogicHost.java new file mode 100644 index 0000000000..c12333a4c6 --- /dev/null +++ b/src/main/java/gregtech/api/logic/interfaces/FluidInventoryLogicHost.java @@ -0,0 +1,95 @@ +package gregtech.api.logic.interfaces; + +import static com.google.common.primitives.Ints.saturatedCast; + +import java.util.HashSet; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import net.minecraftforge.common.util.ForgeDirection; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.FluidTankInfo; +import net.minecraftforge.fluids.IFluidHandler; + +import gregtech.api.enums.InventoryType; +import gregtech.api.logic.FluidInventoryLogic; + +public interface FluidInventoryLogicHost extends IFluidHandler { + + /** + * To be used for single blocks or when directly interacting with the controller + * + * @param side The side from where fluids are being inputted or extracted from + * @param type The type of inventory being accessed. For inputting its Input, For outputting its Output. + * @return The Fluid Logic responsible for said type. Can return null if the side is invalid + */ + @Nullable + FluidInventoryLogic getFluidLogic(@Nonnull ForgeDirection side, @Nonnull InventoryType type); + + /** + * Only to be used by MultiBlockPart for accessing the Controller Inventory + * + * @param type Type of inventory, is it Input or Output + * @param id ID of the locked inventory. A null id is all inventories of said controller of said type + * @return The Fluid Logic responsible for everything that should be done with said inventory + */ + @Nonnull + default FluidInventoryLogic getFluidLogic(@Nonnull InventoryType type, @Nullable UUID id) { + return Objects.requireNonNull(getFluidLogic(ForgeDirection.UNKNOWN, type)); + } + + /** + * Returns an empty set if the type is {@link InventoryType#Both} or when the machine isn't a controller. + */ + @Nonnull + default Set<Entry<UUID, FluidInventoryLogic>> getAllFluidInventoryLogics(@Nonnull InventoryType type) { + return new HashSet<>(); + } + + @Override + default boolean canDrain(@Nonnull ForgeDirection from, Fluid fluid) { + FluidInventoryLogic logic = getFluidLogic(from, InventoryType.Output); + return logic != null; + } + + @Override + default boolean canFill(@Nonnull ForgeDirection from, Fluid fluid) { + FluidInventoryLogic logic = getFluidLogic(from, InventoryType.Input); + return logic != null; + } + + @Override + @Nullable + default FluidStack drain(@Nonnull ForgeDirection from, @Nonnull FluidStack resource, boolean doDrain) { + FluidInventoryLogic logic = getFluidLogic(from, InventoryType.Output); + if (logic == null) return null; + return logic.drain(resource.getFluid(), resource.amount, !doDrain); + } + + @Override + @Nullable + default FluidStack drain(@Nonnull ForgeDirection from, int maxDrain, boolean doDrain) { + FluidInventoryLogic logic = getFluidLogic(from, InventoryType.Output); + if (logic == null) return null; + return logic.drain(maxDrain, !doDrain); + } + + @Override + default int fill(@Nonnull ForgeDirection from, @Nonnull FluidStack resource, boolean doFill) { + FluidInventoryLogic logic = getFluidLogic(from, InventoryType.Input); + if (logic == null) return 0; + return saturatedCast(logic.fill(resource.getFluid(), resource.amount, !doFill)); + } + + @Override + @Nullable + default FluidTankInfo[] getTankInfo(@Nonnull ForgeDirection from) { + return null; + } +} diff --git a/src/main/java/gregtech/api/logic/interfaces/ItemInventoryLogicHost.java b/src/main/java/gregtech/api/logic/interfaces/ItemInventoryLogicHost.java new file mode 100644 index 0000000000..a65f3c50f1 --- /dev/null +++ b/src/main/java/gregtech/api/logic/interfaces/ItemInventoryLogicHost.java @@ -0,0 +1,172 @@ +package gregtech.api.logic.interfaces; + +import java.util.HashSet; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.ISidedInventory; +import net.minecraft.item.ItemStack; +import net.minecraftforge.common.util.ForgeDirection; + +import gregtech.api.enums.InventoryType; +import gregtech.api.logic.ItemInventoryLogic; + +public interface ItemInventoryLogicHost extends ISidedInventory { + + /** + * To be used for single blocks or when directly interacting with the controller + * + * @param side The side from where items are being inputted or extracted from + * @param type The type of inventory being accessed. For inputting its Input, For outputting its Output. + * @return The Item Logic responsible for said type. Will return null if the side is not valid + */ + @Nullable + ItemInventoryLogic getItemLogic(@Nonnull ForgeDirection side, @Nonnull InventoryType type); + + /** + * Only to be used by MultiBlockPart for accessing the Controller Inventory + * + * @param type Type of inventory, is it Input or Output + * @param id ID of the locked inventory. A null id is all inventories of said controller of said type + * @return The Item Logic responsible for everything that should be done with said inventory + */ + @Nonnull + default ItemInventoryLogic getItemLogic(@Nonnull InventoryType type, @Nullable UUID id) { + return Objects.requireNonNull(getItemLogic(ForgeDirection.UNKNOWN, type)); + } + + /** + * Only to be used for MultiBlockPart + * + * @return + */ + @Nullable + default InventoryType getItemInventoryType() { + return null; + } + + /** + * Returns an empty set if the type is {@link InventoryType#Both} or this is used when the machine isn't a + * controller + */ + @Nonnull + default Set<Entry<UUID, ItemInventoryLogic>> getAllItemInventoryLogics(@Nonnull InventoryType type) { + return new HashSet<>(); + } + + @Override + @Nullable + default ItemStack decrStackSize(int slot, int count) { + InventoryType type = getItemInventoryType(); + if (type == InventoryType.Both) return null; + ItemInventoryLogic logic = getItemLogic(ForgeDirection.UNKNOWN, type == null ? InventoryType.Output : type); + if (logic == null) return null; + return logic.extractItem(slot, count); + } + + @Override + default int getSizeInventory() { + InventoryType type = getItemInventoryType(); + if (type == InventoryType.Both) return 0; + ItemInventoryLogic logic = getItemLogic(ForgeDirection.UNKNOWN, type == null ? InventoryType.Output : type); + if (logic == null) return 0; + return logic.getSlots(); + } + + @Override + @Nullable + default ItemStack getStackInSlot(int slot) { + InventoryType type = getItemInventoryType(); + if (type == InventoryType.Both) return null; + ItemInventoryLogic logic = getItemLogic(ForgeDirection.UNKNOWN, type == null ? InventoryType.Output : type); + if (logic == null) return null; + return logic.getInventory() + .getStackInSlot(slot); + } + + @Override + default boolean isItemValidForSlot(int slot, @Nullable ItemStack stack) { + InventoryType type = getItemInventoryType(); + if (type == InventoryType.Both) return false; + ItemInventoryLogic logic = getItemLogic(ForgeDirection.UNKNOWN, type == null ? InventoryType.Output : type); + if (logic == null) return false; + return logic.getInventory() + .isItemValid(slot, stack); + } + + @Override + default void setInventorySlotContents(int slot, @Nullable ItemStack stack) { + InventoryType type = getItemInventoryType(); + if (type == InventoryType.Both) return; + ItemInventoryLogic logic = getItemLogic(ForgeDirection.UNKNOWN, type == null ? InventoryType.Output : type); + if (logic == null) return; + logic.getInventory() + .setStackInSlot(slot, stack); + } + + @Override + default boolean canExtractItem(int ignoredSlot, ItemStack ignoredItem, int side) { + InventoryType type = getItemInventoryType(); + if (type == null) return false; + return getItemLogic(ForgeDirection.getOrientation(side), type) != null; + } + + @Override + default boolean canInsertItem(int ignoredSlot, ItemStack ignoredItem, int side) { + InventoryType type = getItemInventoryType(); + if (type == null) return false; + return getItemInventoryType() != InventoryType.Output + && getItemLogic(ForgeDirection.getOrientation(side), type) != null; + } + + @Override + default int[] getAccessibleSlotsFromSide(int side) { + InventoryType type = getItemInventoryType(); + if (type == null) return new int[0]; + ItemInventoryLogic logic = getItemLogic(ForgeDirection.UNKNOWN, type == null ? InventoryType.Output : type); + if (logic == null) return new int[0]; + int[] indexes = new int[logic.getSlots()]; + for (int i = 0; i < logic.getSlots(); i++) { + indexes[i] = i; + } + return indexes; + } + + @Override + default void closeInventory() {} + + @Override + default String getInventoryName() { + return ""; + } + + @Override + default int getInventoryStackLimit() { + return 64; + } + + @Override + default ItemStack getStackInSlotOnClosing(int index) { + return null; + } + + @Override + default boolean hasCustomInventoryName() { + return false; + } + + @Override + default boolean isUseableByPlayer(@Nonnull EntityPlayer player) { + return false; + } + + @Override + default void openInventory() {} + +} diff --git a/src/main/java/gregtech/api/logic/interfaces/ModelRenderLogicHost.java b/src/main/java/gregtech/api/logic/interfaces/ModelRenderLogicHost.java new file mode 100644 index 0000000000..9a0afaa539 --- /dev/null +++ b/src/main/java/gregtech/api/logic/interfaces/ModelRenderLogicHost.java @@ -0,0 +1,10 @@ +package gregtech.api.logic.interfaces; + +import gregtech.api.logic.ModelRenderLogic; + +public interface ModelRenderLogicHost { + + ModelRenderLogic getRenderLogic(); + + boolean shouldRenderModel(); +} diff --git a/src/main/java/gregtech/api/logic/interfaces/PowerLogicHost.java b/src/main/java/gregtech/api/logic/interfaces/PowerLogicHost.java new file mode 100644 index 0000000000..4903d7fa23 --- /dev/null +++ b/src/main/java/gregtech/api/logic/interfaces/PowerLogicHost.java @@ -0,0 +1,60 @@ +package gregtech.api.logic.interfaces; + +import java.util.Objects; + +import javax.annotation.Nonnull; + +import net.minecraftforge.common.util.ForgeDirection; + +import gregtech.api.interfaces.tileentity.IEnergyConnected; +import gregtech.api.logic.PowerLogic; + +/** + * Power logic class for one to use to enable a machine to use energy + */ +public interface PowerLogicHost { + + /** + * + * @param side Side being access to try and get the power logic from + * @return Can return NullPowerLogic if the side doesn't allow the return of the logic. That power logic is unusable + */ + @Nonnull + PowerLogic getPowerLogic(@Nonnull ForgeDirection side); + + /** + * Gives the power logic ignoring the side. + */ + @Nonnull + default PowerLogic getPowerLogic() { + return Objects.requireNonNull(getPowerLogic(ForgeDirection.UNKNOWN)); + } + + /** + * Shortcut to the method of {@link PowerLogic#isEnergyReceiver()} + */ + default boolean isEnergyReceiver() { + return getPowerLogic().isEnergyReceiver(); + } + + /** + * Shortcut to the method of {@link PowerLogic#isEnergyEmitter()} + */ + default boolean isEnergyEmitter() { + return getPowerLogic().isEnergyEmitter(); + } + + /** + * Method for emitting energy to other blocks and machines. Override when it needs to be changed. + */ + default void emitEnergyFromLogic() { + IEnergyConnected.Util.emitEnergyToNetwork(this, getPowerOutputSide()); + } + + /** + * From where does the machine output energy from? + * When the output side is {@link ForgeDirection#UNKNOWN} then it won't output energy + */ + @Nonnull + ForgeDirection getPowerOutputSide(); +} diff --git a/src/main/java/gregtech/api/logic/interfaces/ProcessingLogicHost.java b/src/main/java/gregtech/api/logic/interfaces/ProcessingLogicHost.java new file mode 100644 index 0000000000..b8291c9843 --- /dev/null +++ b/src/main/java/gregtech/api/logic/interfaces/ProcessingLogicHost.java @@ -0,0 +1,82 @@ +package gregtech.api.logic.interfaces; + +import javax.annotation.Nonnull; + +import gregtech.api.enums.VoidingMode; +import gregtech.api.interfaces.tileentity.IMachineProgress; +import gregtech.api.interfaces.tileentity.IVoidable; +import gregtech.api.logic.MuTEProcessingLogic; + +public interface ProcessingLogicHost<P extends MuTEProcessingLogic<P>> + extends IVoidable, ItemInventoryLogicHost, FluidInventoryLogicHost, IMachineProgress { + + /** + * Get the processing logic for the current machine + */ + @Nonnull + P getProcessingLogic(); + + boolean isInputSeparated(); + + void setInputSeparation(Boolean inputSeparation); + + default boolean supportsInputSeparation() { + return true; + } + + default boolean getDefaultInputSeparationMode() { + return false; + } + + boolean isRecipeLockingEnabled(); + + void setRecipeLocking(Boolean recipeLocked); + + default boolean supportsSingleRecipeLocking() { + return true; + } + + default boolean getDefaultRecipeLockingMode() { + return false; + } + + default boolean supportsBatchMode() { + return true; + } + + void setBatchMode(Boolean batchMode); + + boolean isBatchModeEnabled(); + + default boolean getDefaultBatchMode() { + return false; + } + + /** + * Get what the machine can void or not + */ + @Nonnull + VoidingMode getVoidMode(); + + /** + * Called when the processing logic should be updated by {@link #needsUpdate()} + */ + default void updateProcessingLogic(@Nonnull P processingLogic) {} + + /** + * Called before the recipe check, but after any other updates + */ + default void setProcessingLogicPower(@Nonnull P processingLogic) {} + + /** + * DO NOT CALL YOURSELF!!! + * + * If you want to make the processing logic be updated call {@link #setProcessingUpdate(boolean)} + */ + boolean needsUpdate(); + + /** + * To be called when one needs to updated the processing logic. That can be when parallel changes, ect. + */ + void setProcessingUpdate(boolean update); +} diff --git a/src/main/java/gregtech/api/metatileentity/BaseMetaPipeEntity.java b/src/main/java/gregtech/api/metatileentity/BaseMetaPipeEntity.java new file mode 100644 index 0000000000..1dc0e34d53 --- /dev/null +++ b/src/main/java/gregtech/api/metatileentity/BaseMetaPipeEntity.java @@ -0,0 +1,1416 @@ +package gregtech.api.metatileentity; + +import static gregtech.GT_Mod.GT_FML_LOGGER; +import static gregtech.api.enums.GT_Values.NW; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.UUID; + +import net.minecraft.block.Block; +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.Items; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.AxisAlignedBB; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.world.World; +import net.minecraftforge.common.util.ForgeDirection; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.FluidTankInfo; +import net.minecraftforge.fluids.IFluidHandler; + +import gregtech.api.GregTech_API; +import gregtech.api.enums.GT_Values; +import gregtech.api.enums.SoundResource; +import gregtech.api.enums.Textures; +import gregtech.api.graphs.Lock; +import gregtech.api.graphs.Node; +import gregtech.api.graphs.paths.NodePath; +import gregtech.api.interfaces.ITexture; +import gregtech.api.interfaces.metatileentity.IConnectable; +import gregtech.api.interfaces.metatileentity.IMetaTileEntity; +import gregtech.api.interfaces.tileentity.IDebugableTileEntity; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.interfaces.tileentity.IPipeRenderedTileEntity; +import gregtech.api.net.GT_Packet_TileEntity; +import gregtech.api.objects.GT_ItemStack; +import gregtech.api.util.GT_CoverBehaviorBase; +import gregtech.api.util.GT_Log; +import gregtech.api.util.GT_ModHandler; +import gregtech.api.util.GT_OreDictUnificator; +import gregtech.api.util.GT_Utility; +import gregtech.common.covers.CoverInfo; +import mcp.mobius.waila.api.IWailaConfigHandler; +import mcp.mobius.waila.api.IWailaDataAccessor; + +/** + * NEVER INCLUDE THIS FILE IN YOUR MOD!!! + * <p/> + * This is the main TileEntity for EVERYTHING. + */ +public class BaseMetaPipeEntity extends CommonMetaTileEntity + implements IGregTechTileEntity, IPipeRenderedTileEntity, IDebugableTileEntity { + + public byte mConnections = IConnectable.NO_CONNECTION; + protected MetaPipeEntity mMetaTileEntity; + private final int[] mTimeStatistics = new int[GregTech_API.TICKS_FOR_LAG_AVERAGING]; + private boolean hasTimeStatisticsStarted; + private boolean mWorkUpdate = false, mWorks = true; + private byte mColor = 0, oColor = 0, oStrongRedstone = 0, oRedstoneData = 63, oTextureData = 0, oUpdateData = 0, + mLagWarningCount = 0; + private int oX = 0, oY = 0, oZ = 0, mTimeStatisticsIndex = 0; + protected Node node; + protected NodePath nodePath; + + public Node getNode() { + return node; + } + + public void setNode(Node node) { + this.node = node; + } + + public NodePath getNodePath() { + return nodePath; + } + + public void setNodePath(NodePath nodePath) { + this.nodePath = nodePath; + } + + public void addToLock(TileEntity tileEntity, ForgeDirection side) { + if (node != null) { + final Lock lock = node.locks[side.ordinal()]; + if (lock != null) { + lock.addTileEntity(tileEntity); + } + } else if (nodePath != null) { + nodePath.lock.addTileEntity(tileEntity); + } + } + + public void removeFromLock(TileEntity tileEntity, ForgeDirection side) { + if (node != null) { + final Lock lock = node.locks[side.ordinal()]; + if (lock != null) { + lock.removeTileEntity(tileEntity); + } + } else if (nodePath != null) { + nodePath.lock.removeTileEntity(tileEntity); + } + } + + public void reloadLocks() { + final IMetaTileEntity meta = getMetaTileEntity(); + if (meta instanceof MetaPipeEntity) { + ((MetaPipeEntity) meta).reloadLocks(); + } + } + + public BaseMetaPipeEntity() {} + + @Override + public void writeToNBT(NBTTagCompound aNBT) { + try { + super.writeToNBT(aNBT); + } catch (Throwable e) { + GT_FML_LOGGER.error("Encountered CRITICAL ERROR while saving MetaTileEntity", e); + } + try { + aNBT.setInteger("mID", mID); + writeCoverNBT(aNBT, false); + aNBT.setByte("mConnections", mConnections); + aNBT.setByte("mColor", mColor); + aNBT.setBoolean("mWorks", !mWorks); + } catch (Throwable e) { + GT_FML_LOGGER.error("Encountered CRITICAL ERROR while saving MetaTileEntity", e); + } + saveMetaTileNBT(aNBT); + } + + @Override + public void readFromNBT(NBTTagCompound aNBT) { + super.readFromNBT(aNBT); + setInitialValuesAsNBT(aNBT, (short) 0); + } + + @Override + public void setInitialValuesAsNBT(NBTTagCompound aNBT, short aID) { + if (aNBT == null) { + if (aID > 0) mID = aID; + else mID = mID > 0 ? mID : 0; + if (mID != 0) createNewMetatileEntity(mID); + } else { + if (aID <= 0) mID = (short) aNBT.getInteger("mID"); + else mID = aID; + mConnections = aNBT.getByte("mConnections"); + mColor = aNBT.getByte("mColor"); + mWorks = !aNBT.getBoolean("mWorks"); + + if (mSidedRedstone.length != 6) mSidedRedstone = new byte[] { 0, 0, 0, 0, 0, 0 }; + + readCoverNBT(aNBT); + loadMetaTileNBT(aNBT); + } + } + + @Override + public void updateEntity() { + super.updateEntity(); + + if (!hasValidMetaTileEntity()) { + if (mMetaTileEntity == null) return; + mMetaTileEntity.setBaseMetaTileEntity(this); + } + + long tTime; + if (hasTimeStatisticsStarted) { + tTime = System.nanoTime(); + } else { + tTime = 0; + } + try { + if (hasValidMetaTileEntity()) { + if (mTickTimer++ == 0) { + oX = xCoord; + oY = yCoord; + oZ = zCoord; + if (isServerSide()) checkDropCover(); + else { + requestCoverDataIfNeeded(); + } + worldObj.markTileEntityChunkModified(xCoord, yCoord, zCoord, this); + mMetaTileEntity.onFirstTick(this); + if (!hasValidMetaTileEntity()) return; + } + + if (isClientSide()) { + if (mColor != oColor) { + mMetaTileEntity.onColorChangeClient(oColor = mColor); + issueTextureUpdate(); + } + + if (mNeedsUpdate) { + worldObj.markBlockForUpdate(xCoord, yCoord, zCoord); + mNeedsUpdate = false; + } + } + if (isServerSide() && mTickTimer > 10) { + if (!doCoverThings()) return; + + final byte oldConnections = mConnections; + // Mask-out connection direction bits to keep only Foam related connections + mConnections = (byte) (mMetaTileEntity.mConnections | (mConnections & ~IConnectable.CONNECTED_ALL)); + // If foam not hardened, tries roll chance to harden + if ((mConnections & IConnectable.HAS_FOAM) == IConnectable.HAS_FRESHFOAM + && getRandomNumber(1000) == 0) { + mConnections = (byte) ((mConnections & ~IConnectable.HAS_FRESHFOAM) + | IConnectable.HAS_HARDENEDFOAM); + } + if (mTickTimer > 12 && oldConnections != mConnections) + GregTech_API.causeCableUpdate(worldObj, xCoord, yCoord, zCoord); + } + mMetaTileEntity.onPreTick(this, mTickTimer); + if (!hasValidMetaTileEntity()) return; + if (isServerSide()) { + if (mTickTimer == 10) { + updateCoverBehavior(); + issueBlockUpdate(); + joinEnet(); + } + + if (xCoord != oX || yCoord != oY || zCoord != oZ) { + oX = xCoord; + oY = yCoord; + oZ = zCoord; + issueClientUpdate(); + clearTileEntityBuffer(); + } + } + + mMetaTileEntity.onPostTick(this, mTickTimer); + if (!hasValidMetaTileEntity()) return; + + if (isServerSide()) { + if (mTickTimer % 10 == 0) { + sendClientData(); + } + + if (mTickTimer > 10) { + if (mConnections != oTextureData) sendBlockEvent((byte) 0, oTextureData = mConnections); + byte tData = mMetaTileEntity.getUpdateData(); + if (tData != oUpdateData) sendBlockEvent((byte) 1, oUpdateData = tData); + if (mColor != oColor) sendBlockEvent((byte) 2, oColor = mColor); + tData = (byte) (((mSidedRedstone[0] > 0) ? 1 : 0) | ((mSidedRedstone[1] > 0) ? 2 : 0) + | ((mSidedRedstone[2] > 0) ? 4 : 0) + | ((mSidedRedstone[3] > 0) ? 8 : 0) + | ((mSidedRedstone[4] > 0) ? 16 : 0) + | ((mSidedRedstone[5] > 0) ? 32 : 0)); + if (tData != oRedstoneData) sendBlockEvent((byte) 3, oRedstoneData = tData); + } + + if (mNeedsBlockUpdate) { + updateNeighbours(mStrongRedstone, oStrongRedstone); + oStrongRedstone = mStrongRedstone; + mNeedsBlockUpdate = false; + } + } + } + } catch (Throwable e) { + e.printStackTrace(); + e.printStackTrace(GT_Log.err); + } + + if (isServerSide() && hasTimeStatisticsStarted && hasValidMetaTileEntity()) { + tTime = System.nanoTime() - tTime; + mTimeStatisticsIndex = (mTimeStatisticsIndex + 1) % mTimeStatistics.length; + mTimeStatistics[mTimeStatisticsIndex] = (int) tTime; + if (tTime > 0 && tTime > (GregTech_API.MILLISECOND_THRESHOLD_UNTIL_LAG_WARNING * 1000000L) + && mTickTimer > 1000 + && getMetaTileEntity().doTickProfilingMessageDuringThisTick() + && mLagWarningCount++ < 10) + GT_FML_LOGGER.warn( + "WARNING: Possible Lag Source at [" + xCoord + + "," + + yCoord + + "," + + zCoord + + "] in Dimension " + + worldObj.provider.dimensionId + + " with " + + tTime + + " ns caused by an instance of " + + getMetaTileEntity().getClass()); + } + + mWorkUpdate = mInventoryChanged = false; + } + + private void sendClientData() { + if (mSendClientData) { + NW.sendPacketToAllPlayersInRange( + worldObj, + new GT_Packet_TileEntity( + xCoord, + (short) yCoord, + zCoord, + mID, + getCoverInfoAtSide(ForgeDirection.DOWN).getCoverID(), + getCoverInfoAtSide(ForgeDirection.UP).getCoverID(), + getCoverInfoAtSide(ForgeDirection.NORTH).getCoverID(), + getCoverInfoAtSide(ForgeDirection.SOUTH).getCoverID(), + getCoverInfoAtSide(ForgeDirection.WEST).getCoverID(), + getCoverInfoAtSide(ForgeDirection.EAST).getCoverID(), + oTextureData = mConnections, + oUpdateData = hasValidMetaTileEntity() ? mMetaTileEntity.getUpdateData() : 0, + oRedstoneData = (byte) (((mSidedRedstone[0] > 0) ? 1 : 0) | ((mSidedRedstone[1] > 0) ? 2 : 0) + | ((mSidedRedstone[2] > 0) ? 4 : 0) + | ((mSidedRedstone[3] > 0) ? 8 : 0) + | ((mSidedRedstone[4] > 0) ? 16 : 0) + | ((mSidedRedstone[5] > 0) ? 32 : 0)), + oColor = mColor), + xCoord, + zCoord); + mSendClientData = false; + } + sendCoverDataIfNeeded(); + } + + public final void receiveMetaTileEntityData(short aID, int aCover0, int aCover1, int aCover2, int aCover3, + int aCover4, int aCover5, byte aTextureData, byte aUpdateData, byte aRedstoneData, byte aColorData) { + issueTextureUpdate(); + if (aID > 0 && mID != aID) { + mID = aID; + createNewMetatileEntity(mID); + } + + setCoverIDAtSide(ForgeDirection.DOWN, aCover0); + setCoverIDAtSide(ForgeDirection.UP, aCover1); + setCoverIDAtSide(ForgeDirection.NORTH, aCover2); + setCoverIDAtSide(ForgeDirection.SOUTH, aCover3); + setCoverIDAtSide(ForgeDirection.WEST, aCover4); + setCoverIDAtSide(ForgeDirection.EAST, aCover5); + + receiveClientEvent(GregTechTileClientEvents.CHANGE_COMMON_DATA, aTextureData); + receiveClientEvent(GregTechTileClientEvents.CHANGE_CUSTOM_DATA, aUpdateData); + receiveClientEvent(GregTechTileClientEvents.CHANGE_COLOR, aColorData); + receiveClientEvent(GregTechTileClientEvents.CHANGE_REDSTONE_OUTPUT, aRedstoneData); + } + + @Override + public boolean receiveClientEvent(int aEventID, int aValue) { + super.receiveClientEvent(aEventID, aValue); + + if (hasValidMetaTileEntity()) { + try { + mMetaTileEntity.receiveClientEvent((byte) aEventID, (byte) aValue); + } catch (Throwable e) { + GT_FML_LOGGER.error("Encountered Exception while receiving Data from the Server", e); + } + } + + if (isClientSide()) { + issueTextureUpdate(); + switch (aEventID) { + case GregTechTileClientEvents.CHANGE_COMMON_DATA -> mConnections = (byte) aValue; + case GregTechTileClientEvents.CHANGE_CUSTOM_DATA -> { + if (hasValidMetaTileEntity()) mMetaTileEntity.onValueUpdate((byte) aValue); + } + case GregTechTileClientEvents.CHANGE_COLOR -> { + if (aValue > 16 || aValue < 0) aValue = 0; + mColor = (byte) aValue; + } + case GregTechTileClientEvents.CHANGE_REDSTONE_OUTPUT -> { + mSidedRedstone[0] = (byte) ((aValue & 1) == 1 ? 15 : 0); + mSidedRedstone[1] = (byte) ((aValue & 2) == 2 ? 15 : 0); + mSidedRedstone[2] = (byte) ((aValue & 4) == 4 ? 15 : 0); + mSidedRedstone[3] = (byte) ((aValue & 8) == 8 ? 15 : 0); + mSidedRedstone[4] = (byte) ((aValue & 16) == 16 ? 15 : 0); + mSidedRedstone[5] = (byte) ((aValue & 32) == 32 ? 15 : 0); + } + case GregTechTileClientEvents.DO_SOUND -> { + if (hasValidMetaTileEntity() && mTickTimer > 20) + mMetaTileEntity.doSound((byte) aValue, xCoord + 0.5, yCoord + 0.5, zCoord + 0.5); + } + case GregTechTileClientEvents.START_SOUND_LOOP -> { + if (hasValidMetaTileEntity() && mTickTimer > 20) + mMetaTileEntity.startSoundLoop((byte) aValue, xCoord + 0.5, yCoord + 0.5, zCoord + 0.5); + } + case GregTechTileClientEvents.STOP_SOUND_LOOP -> { + if (hasValidMetaTileEntity() && mTickTimer > 20) + mMetaTileEntity.stopSoundLoop((byte) aValue, xCoord + 0.5, yCoord + 0.5, zCoord + 0.5); + } + } + } + return true; + } + + @Override + public ArrayList<String> getDebugInfo(EntityPlayer aPlayer, int aLogLevel) { + final ArrayList<String> tList = new ArrayList<>(); + if (aLogLevel > 3) { + tList.add( + "Meta-ID: " + EnumChatFormatting.BLUE + + mID + + EnumChatFormatting.RESET + + (hasValidMetaTileEntity() ? EnumChatFormatting.GREEN + " valid" + EnumChatFormatting.RESET + : EnumChatFormatting.RED + " invalid" + EnumChatFormatting.RESET) + + (mMetaTileEntity == null + ? EnumChatFormatting.RED + " MetaTileEntity == null!" + EnumChatFormatting.RESET + : " ")); + } + if (aLogLevel > 1) { + if (hasTimeStatisticsStarted) { + double tAverageTime = 0; + double tWorstTime = 0; + int amountOfZero = 0; + for (int tTime : mTimeStatistics) { + tAverageTime += tTime; + if (tTime > tWorstTime) { + tWorstTime = tTime; + } + if (tTime == 0) { + amountOfZero += 1; + } + } + // tick time zero means it has not been updated yet + int samples = mTimeStatistics.length - amountOfZero; + if (samples > 0) { + tList.add( + "Average CPU-load of ~" + (tAverageTime / samples) + + "ns since " + + samples + + " ticks with worst time of " + + tWorstTime + + "ns."); + } + } else { + startTimeStatistics(); + tList.add("Just started tick time statistics."); + } + if (mLagWarningCount > 0) { + tList.add( + "Caused " + (mLagWarningCount >= 10 ? "more than 10" : mLagWarningCount) + + " Lag Spike Warnings (anything taking longer than " + + GregTech_API.MILLISECOND_THRESHOLD_UNTIL_LAG_WARNING + + "ms) on the Server."); + } + if (mMetaTileEntity != null) { + tList.add( + "Is" + (mMetaTileEntity.isAccessAllowed(aPlayer) ? " " + : EnumChatFormatting.RED + " not " + EnumChatFormatting.RESET) + "accessible for you"); + } + } + if (joinedIc2Enet) tList.add("Joined IC2 ENet"); + + return mMetaTileEntity != null ? mMetaTileEntity.getSpecialDebugInfo(this, aPlayer, aLogLevel, tList) + : new ArrayList<>(); + } + + @Override + public boolean isGivingInformation() { + if (canAccessData()) return mMetaTileEntity.isGivingInformation(); + return false; + } + + @Override + public ForgeDirection getBackFacing() { + return getFrontFacing().getOpposite(); + } + + @Override + public ForgeDirection getFrontFacing() { + return ForgeDirection.UNKNOWN; + } + + @Override + public void setFrontFacing(ForgeDirection aFacing) { + doEnetUpdate(); + } + + @Override + public int getSizeInventory() { + if (canAccessData()) return mMetaTileEntity.getSizeInventory(); + return 0; + } + + @Override + public ItemStack getStackInSlot(int aIndex) { + if (canAccessData()) return mMetaTileEntity.getStackInSlot(aIndex); + return null; + } + + @Override + public void setInventorySlotContents(int aIndex, ItemStack aStack) { + markDirty(); + mInventoryChanged = true; + if (canAccessData()) mMetaTileEntity + .setInventorySlotContents(aIndex, worldObj.isRemote ? aStack : GT_OreDictUnificator.setStack(true, aStack)); + } + + @Override + public String getInventoryName() { + if (canAccessData()) return mMetaTileEntity.getInventoryName(); + if (GregTech_API.METATILEENTITIES[mID] != null) return GregTech_API.METATILEENTITIES[mID].getInventoryName(); + return ""; + } + + @Override + public int getInventoryStackLimit() { + if (canAccessData()) return mMetaTileEntity.getInventoryStackLimit(); + return 64; + } + + @Override + public void openInventory() { + /* Do nothing */ + } + + @Override + public void closeInventory() { + /* Do nothing */ + } + + @Override + public boolean isUseableByPlayer(EntityPlayer aPlayer) { + return hasValidMetaTileEntity() && mTickTimer > 1 + && getTileEntityOffset(0, 0, 0) == this + && aPlayer.getDistanceSq(xCoord + 0.5, yCoord + 0.5, zCoord + 0.5) < 64 + && mMetaTileEntity.isAccessAllowed(aPlayer); + } + + @Override + public void validate() { + super.validate(); + mTickTimer = 0; + } + + @Override + public void invalidate() { + tileEntityInvalid = false; + if (hasValidMetaTileEntity()) { + mMetaTileEntity.onRemoval(); + mMetaTileEntity.setBaseMetaTileEntity(null); + } + leaveEnet(); + super.invalidate(); + } + + @Override + public boolean hasCustomInventoryName() { + return false; + } + + @Override + public ItemStack getStackInSlotOnClosing(int slot) { + ItemStack stack = getStackInSlot(slot); + if (stack != null) setInventorySlotContents(slot, null); + return stack; + } + + /** + * Checks validity of meta tile and delegates to it + */ + @Override + public void onMachineBlockUpdate() { + if (canAccessData()) mMetaTileEntity.onMachineBlockUpdate(); + } + + /** + * Checks validity of meta tile and delegates to it + */ + @Override + public boolean isMachineBlockUpdateRecursive() { + return canAccessData() && mMetaTileEntity.isMachineBlockUpdateRecursive(); + } + + @Override + public int getProgress() { + return canAccessData() ? mMetaTileEntity.getProgresstime() : 0; + } + + @Override + public int getMaxProgress() { + return canAccessData() ? mMetaTileEntity.maxProgresstime() : 0; + } + + @Override + public boolean increaseProgress(int aProgressAmountInTicks) { + return canAccessData() && mMetaTileEntity.increaseProgress(aProgressAmountInTicks) != aProgressAmountInTicks; + } + + @Override + public boolean hasThingsToDo() { + return getMaxProgress() > 0; + } + + @Override + public void enableWorking() { + if (!mWorks) mWorkUpdate = true; + mWorks = true; + reloadLocks(); + } + + @Override + public void disableWorking() { + mWorks = false; + reloadLocks(); + } + + @Override + public boolean isAllowedToWork() { + return mWorks; + } + + @Override + public boolean hasWorkJustBeenEnabled() { + return mWorkUpdate; + } + + @Override + public byte getWorkDataValue() { + return 0; + } + + @Override + public void setWorkDataValue(byte aValue) { + /* Do nothing */ + } + + @Override + public int getMetaTileID() { + return mID; + } + + @Override + public int setMetaTileID(short aID) { + return mID = aID; + } + + @Override + public boolean isActive() { + return false; + } + + @Override + public void setActive(boolean aActive) { + /* Do nothing */ + } + + @Override + public long getTimer() { + return mTickTimer; + } + + @Override + public boolean decreaseStoredEnergyUnits(long aEnergy, boolean aIgnoreTooLessEnergy) { + return false; + } + + @Override + public boolean increaseStoredEnergyUnits(long aEnergy, boolean aIgnoreTooMuchEnergy) { + return false; + } + + @Override + public boolean inputEnergyFrom(ForgeDirection side) { + return false; + } + + @Override + public boolean inputEnergyFrom(ForgeDirection side, boolean waitForActive) { + return false; + } + + @Override + public boolean outputsEnergyTo(ForgeDirection side) { + return false; + } + + @Override + public boolean outputsEnergyTo(ForgeDirection side, boolean waitForActive) { + return false; + } + + @Override + public long getOutputAmperage() { + return 0; + } + + @Override + public long getOutputVoltage() { + return 0; + } + + @Override + public long getInputAmperage() { + return 0; + } + + @Override + public long getInputVoltage() { + return 0; + } + + @Override + public long getUniversalEnergyStored() { + return Math.max(getStoredEU(), getStoredSteam()); + } + + @Override + public long getUniversalEnergyCapacity() { + return Math.max(getEUCapacity(), getSteamCapacity()); + } + + @Override + public long getStoredEU() { + return 0; + } + + @Override + public long getEUCapacity() { + return 0; + } + + @Override + public ITexture[] getTexture(Block aBlock, ForgeDirection side) { + final ITexture rIcon = getCoverTexture(side); + if (rIcon != null) return new ITexture[] { rIcon }; + return getTextureUncovered(side); + } + + @Override + public ITexture[] getTextureCovered(ForgeDirection side) { + final ITexture coverTexture = getCoverTexture(side); + final ITexture[] textureUncovered = getTextureUncovered(side); + final ITexture[] textureCovered; + if (coverTexture != null) { + textureCovered = Arrays.copyOf(textureUncovered, textureUncovered.length + 1); + textureCovered[textureUncovered.length] = coverTexture; + return textureCovered; + } else { + return textureUncovered; + } + } + + @Override + public ITexture[] getTextureUncovered(ForgeDirection sideDirection) { + if ((mConnections & IConnectable.HAS_FRESHFOAM) != 0) return Textures.BlockIcons.FRESHFOAM; + if ((mConnections & IConnectable.HAS_HARDENEDFOAM) != 0) return Textures.BlockIcons.HARDENEDFOAMS[mColor]; + if ((mConnections & IConnectable.HAS_FOAM) != 0) return Textures.BlockIcons.ERROR_RENDERING; + int tConnections = mConnections; + if (tConnections == IConnectable.CONNECTED_WEST || tConnections == IConnectable.CONNECTED_EAST) + tConnections = IConnectable.CONNECTED_WEST | IConnectable.CONNECTED_EAST; + else if (tConnections == IConnectable.CONNECTED_DOWN || tConnections == IConnectable.CONNECTED_UP) + tConnections = IConnectable.CONNECTED_DOWN | IConnectable.CONNECTED_UP; + else if (tConnections == IConnectable.CONNECTED_NORTH || tConnections == IConnectable.CONNECTED_SOUTH) + tConnections = IConnectable.CONNECTED_NORTH | IConnectable.CONNECTED_SOUTH; + if (hasValidMetaTileEntity()) return mMetaTileEntity.getTexture( + this, + sideDirection, + tConnections, + mColor - 1, + tConnections == 0 || (tConnections & sideDirection.flag) != 0, + getOutputRedstoneSignal(sideDirection) > 0); + return Textures.BlockIcons.ERROR_RENDERING; + } + + @Override + protected boolean hasValidMetaTileEntity() { + return mMetaTileEntity != null && mMetaTileEntity.getBaseMetaTileEntity() == this; + } + + @Override + public void doExplosion(long aAmount) { + if (canAccessData()) { + mMetaTileEntity.onExplosion(); + mMetaTileEntity.doExplosion(aAmount); + } + } + + @Override + public ArrayList<ItemStack> getDrops() { + final ItemStack rStack = new ItemStack(GregTech_API.sBlockMachines, 1, mID); + final NBTTagCompound tNBT = new NBTTagCompound(); + + writeCoverNBT(tNBT, true); + + if (hasValidMetaTileEntity()) mMetaTileEntity.setItemNBT(tNBT); + if (!tNBT.hasNoTags()) rStack.setTagCompound(tNBT); + + onBaseTEDestroyed(); + return new ArrayList<>(Collections.singletonList(rStack)); + } + + @Override + public boolean shouldDropItemAt(int index) { + return this.mMetaTileEntity == null || this.mMetaTileEntity.shouldDropItemAt(index); + } + + @Override + public boolean onRightclick(EntityPlayer aPlayer, ForgeDirection side, float aX, float aY, float aZ) { + if (isClientSide()) { + // Configure Cover, sneak can also be: screwdriver, wrench, side cutter, soldering iron + if (aPlayer.isSneaking()) { + final ForgeDirection tSide = (getCoverIDAtSide(side) == 0) + ? GT_Utility.determineWrenchingSide(side, aX, aY, aZ) + : side; + return (getCoverInfoAtSide(tSide).hasCoverGUI()); + } else if (getCoverBehaviorAtSideNew(side).onCoverRightclickClient(side, this, aPlayer, aX, aY, aZ)) { + return true; + } + } + if (isServerSide()) { + final ItemStack tCurrentItem = aPlayer.inventory.getCurrentItem(); + if (tCurrentItem != null) { + if (getColorization() >= 0 + && GT_Utility.areStacksEqual(new ItemStack(Items.water_bucket, 1), tCurrentItem)) { + mMetaTileEntity.markDirty(); + tCurrentItem.func_150996_a(Items.bucket); + setColorization((byte) -1); + return true; + } + final ForgeDirection tSide = GT_Utility.determineWrenchingSide(side, aX, aY, aZ); + if (GT_Utility.isStackInList(tCurrentItem, GregTech_API.sWrenchList)) { + + if (mMetaTileEntity.onWrenchRightClick(side, tSide, aPlayer, aX, aY, aZ, tCurrentItem)) { + mMetaTileEntity.markDirty(); + GT_ModHandler.damageOrDechargeItem(tCurrentItem, 1, 1000, aPlayer); + GT_Utility.sendSoundToPlayers( + worldObj, + SoundResource.IC2_TOOLS_WRENCH, + 1.0F, + -1, + xCoord, + yCoord, + zCoord); + } + return true; + } + if (GT_Utility.isStackInList(tCurrentItem, GregTech_API.sScrewdriverList)) { + if (getCoverIDAtSide(side) == 0 && getCoverIDAtSide(tSide) != 0) { + if (GT_ModHandler.damageOrDechargeItem(tCurrentItem, 1, 200, aPlayer)) { + setCoverDataAtSide( + tSide, + getCoverInfoAtSide(tSide).onCoverScrewdriverClick(aPlayer, 0.5F, 0.5F, 0.5F)); + mMetaTileEntity.onScrewdriverRightClick(tSide, aPlayer, aX, aY, aZ, tCurrentItem); + mMetaTileEntity.markDirty(); + GT_Utility.sendSoundToPlayers( + worldObj, + SoundResource.IC2_TOOLS_WRENCH, + 1.0F, + -1, + xCoord, + yCoord, + zCoord); + } + } else { + if (GT_ModHandler.damageOrDechargeItem(tCurrentItem, 1, 1000, aPlayer)) { + setCoverDataAtSide( + side, + getCoverInfoAtSide(side).onCoverScrewdriverClick(aPlayer, aX, aY, aZ)); + mMetaTileEntity.onScrewdriverRightClick(side, aPlayer, aX, aY, aZ, tCurrentItem); + mMetaTileEntity.markDirty(); + GT_Utility.sendSoundToPlayers( + worldObj, + SoundResource.IC2_TOOLS_WRENCH, + 1.0F, + -1, + xCoord, + yCoord, + zCoord); + } + } + return true; + } + + if (GT_Utility.isStackInList(tCurrentItem, GregTech_API.sHardHammerList)) { + return true; + } + + if (GT_Utility.isStackInList(tCurrentItem, GregTech_API.sSoftHammerList)) { + if (GT_ModHandler.damageOrDechargeItem(tCurrentItem, 1, 1000, aPlayer)) { + if (mWorks) disableWorking(); + else enableWorking(); + mMetaTileEntity.markDirty(); + GT_Utility.sendChatToPlayer( + aPlayer, + GT_Utility.trans("090", "Machine Processing: ") + + (isAllowedToWork() ? GT_Utility.trans("088", "Enabled") + : GT_Utility.trans("087", "Disabled"))); + GT_Utility.sendSoundToPlayers( + worldObj, + SoundResource.IC2_TOOLS_RUBBER_TRAMPOLINE, + 1.0F, + -1, + xCoord, + yCoord, + zCoord); + } + return true; + } + + if (GT_Utility.isStackInList(tCurrentItem, GregTech_API.sWireCutterList)) { + if (mMetaTileEntity.onWireCutterRightClick(side, tSide, aPlayer, aX, aY, aZ, tCurrentItem)) { + mMetaTileEntity.markDirty(); + // logic handled internally + GT_Utility.sendSoundToPlayers( + worldObj, + SoundResource.IC2_TOOLS_WRENCH, + 1.0F, + -1, + xCoord, + yCoord, + zCoord); + } + doEnetUpdate(); + return true; + } + + if (GT_Utility.isStackInList(tCurrentItem, GregTech_API.sSolderingToolList)) { + if (mMetaTileEntity.onSolderingToolRightClick(side, tSide, aPlayer, aX, aY, aZ, tCurrentItem)) { + mMetaTileEntity.markDirty(); + // logic handled internally + GT_Utility.sendSoundToPlayers( + worldObj, + SoundResource.IC2_TOOLS_BATTERY_USE, + 1.0F, + -1, + xCoord, + yCoord, + zCoord); + } else if (GT_ModHandler.useSolderingIron(tCurrentItem, aPlayer)) { + mMetaTileEntity.markDirty(); + mStrongRedstone ^= tSide.flag; + GT_Utility.sendChatToPlayer( + aPlayer, + GT_Utility.trans("091", "Redstone Output at Side ") + tSide + + GT_Utility.trans("092", " set to: ") + + ((mStrongRedstone & tSide.flag) != 0 ? GT_Utility.trans("093", "Strong") + : GT_Utility.trans("094", "Weak"))); + GT_Utility.sendSoundToPlayers( + worldObj, + SoundResource.IC2_TOOLS_BATTERY_USE, + 3.0F, + -1, + xCoord, + yCoord, + zCoord); + issueBlockUpdate(); + } + doEnetUpdate(); + return true; + } + + ForgeDirection coverSide = side; + if (getCoverIDAtSide(side) == 0) coverSide = tSide; + + final CoverInfo coverInfo = getCoverInfoAtSide(coverSide); + + if (coverInfo.getCoverID() == 0) { + if (GT_Utility.isStackInList(tCurrentItem, GregTech_API.sCovers.keySet())) { + final GT_CoverBehaviorBase<?> coverBehavior = GregTech_API.getCoverBehaviorNew(tCurrentItem); + if (coverBehavior.isCoverPlaceable(coverSide, tCurrentItem, this) + && mMetaTileEntity.allowCoverOnSide(coverSide, new GT_ItemStack(tCurrentItem))) { + + setCoverItemAtSide(coverSide, tCurrentItem); + coverBehavior.onPlayerAttach(aPlayer, tCurrentItem, this, side); + + mMetaTileEntity.markDirty(); + if (!aPlayer.capabilities.isCreativeMode) tCurrentItem.stackSize--; + GT_Utility.sendSoundToPlayers( + worldObj, + SoundResource.IC2_TOOLS_WRENCH, + 1.0F, + -1, + xCoord, + yCoord, + zCoord); + } + return true; + } + } else { + if (GT_Utility.isStackInList(tCurrentItem, GregTech_API.sCrowbarList)) { + if (GT_ModHandler.damageOrDechargeItem(tCurrentItem, 1, 1000, aPlayer)) { + GT_Utility.sendSoundToPlayers( + worldObj, + SoundResource.RANDOM_BREAK, + 1.0F, + -1, + xCoord, + yCoord, + zCoord); + dropCover(coverSide, side, false); + mMetaTileEntity.markDirty(); + } + return true; + } + } + } else if (aPlayer.isSneaking()) { // Sneak click, no tool -> open cover config or turn back. + side = (getCoverIDAtSide(side) == 0) ? GT_Utility.determineWrenchingSide(side, aX, aY, aZ) : side; + final CoverInfo coverInfo = getCoverInfoAtSide(side); + return coverInfo.isValid() && coverInfo.onCoverShiftRightClick(aPlayer); + } + + if (getCoverInfoAtSide(side).onCoverRightClick(aPlayer, aX, aY, aZ)) return true; + } + + if (!getCoverInfoAtSide(side).isGUIClickable()) return false; + + try { + if (!aPlayer.isSneaking() && hasValidMetaTileEntity()) { + final boolean handled = mMetaTileEntity.onRightclick(this, aPlayer, side, aX, aY, aZ); + if (handled) { + mMetaTileEntity.markDirty(); + } + return handled; + } + } catch (Throwable e) { + GT_FML_LOGGER.error("Encountered Exception while right clicking TileEntity", e); + } + + return false; + } + + @Override + public void onLeftclick(EntityPlayer aPlayer) { + try { + if (aPlayer != null && hasValidMetaTileEntity()) mMetaTileEntity.onLeftclick(this, aPlayer); + } catch (Throwable e) { + GT_FML_LOGGER.error("Encountered Exception while left clicking TileEntity", e); + } + } + + @Override + public boolean isDigitalChest() { + return false; + } + + @Override + public ItemStack[] getStoredItemData() { + return null; + } + + @Override + public void setItemCount(int aCount) { + // + } + + @Override + public int getMaxItemCount() { + return 0; + } + + /** + * Can put aStack into Slot + */ + @Override + public boolean isItemValidForSlot(int aIndex, ItemStack aStack) { + return canAccessData() && mMetaTileEntity.isItemValidForSlot(aIndex, aStack); + } + + /** + * returns all valid Inventory Slots, no matter which Side (Unless it's covered). The Side Stuff is done in the + * following two Functions. + */ + @Override + public int[] getAccessibleSlotsFromSide(int ordinalSide) { + final CoverInfo coverInfo = getCoverInfoAtSide(ForgeDirection.getOrientation(ordinalSide)); + if (canAccessData() && (coverInfo.letsItemsOut(-1) || coverInfo.letsItemsIn(-1))) + return mMetaTileEntity.getAccessibleSlotsFromSide(ordinalSide); + return GT_Values.emptyIntArray; + } + + /** + * Can put aStack into Slot at Side + */ + @Override + public boolean canInsertItem(int aIndex, ItemStack aStack, int ordinalSide) { + return canAccessData() && getCoverInfoAtSide(ForgeDirection.getOrientation(ordinalSide)).letsItemsIn(aIndex) + && mMetaTileEntity.canInsertItem(aIndex, aStack, ordinalSide); + } + + /** + * Can pull aStack out of Slot from Side + */ + @Override + public boolean canExtractItem(int aIndex, ItemStack aStack, int ordinalSide) { + final ForgeDirection side = ForgeDirection.getOrientation(ordinalSide); + return canAccessData() + && getCoverBehaviorAtSideNew(side) + .letsItemsOut(side, getCoverIDAtSide(side), getComplexCoverDataAtSide(side), aIndex, this) + && mMetaTileEntity.canExtractItem(aIndex, aStack, ordinalSide); + } + + @Override + public boolean isUpgradable() { + return false; + } + + @Override + public boolean isSteamEngineUpgradable() { + return isUpgradable() && !hasSteamEngineUpgrade() && getSteamCapacity() > 0; + } + + @Override + public boolean addSteamEngineUpgrade() { + if (isSteamEngineUpgradable()) { + issueBlockUpdate(); + return true; + } + return false; + } + + @Override + public boolean hasSteamEngineUpgrade() { + return false; + } + + @Override + public void setGenericRedstoneOutput(boolean aOnOff) { + // Do nothing + } + + @Override + public int getErrorDisplayID() { + return 0; + } + + @Override + public void setErrorDisplayID(int aErrorID) { + // + } + + @Override + public IMetaTileEntity getMetaTileEntity() { + return hasValidMetaTileEntity() ? mMetaTileEntity : null; + } + + @Override + public void setMetaTileEntity(IMetaTileEntity aMetaTileEntity) { + mMetaTileEntity = (MetaPipeEntity) aMetaTileEntity; + } + + @Override + public void setLightValue(byte aLightValue) { + // + } + + @Override + public long getAverageElectricInput() { + return 0; + } + + @Override + public long getAverageElectricOutput() { + return 0; + } + + @Override + public String getOwnerName() { + return "Player"; + } + + @Override + public String setOwnerName(String aName) { + return "Player"; + } + + @Override + public UUID getOwnerUuid() { + return GT_Utility.defaultUuid; + } + + @Override + public void setOwnerUuid(UUID uuid) {} + + @Override + public byte getComparatorValue(ForgeDirection side) { + return canAccessData() ? mMetaTileEntity.getComparatorValue(side) : 0; + } + + @Override + public ItemStack decrStackSize(int aIndex, int aAmount) { + if (canAccessData()) { + mInventoryChanged = true; + return mMetaTileEntity.decrStackSize(aIndex, aAmount); + } + return null; + } + + @Override + public long injectEnergyUnits(ForgeDirection side, long aVoltage, long aAmperage) { + if (canAccessData()) return mMetaTileEntity.injectEnergyUnits(side, aVoltage, aAmperage); + return 0; + } + + @Override + public boolean drainEnergyUnits(ForgeDirection side, long aVoltage, long aAmperage) { + return false; + } + + @Override + public boolean acceptsRotationalEnergy(ForgeDirection side) { + if (!canAccessData() || getCoverIDAtSide(side) != 0) return false; + return mMetaTileEntity.acceptsRotationalEnergy(side); + } + + @Override + public boolean injectRotationalEnergy(ForgeDirection side, long aSpeed, long aEnergy) { + if (!canAccessData() || getCoverIDAtSide(side) != 0) return false; + return mMetaTileEntity.injectRotationalEnergy(side, aSpeed, aEnergy); + } + + private boolean canMoveFluidOnSide(ForgeDirection side, Fluid fluid, boolean isFill) { + if (side == ForgeDirection.UNKNOWN) return true; + + final IFluidHandler tTileEntity = getITankContainerAtSide(side); + // Only require a connection if there's something to connect to - Allows fluid cells & buckets to interact with + // the pipe + if (tTileEntity != null && !mMetaTileEntity.isConnectedAtSide(side)) return false; + + if (isFill && mMetaTileEntity.isLiquidInput(side) && getCoverInfoAtSide(side).letsFluidIn(fluid)) return true; + + return !isFill && mMetaTileEntity.isLiquidOutput(side) && getCoverInfoAtSide(side).letsFluidOut(fluid); + } + + @Override + public int fill(ForgeDirection side, FluidStack aFluidStack, boolean doFill) { + if (mTickTimer > 5 && canAccessData() + && canMoveFluidOnSide(side, aFluidStack == null ? null : aFluidStack.getFluid(), true)) + return mMetaTileEntity.fill(side, aFluidStack, doFill); + return 0; + } + + @Override + public FluidStack drain(ForgeDirection side, int maxDrain, boolean doDrain) { + if (mTickTimer > 5 && canAccessData() + && canMoveFluidOnSide( + side, + mMetaTileEntity.getFluid() == null ? null + : mMetaTileEntity.getFluid() + .getFluid(), + false)) + return mMetaTileEntity.drain(side, maxDrain, doDrain); + return null; + } + + @Override + public FluidStack drain(ForgeDirection side, FluidStack aFluidStack, boolean doDrain) { + if (mTickTimer > 5 && canAccessData() + && canMoveFluidOnSide(side, aFluidStack == null ? null : aFluidStack.getFluid(), false)) + return mMetaTileEntity.drain(side, aFluidStack, doDrain); + return null; + } + + @Override + public boolean canFill(ForgeDirection side, Fluid aFluid) { + if (mTickTimer > 5 && canAccessData() && canMoveFluidOnSide(side, aFluid, true)) + return mMetaTileEntity.canFill(side, aFluid); + return false; + } + + @Override + public boolean canDrain(ForgeDirection side, Fluid aFluid) { + if (mTickTimer > 5 && canAccessData() && canMoveFluidOnSide(side, aFluid, false)) + return mMetaTileEntity.canDrain(side, aFluid); + return false; + } + + @Override + public FluidTankInfo[] getTankInfo(ForgeDirection side) { + final CoverInfo coverInfo = getCoverInfoAtSide(side); + if (canAccessData() + && (side == ForgeDirection.UNKNOWN || (mMetaTileEntity.isLiquidInput(side) && coverInfo.letsFluidIn(null)) + || (mMetaTileEntity.isLiquidOutput(side) && coverInfo.letsFluidOut(null)) + // Doesn't need to be connected to get Tank Info -- otherwise things can't connect + )) return mMetaTileEntity.getTankInfo(side); + return new FluidTankInfo[] {}; + } + + @Override + public boolean addStackToSlot(int aIndex, ItemStack aStack) { + if (GT_Utility.isStackInvalid(aStack)) return true; + if (aIndex < 0 || aIndex >= getSizeInventory()) return false; + final ItemStack tStack = getStackInSlot(aIndex); + if (GT_Utility.isStackInvalid(tStack)) { + setInventorySlotContents(aIndex, aStack); + return true; + } + aStack = GT_OreDictUnificator.get(aStack); + if (GT_Utility.areStacksEqual(tStack, aStack) + && tStack.stackSize + aStack.stackSize <= Math.min(aStack.getMaxStackSize(), getInventoryStackLimit())) { + markDirty(); + tStack.stackSize += aStack.stackSize; + return true; + } + return false; + } + + @Override + public boolean addStackToSlot(int aIndex, ItemStack aStack, int aAmount) { + return addStackToSlot(aIndex, GT_Utility.copyAmount(aAmount, aStack)); + } + + @Override + public byte getColorization() { + return (byte) (mColor - 1); + } + + @Override + public byte setColorization(byte aColor) { + if (aColor > 15 || aColor < -1) aColor = -1; + mColor = (byte) (aColor + 1); + if (canAccessData()) mMetaTileEntity.onColorChangeServer(aColor); + return mColor; + } + + @Override + public float getThickNess() { + if (canAccessData()) return mMetaTileEntity.getThickNess(); + return 1.0F; + } + + public boolean renderInside(ForgeDirection side) { + if (canAccessData()) return mMetaTileEntity.renderInside(side); + return false; + } + + @Override + public float getBlastResistance(ForgeDirection side) { + return canAccessData() ? Math.max(0, getMetaTileEntity().getExplosionResistance(side)) : 5.0F; + } + + @Override + public void onBlockDestroyed() { + if (canAccessData()) getMetaTileEntity().onBlockDestroyed(); + } + + @Override + public boolean isMufflerUpgradable() { + return false; + } + + @Override + public boolean addMufflerUpgrade() { + return false; + } + + @Override + public boolean hasMufflerUpgrade() { + return false; + } + + @Override + public boolean isUniversalEnergyStored(long aEnergyAmount) { + return getUniversalEnergyStored() >= aEnergyAmount; + } + + @Override + public String[] getInfoData() { + if (canAccessData()) return getMetaTileEntity().getInfoData(); + return new String[] {}; + } + + @Override + public byte getConnections() { + return mConnections; + } + + public void onNeighborBlockChange(int aX, int aY, int aZ) { + if (canAccessData()) { + final IMetaTileEntity meta = getMetaTileEntity(); + if (meta instanceof MetaPipeEntity) { + // Trigger a checking of connections in case someone placed down a block that the pipe/wire shouldn't be + // connected to. + // However; don't do it immediately in case the world isn't finished loading + // (This caused issues with AE2 GTEU p2p connections. + ((MetaPipeEntity) meta).setCheckConnections(); + } + } + } + + @Override + public int getLightOpacity() { + return mMetaTileEntity == null ? 0 : mMetaTileEntity.getLightOpacity(); + } + + @Override + public void addCollisionBoxesToList(World aWorld, int aX, int aY, int aZ, AxisAlignedBB inputAABB, + List<AxisAlignedBB> outputAABB, Entity collider) { + mMetaTileEntity.addCollisionBoxesToList(aWorld, aX, aY, aZ, inputAABB, outputAABB, collider); + } + + @Override + public AxisAlignedBB getCollisionBoundingBoxFromPool(World aWorld, int aX, int aY, int aZ) { + return mMetaTileEntity.getCollisionBoundingBoxFromPool(aWorld, aX, aY, aZ); + } + + @Override + public void onEntityCollidedWithBlock(World aWorld, int aX, int aY, int aZ, Entity collider) { + mMetaTileEntity.onEntityCollidedWithBlock(aWorld, aX, aY, aZ, collider); + } + + @Override + public int[] getTimeStatistics() { + return mTimeStatistics; + } + + @Override + public void startTimeStatistics() { + hasTimeStatisticsStarted = true; + } + + @Override + public void getWailaBody(ItemStack itemStack, List<String> currentTip, IWailaDataAccessor accessor, + IWailaConfigHandler config) { + super.getWailaBody(itemStack, currentTip, accessor, config); + mMetaTileEntity.getWailaBody(itemStack, currentTip, accessor, config); + } +} diff --git a/src/main/java/gregtech/api/metatileentity/BaseMetaTileEntity.java b/src/main/java/gregtech/api/metatileentity/BaseMetaTileEntity.java new file mode 100644 index 0000000000..bf6358c884 --- /dev/null +++ b/src/main/java/gregtech/api/metatileentity/BaseMetaTileEntity.java @@ -0,0 +1,2538 @@ +package gregtech.api.metatileentity; + +import static gregtech.GT_Mod.GT_FML_LOGGER; +import static gregtech.api.enums.GT_Values.NW; +import static gregtech.api.enums.GT_Values.V; +import static gregtech.api.objects.XSTR.XSTR_INSTANCE; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.UUID; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import net.minecraft.block.Block; +import net.minecraft.block.BlockFire; +import net.minecraft.entity.Entity; +import net.minecraft.entity.item.EntityItem; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.init.Items; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.server.MinecraftServer; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.AxisAlignedBB; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.StatCollector; +import net.minecraft.world.EnumSkyBlock; +import net.minecraft.world.World; +import net.minecraft.world.biome.BiomeGenBase; +import net.minecraftforge.common.util.ForgeDirection; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.FluidTankInfo; + +import org.jetbrains.annotations.NotNull; + +import com.gtnewhorizon.structurelib.alignment.IAlignment; +import com.gtnewhorizon.structurelib.alignment.IAlignmentProvider; +import com.gtnewhorizon.structurelib.alignment.constructable.IConstructable; +import com.gtnewhorizon.structurelib.alignment.constructable.IConstructableProvider; + +import appeng.api.networking.IGridNode; +import appeng.api.networking.security.IActionHost; +import appeng.api.util.AECableType; +import appeng.api.util.DimensionalCoord; +import appeng.helpers.ICustomNameObject; +import appeng.me.helpers.AENetworkProxy; +import appeng.me.helpers.IGridProxyable; +import appeng.tile.TileEvent; +import appeng.tile.events.TileEventType; +import cpw.mods.fml.relauncher.ReflectionHelper; +import gregtech.GT_Mod; +import gregtech.api.GregTech_API; +import gregtech.api.enums.GT_Values; +import gregtech.api.enums.ItemList; +import gregtech.api.enums.SoundResource; +import gregtech.api.enums.Textures; +import gregtech.api.graphs.GenerateNodeMap; +import gregtech.api.graphs.GenerateNodeMapPower; +import gregtech.api.graphs.Node; +import gregtech.api.interfaces.ICleanroom; +import gregtech.api.interfaces.ICleanroomReceiver; +import gregtech.api.interfaces.ITexture; +import gregtech.api.interfaces.metatileentity.IMetaTileEntity; +import gregtech.api.interfaces.tileentity.IDebugableTileEntity; +import gregtech.api.interfaces.tileentity.IEnergyConnected; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.interfaces.tileentity.IGregtechWailaProvider; +import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_BasicMachine; +import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch; +import gregtech.api.net.GT_Packet_TileEntity; +import gregtech.api.objects.GT_ItemStack; +import gregtech.api.objects.blockupdate.BlockUpdateHandler; +import gregtech.api.util.GT_CoverBehaviorBase; +import gregtech.api.util.GT_Log; +import gregtech.api.util.GT_ModHandler; +import gregtech.api.util.GT_OreDictUnificator; +import gregtech.api.util.GT_Utility; +import gregtech.api.util.shutdown.ShutDownReason; +import gregtech.api.util.shutdown.ShutDownReasonRegistry; +import gregtech.common.GT_Pollution; +import gregtech.common.covers.CoverInfo; +import ic2.api.Direction; +import mcp.mobius.waila.api.IWailaConfigHandler; +import mcp.mobius.waila.api.IWailaDataAccessor; + +/** + * NEVER INCLUDE THIS FILE IN YOUR MOD!!! + * <p/> + * This is the main TileEntity for EVERYTHING. + */ +public class BaseMetaTileEntity extends CommonMetaTileEntity + implements IGregTechTileEntity, IActionHost, IGridProxyable, IAlignmentProvider, IConstructableProvider, + IDebugableTileEntity, IGregtechWailaProvider, ICleanroomReceiver, ICustomNameObject { + + private static final Field ENTITY_ITEM_HEALTH_FIELD = ReflectionHelper + .findField(EntityItem.class, "health", "field_70291_e"); + private final boolean[] mActiveEUInputs = new boolean[] { false, false, false, false, false, false }; + private final boolean[] mActiveEUOutputs = new boolean[] { false, false, false, false, false, false }; + private final int[] mTimeStatistics = new int[GregTech_API.TICKS_FOR_LAG_AVERAGING]; + private boolean hasTimeStatisticsStarted; + public long mLastSoundTick = 0; + public boolean mWasShutdown = false; + public @Nonnull ShutDownReason lastShutDownReason = ShutDownReasonRegistry.NONE; + protected MetaTileEntity mMetaTileEntity; + protected long mStoredEnergy = 0, mStoredSteam = 0; + protected int mAverageEUInputIndex = 0, mAverageEUOutputIndex = 0; + protected boolean mReleaseEnergy = false; + protected final long[] mAverageEUInput = new long[] { 0, 0, 0, 0, 0 }; + protected final long[] mAverageEUOutput = new long[] { 0, 0, 0, 0, 0 }; + private boolean mHasEnoughEnergy = true, mRunningThroughTick = false, mInputDisabled = false, + mOutputDisabled = false, mMuffler = false, mLockUpgrade = false; + private boolean mActive = false, mWorkUpdate = false, mSteamConverter = false, mWorks = true; + private boolean oRedstone = false; + private byte mColor = 0, oColor = 0, oStrongRedstone = 0, oRedstoneData = 63, oTextureData = 0, oUpdateData = 0, + oTexturePage = 0; + private byte oLightValueClient = 0, oLightValue = -1, mLightValue = 0, mOtherUpgrades = 0, mWorkData = 0; + private ForgeDirection mFacing = ForgeDirection.DOWN, oFacing = ForgeDirection.DOWN; + private int mDisplayErrorCode = 0, oX = 0, oY = 0, oZ = 0, mTimeStatisticsIndex = 0, mLagWarningCount = 0; + private long oOutput = 0, mAcceptedAmperes = Long.MAX_VALUE; + private long mLastCheckTick = 0; + private String mOwnerName = ""; + private UUID mOwnerUuid = GT_Utility.defaultUuid; + private NBTTagCompound mRecipeStuff = new NBTTagCompound(); + private int cableUpdateDelay = 30; + + public BaseMetaTileEntity() {} + + @Override + public void writeToNBT(NBTTagCompound aNBT) { + try { + super.writeToNBT(aNBT); + } catch (Throwable e) { + GT_FML_LOGGER.error("Encountered CRITICAL ERROR while saving MetaTileEntity.", e); + } + try { + aNBT.setInteger("mID", mID); + aNBT.setLong("mStoredSteam", mStoredSteam); + aNBT.setLong("mStoredEnergy", mStoredEnergy); + writeCoverNBT(aNBT, false); + aNBT.setByte("mColor", mColor); + aNBT.setByte("mLightValue", mLightValue); + aNBT.setByte("mOtherUpgrades", mOtherUpgrades); + aNBT.setByte("mWorkData", mWorkData); + aNBT.setShort("mFacing", (short) mFacing.ordinal()); + aNBT.setString("mOwnerName", mOwnerName); + aNBT.setString("mOwnerUuid", mOwnerUuid == null ? "" : mOwnerUuid.toString()); + aNBT.setBoolean("mLockUpgrade", mLockUpgrade); + aNBT.setBoolean("mMuffler", mMuffler); + aNBT.setBoolean("mSteamConverter", mSteamConverter); + aNBT.setBoolean("mActive", mActive); + aNBT.setBoolean("mWorks", !mWorks); + aNBT.setBoolean("mInputDisabled", mInputDisabled); + aNBT.setBoolean("mOutputDisabled", mOutputDisabled); + aNBT.setTag("GT.CraftingComponents", mRecipeStuff); + } catch (Throwable e) { + GT_FML_LOGGER.error("Encountered CRITICAL ERROR while saving MetaTileEntity.", e); + } + saveMetaTileNBT(aNBT); + } + + @Override + public void readFromNBT(NBTTagCompound aNBT) { + super.readFromNBT(aNBT); + setInitialValuesAsNBT(aNBT, (short) 0); + } + + @Override + public void setInitialValuesAsNBT(NBTTagCompound aNBT, short aID) { + if (aNBT == null) { + if (aID > 0) mID = aID; + else mID = mID > 0 ? mID : 0; + if (mID != 0) createNewMetatileEntity(mID); + mSidedRedstone = (hasValidMetaTileEntity() && mMetaTileEntity.hasSidedRedstoneOutputBehavior() + ? new byte[] { 0, 0, 0, 0, 0, 0 } + : new byte[] { 15, 15, 15, 15, 15, 15 }); + } else { + if (aID <= 0) mID = (short) aNBT.getInteger("mID"); + else mID = aID; + mStoredSteam = aNBT.getLong("mStoredSteam"); + mStoredEnergy = aNBT.getLong("mStoredEnergy"); + mColor = aNBT.getByte("mColor"); + mLightValue = aNBT.getByte("mLightValue"); + mWorkData = aNBT.getByte("mWorkData"); + mFacing = oFacing = ForgeDirection.getOrientation(aNBT.getShort("mFacing")); + mOwnerName = aNBT.getString("mOwnerName"); + try { + mOwnerUuid = UUID.fromString(aNBT.getString("mOwnerUuid")); + } catch (IllegalArgumentException e) { + mOwnerUuid = null; + } + mLockUpgrade = aNBT.getBoolean("mLockUpgrade"); + mMuffler = aNBT.getBoolean("mMuffler"); + mSteamConverter = aNBT.getBoolean("mSteamConverter"); + mActive = aNBT.getBoolean("mActive"); + mWorks = !aNBT.getBoolean("mWorks"); + mInputDisabled = aNBT.getBoolean("mInputDisabled"); + mOutputDisabled = aNBT.getBoolean("mOutputDisabled"); + mOtherUpgrades = (byte) (aNBT.getByte("mOtherUpgrades") + aNBT.getByte("mBatteries") + + aNBT.getByte("mLiBatteries")); + + mRecipeStuff = aNBT.getCompoundTag("GT.CraftingComponents"); + final int nbtVersion = aNBT.getInteger("nbtVersion"); + readCoverNBT(aNBT); + loadMetaTileNBT(aNBT); + } + + if (mSidedRedstone.length != 6) + if (hasValidMetaTileEntity() && mMetaTileEntity.hasSidedRedstoneOutputBehavior()) + mSidedRedstone = new byte[] { 0, 0, 0, 0, 0, 0 }; + else mSidedRedstone = new byte[] { 15, 15, 15, 15, 15, 15 }; + + updateCoverBehavior(); + } + + /** + * Used for ticking special BaseMetaTileEntities, which need that for Energy Conversion It's called right before + * onPostTick() + */ + public void updateStatus() { + // + } + + /** + * Called when trying to charge Items + */ + public void chargeItem(ItemStack aStack) { + decreaseStoredEU( + GT_ModHandler.chargeElectricItem( + aStack, + (int) Math.min(Integer.MAX_VALUE, getStoredEU()), + (int) Math.min(Integer.MAX_VALUE, mMetaTileEntity.getOutputTier()), + false, + false), + true); + } + + /** + * Called when trying to discharge Items + */ + public void dischargeItem(ItemStack aStack) { + increaseStoredEnergyUnits( + GT_ModHandler.dischargeElectricItem( + aStack, + (int) Math.min(Integer.MAX_VALUE, getEUCapacity() - getStoredEU()), + (int) Math.min(Integer.MAX_VALUE, mMetaTileEntity.getInputTier()), + false, + false, + false), + true); + } + + protected boolean isRainPossible() { + BiomeGenBase biome = getBiome(); + // see net.minecraft.client.renderer.EntityRenderer.renderRainSnow + return biome.rainfall > 0 && (biome.canSpawnLightningBolt() || biome.getEnableSnow()); + } + + /** + * Check if this is exposed to rain + * + * @return True if exposed to rain, else false + */ + public boolean isRainExposed() { + final int precipitationHeightAtSide2 = worldObj.getPrecipitationHeight(xCoord, zCoord - 1); + final int precipitationHeightAtSide3 = worldObj.getPrecipitationHeight(xCoord, zCoord + 1); + final int precipitationHeightAtSide4 = worldObj.getPrecipitationHeight(xCoord - 1, zCoord); + final int precipitationHeightAtSide5 = worldObj.getPrecipitationHeight(xCoord + 1, zCoord); + return (getCoverIDAtSide(ForgeDirection.UP) == 0 + && worldObj.getPrecipitationHeight(xCoord, zCoord) - 2 < yCoord) + || (getCoverIDAtSide(ForgeDirection.NORTH) == 0 && precipitationHeightAtSide2 - 1 < yCoord + && precipitationHeightAtSide2 > -1) + || (getCoverIDAtSide(ForgeDirection.SOUTH) == 0 && precipitationHeightAtSide3 - 1 < yCoord + && precipitationHeightAtSide3 > -1) + || (getCoverIDAtSide(ForgeDirection.WEST) == 0 && precipitationHeightAtSide4 - 1 < yCoord + && precipitationHeightAtSide4 > -1) + || (getCoverIDAtSide(ForgeDirection.EAST) == 0 && precipitationHeightAtSide5 - 1 < yCoord + && precipitationHeightAtSide5 > -1); + } + + @Override + public void updateEntity() { + super.updateEntity(); + + if (!hasValidMetaTileEntity()) { + if (mMetaTileEntity == null) return; + mMetaTileEntity.setBaseMetaTileEntity(this); + } + + mRunningThroughTick = true; + long tTime; + if (hasTimeStatisticsStarted) { + tTime = System.nanoTime(); + } else { + tTime = 0; + } + final boolean aSideServer = isServerSide(); + final boolean aSideClient = isClientSide(); + + try { + if (hasValidMetaTileEntity()) { + if (mTickTimer++ == 0) { + oX = xCoord; + oY = yCoord; + oZ = zCoord; + if (aSideServer) { + checkDropCover(); + } else { + requestCoverDataIfNeeded(); + } + worldObj.markTileEntityChunkModified(xCoord, yCoord, zCoord, this); + mMetaTileEntity.onFirstTick(this); + if (!hasValidMetaTileEntity()) { + mRunningThroughTick = false; + return; + } + } + if (aSideClient) { + if (mColor != oColor) { + mMetaTileEntity.onColorChangeClient(oColor = mColor); + issueTextureUpdate(); + } + + if (mLightValue != oLightValueClient) { + worldObj.setLightValue(EnumSkyBlock.Block, xCoord, yCoord, zCoord, mLightValue); + worldObj.updateLightByType(EnumSkyBlock.Block, xCoord, yCoord, zCoord); + worldObj.updateLightByType(EnumSkyBlock.Block, xCoord + 1, yCoord, zCoord); + worldObj.updateLightByType(EnumSkyBlock.Block, xCoord - 1, yCoord, zCoord); + worldObj.updateLightByType(EnumSkyBlock.Block, xCoord, yCoord + 1, zCoord); + worldObj.updateLightByType(EnumSkyBlock.Block, xCoord, yCoord - 1, zCoord); + worldObj.updateLightByType(EnumSkyBlock.Block, xCoord, yCoord, zCoord + 1); + worldObj.updateLightByType(EnumSkyBlock.Block, xCoord, yCoord, zCoord - 1); + oLightValueClient = mLightValue; + issueTextureUpdate(); + } + + if (mNeedsUpdate) { + if (GT_Mod.gregtechproxy.mUseBlockUpdateHandler) { + BlockUpdateHandler.Instance.enqueueBlockUpdate(worldObj, getLocation()); + } else { + worldObj.markBlockForUpdate(xCoord, yCoord, zCoord); + } + mNeedsUpdate = false; + } + } + if (aSideServer && mTickTimer > 10) { + if (!doCoverThings()) { + mRunningThroughTick = false; + return; + } + } + if (aSideServer) { + if (++mAverageEUInputIndex >= mAverageEUInput.length) mAverageEUInputIndex = 0; + if (++mAverageEUOutputIndex >= mAverageEUOutput.length) mAverageEUOutputIndex = 0; + + mAverageEUInput[mAverageEUInputIndex] = 0; + mAverageEUOutput[mAverageEUOutputIndex] = 0; + } + + mMetaTileEntity.onPreTick(this, mTickTimer); + + if (!hasValidMetaTileEntity()) { + mRunningThroughTick = false; + return; + } + if (aSideServer) { + if (mRedstone != oRedstone || mTickTimer == 10) { + updateCoverBehavior(); + oRedstone = mRedstone; + issueBlockUpdate(); + } + if (mTickTimer == 10) joinEnet(); + + if (xCoord != oX || yCoord != oY || zCoord != oZ) { + oX = xCoord; + oY = yCoord; + oZ = zCoord; + issueClientUpdate(); + clearTileEntityBuffer(); + } + + if (mFacing != oFacing) { + oFacing = mFacing; + checkDropCover(); + issueBlockUpdate(); + } + + if (mTickTimer > 20 && mMetaTileEntity.isElectric()) { + mAcceptedAmperes = 0; + + if (getOutputVoltage() != oOutput) { + oOutput = getOutputVoltage(); + } + + if (mMetaTileEntity.isEnetOutput() || mMetaTileEntity.isEnetInput()) { + for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) { + final int ordinalSide = side.ordinal(); + boolean temp = isEnergyInputSide(side); + if (temp != mActiveEUInputs[ordinalSide]) { + mActiveEUInputs[ordinalSide] = temp; + } + temp = isEnergyOutputSide(side); + if (temp != mActiveEUOutputs[ordinalSide]) { + mActiveEUOutputs[ordinalSide] = temp; + } + } + } + + if (mMetaTileEntity.isEnetOutput() && oOutput > 0) { + final long tOutputVoltage = Math + .max(oOutput, oOutput + (1L << Math.max(0, GT_Utility.getTier(oOutput) - 1))); + final long tUsableAmperage = Math.min( + getOutputAmperage(), + (getStoredEU() - mMetaTileEntity.getMinimumStoredEU()) / tOutputVoltage); + if (tUsableAmperage > 0) { + final long tEU = tOutputVoltage + * Util.emitEnergyToNetwork(oOutput, tUsableAmperage, this); + mAverageEUOutput[mAverageEUOutputIndex] += tEU; + decreaseStoredEU(tEU, true); + } + } + if (getEUCapacity() > 0) { + if (GregTech_API.sMachineFireExplosions && getRandomNumber(1000) == 0) { + final Block tBlock = getBlockAtSide(ForgeDirection.getOrientation(getRandomNumber(6))); + if (tBlock instanceof BlockFire) doEnergyExplosion(); + } + + if (!hasValidMetaTileEntity()) { + mRunningThroughTick = false; + return; + } + + if (GregTech_API.sMachineRainExplosions) { + if (mMetaTileEntity.willExplodeInRain()) { + if (getRandomNumber(1000) == 0 && isRainPossible()) { + // Short-circuit so raincheck happens before isRainExposed, + // saves sme TPS since rain exposed check can be slow + // This logic can be compressed further by only checking for + // isRainExposed once IF we can guarantee it never thunders without + // raining, but I don't know if this is true or not. + if (worldObj.isRaining() && isRainExposed()) { + if (getRandomNumber(10) == 0) { + try { + GT_Mod.achievements.issueAchievement( + this.getWorldObj() + .getPlayerEntityByName(mOwnerName), + "badweather"); + } catch (Exception ignored) {} + GT_Log.exp.println( + "Machine at: " + this.getXCoord() + + " | " + + this.getYCoord() + + " | " + + this.getZCoord() + + " DIMID: " + + this.worldObj.provider.dimensionId + + " explosion due to rain!"); + doEnergyExplosion(); + } else { + GT_Log.exp.println( + "Machine at: " + this.getXCoord() + + " | " + + this.getYCoord() + + " | " + + this.getZCoord() + + " DIMID: " + + this.worldObj.provider.dimensionId + + " set to Fire due to rain!"); + setOnFire(); + } + } + if (!hasValidMetaTileEntity()) { + mRunningThroughTick = false; + return; + } + if (GregTech_API.sMachineThunderExplosions && worldObj.isThundering() + && getRandomNumber(3) == 0 + && isRainExposed()) { + try { + GT_Mod.achievements.issueAchievement( + this.getWorldObj() + .getPlayerEntityByName(mOwnerName), + "badweather"); + } catch (Exception ignored) {} + GT_Log.exp.println( + "Machine at: " + this.getXCoord() + + " | " + + this.getYCoord() + + " | " + + this.getZCoord() + + " DIMID: " + + this.worldObj.provider.dimensionId + + " explosion due to Thunderstorm!"); + doEnergyExplosion(); + } + } + } + } + } + } + + if (!hasValidMetaTileEntity()) { + mRunningThroughTick = false; + return; + } + } + if (aSideServer) { + if (mMetaTileEntity.dechargerSlotCount() > 0 && getStoredEU() < getEUCapacity()) { + for (int i = mMetaTileEntity.dechargerSlotStartIndex(), + k = mMetaTileEntity.dechargerSlotCount() + i; i < k; i++) { + if (mMetaTileEntity.mInventory[i] != null && getStoredEU() < getEUCapacity()) { + dischargeItem(mMetaTileEntity.mInventory[i]); + if (ic2.api.info.Info.itemEnergy.getEnergyValue(mMetaTileEntity.mInventory[i]) > 0) { + if ((getStoredEU() + + ic2.api.info.Info.itemEnergy.getEnergyValue(mMetaTileEntity.mInventory[i])) + < getEUCapacity()) { + increaseStoredEnergyUnits( + (long) ic2.api.info.Info.itemEnergy + .getEnergyValue(mMetaTileEntity.mInventory[i]), + false); + mMetaTileEntity.mInventory[i].stackSize--; + mInventoryChanged = true; + } + } + if (mMetaTileEntity.mInventory[i].stackSize <= 0) { + mMetaTileEntity.mInventory[i] = null; + mInventoryChanged = true; + } + } + } + } + } + if (aSideServer) { + if (mMetaTileEntity.rechargerSlotCount() > 0 && getStoredEU() > 0) { + for (int i = mMetaTileEntity.rechargerSlotStartIndex(), + k = mMetaTileEntity.rechargerSlotCount() + i; i < k; i++) { + if (getStoredEU() > 0 && mMetaTileEntity.mInventory[i] != null) { + chargeItem(mMetaTileEntity.mInventory[i]); + if (mMetaTileEntity.mInventory[i].stackSize <= 0) { + mMetaTileEntity.mInventory[i] = null; + mInventoryChanged = true; + } + } + } + } + } + updateStatus(); + if (!hasValidMetaTileEntity()) { + mRunningThroughTick = false; + return; + } + mMetaTileEntity.onPostTick(this, mTickTimer); + if (!hasValidMetaTileEntity()) { + mRunningThroughTick = false; + return; + } + if (aSideServer) { + if (mTickTimer > 20 && cableUpdateDelay == 0) { + generatePowerNodes(); + } + cableUpdateDelay--; + if (mTickTimer % 10 == 0) { + sendClientData(); + } + + if (mTickTimer > 10) { + byte tData = (byte) ((mFacing.ordinal() & 7) | (mActive ? 8 : 0) + | (mRedstone ? 16 : 0) + | (mLockUpgrade ? 32 : 0) + | (mWorks ? 64 : 0) + | (mMuffler ? 128 : 0)); + if (tData != oTextureData) + sendBlockEvent(GregTechTileClientEvents.CHANGE_COMMON_DATA, oTextureData = tData); + + tData = mMetaTileEntity.getUpdateData(); + if (tData != oUpdateData) + sendBlockEvent(GregTechTileClientEvents.CHANGE_CUSTOM_DATA, oUpdateData = tData); + if (mMetaTileEntity instanceof GT_MetaTileEntity_Hatch) { + tData = ((GT_MetaTileEntity_Hatch) mMetaTileEntity).getTexturePage(); + if (tData != oTexturePage) sendBlockEvent( + GregTechTileClientEvents.CHANGE_CUSTOM_DATA, + (byte) ((oTexturePage = tData) | 0x80)); // set last bit as a flag for page + } + if (mColor != oColor) sendBlockEvent(GregTechTileClientEvents.CHANGE_COLOR, oColor = mColor); + tData = (byte) (((mSidedRedstone[0] > 0) ? 1 : 0) | ((mSidedRedstone[1] > 0) ? 2 : 0) + | ((mSidedRedstone[2] > 0) ? 4 : 0) + | ((mSidedRedstone[3] > 0) ? 8 : 0) + | ((mSidedRedstone[4] > 0) ? 16 : 0) + | ((mSidedRedstone[5] > 0) ? 32 : 0)); + if (tData != oRedstoneData) + sendBlockEvent(GregTechTileClientEvents.CHANGE_REDSTONE_OUTPUT, oRedstoneData = tData); + if (mLightValue != oLightValue) { + worldObj.setLightValue(EnumSkyBlock.Block, xCoord, yCoord, zCoord, mLightValue); + worldObj.updateLightByType(EnumSkyBlock.Block, xCoord, yCoord, zCoord); + worldObj.updateLightByType(EnumSkyBlock.Block, xCoord + 1, yCoord, zCoord); + worldObj.updateLightByType(EnumSkyBlock.Block, xCoord - 1, yCoord, zCoord); + worldObj.updateLightByType(EnumSkyBlock.Block, xCoord, yCoord + 1, zCoord); + worldObj.updateLightByType(EnumSkyBlock.Block, xCoord, yCoord - 1, zCoord); + worldObj.updateLightByType(EnumSkyBlock.Block, xCoord, yCoord, zCoord + 1); + worldObj.updateLightByType(EnumSkyBlock.Block, xCoord, yCoord, zCoord - 1); + issueTextureUpdate(); + sendBlockEvent(GregTechTileClientEvents.CHANGE_LIGHT, oLightValue = mLightValue); + } + } + + if (mNeedsBlockUpdate) { + updateNeighbours(mStrongRedstone, oStrongRedstone); + oStrongRedstone = mStrongRedstone; + mNeedsBlockUpdate = false; + } + } + } + } catch (Throwable e) { + e.printStackTrace(); + e.printStackTrace(GT_Log.err); + try { + mMetaTileEntity.onTickFail(this, mTickTimer); + } catch (Throwable ex) { + ex.printStackTrace(); + ex.printStackTrace(GT_Log.err); + } + } + + if (aSideServer && hasTimeStatisticsStarted && hasValidMetaTileEntity()) { + tTime = System.nanoTime() - tTime; + mTimeStatisticsIndex = (mTimeStatisticsIndex + 1) % mTimeStatistics.length; + mTimeStatistics[mTimeStatisticsIndex] = (int) tTime; + if (tTime > 0 && tTime > (GregTech_API.MILLISECOND_THRESHOLD_UNTIL_LAG_WARNING * 1_000_000L) + && mTickTimer > 1000 + && getMetaTileEntity().doTickProfilingMessageDuringThisTick() + && mLagWarningCount++ < 10) + GT_FML_LOGGER.warn( + "WARNING: Possible Lag Source at [" + xCoord + + ", " + + yCoord + + ", " + + zCoord + + "] in Dimension " + + worldObj.provider.dimensionId + + " with " + + tTime + + "ns caused by an instance of " + + getMetaTileEntity().getClass()); + } + + mWorkUpdate = mInventoryChanged = mRunningThroughTick = false; + } + + @Override + public void getWailaBody(ItemStack itemStack, List<String> currentTip, IWailaDataAccessor accessor, + IWailaConfigHandler config) { + if (hasValidMetaTileEntity()) { + getMetaTileEntity().getWailaBody(itemStack, currentTip, accessor, config); + } + super.getWailaBody(itemStack, currentTip, accessor, config); + } + + @Override + public void getWailaNBTData(EntityPlayerMP player, TileEntity tile, NBTTagCompound tag, World world, int x, int y, + int z) { + super.getWailaNBTData(player, tile, tag, world, x, y, z); + if (hasValidMetaTileEntity()) { + getMetaTileEntity().getWailaNBTData(player, tile, tag, world, x, y, z); + } + } + + private void sendClientData() { + if (mSendClientData) { + NW.sendPacketToAllPlayersInRange( + worldObj, + new GT_Packet_TileEntity( + xCoord, + (short) yCoord, + zCoord, + mID, + getCoverInfoAtSide(ForgeDirection.DOWN).getCoverID(), + getCoverInfoAtSide(ForgeDirection.UP).getCoverID(), + getCoverInfoAtSide(ForgeDirection.NORTH).getCoverID(), + getCoverInfoAtSide(ForgeDirection.SOUTH).getCoverID(), + getCoverInfoAtSide(ForgeDirection.WEST).getCoverID(), + getCoverInfoAtSide(ForgeDirection.EAST).getCoverID(), + oTextureData = (byte) ((mFacing.ordinal() & 7) | (mActive ? 8 : 0) + | (mRedstone ? 16 : 0) + | (mLockUpgrade ? 32 : 0) + | (mWorks ? 64 : 0)), + oTexturePage = (hasValidMetaTileEntity() && mMetaTileEntity instanceof GT_MetaTileEntity_Hatch) + ? ((GT_MetaTileEntity_Hatch) mMetaTileEntity).getTexturePage() + : 0, + oUpdateData = hasValidMetaTileEntity() ? mMetaTileEntity.getUpdateData() : 0, + oRedstoneData = (byte) (((mSidedRedstone[0] > 0) ? 1 : 0) | ((mSidedRedstone[1] > 0) ? 2 : 0) + | ((mSidedRedstone[2] > 0) ? 4 : 0) + | ((mSidedRedstone[3] > 0) ? 8 : 0) + | ((mSidedRedstone[4] > 0) ? 16 : 0) + | ((mSidedRedstone[5] > 0) ? 32 : 0)), + oColor = mColor), + xCoord, + zCoord); + mSendClientData = false; + } + sendCoverDataIfNeeded(); + } + + public final void receiveMetaTileEntityData(short aID, int aCover0, int aCover1, int aCover2, int aCover3, + int aCover4, int aCover5, byte aTextureData, byte aTexturePage, byte aUpdateData, byte aRedstoneData, + byte aColorData) { + issueTextureUpdate(); + if (mID != aID && aID > 0) { + mID = aID; + createNewMetatileEntity(mID); + } + + setCoverIDAtSide(ForgeDirection.DOWN, aCover0); + setCoverIDAtSide(ForgeDirection.UP, aCover1); + setCoverIDAtSide(ForgeDirection.NORTH, aCover2); + setCoverIDAtSide(ForgeDirection.SOUTH, aCover3); + setCoverIDAtSide(ForgeDirection.WEST, aCover4); + setCoverIDAtSide(ForgeDirection.EAST, aCover5); + + receiveClientEvent(GregTechTileClientEvents.CHANGE_COMMON_DATA, aTextureData); + receiveClientEvent(GregTechTileClientEvents.CHANGE_CUSTOM_DATA, aUpdateData & 0x7F); + receiveClientEvent(GregTechTileClientEvents.CHANGE_CUSTOM_DATA, aTexturePage | 0x80); + receiveClientEvent(GregTechTileClientEvents.CHANGE_COLOR, aColorData); + receiveClientEvent(GregTechTileClientEvents.CHANGE_REDSTONE_OUTPUT, aRedstoneData); + } + + @Override + public boolean receiveClientEvent(int aEventID, int aValue) { + super.receiveClientEvent(aEventID, aValue); + + if (hasValidMetaTileEntity()) { + try { + mMetaTileEntity.receiveClientEvent((byte) aEventID, (byte) aValue); + } catch (Throwable e) { + GT_Log.err.println( + "Encountered Exception while receiving Data from the Server, the Client should've been crashed by now, but I prevented that. Please report immediately to GregTech Intergalactical!!!"); + e.printStackTrace(GT_Log.err); + } + } + + if (isClientSide()) { + issueTextureUpdate(); + switch (aEventID) { + case GregTechTileClientEvents.CHANGE_COMMON_DATA -> { + mFacing = ForgeDirection.getOrientation((byte) (aValue & 7)); + mActive = ((aValue & 8) != 0); + mRedstone = ((aValue & 16) != 0); + // mLockUpgrade = ((aValue&32) != 0); + mWorks = ((aValue & 64) != 0); + mMuffler = ((aValue & 128) != 0); + } + case GregTechTileClientEvents.CHANGE_CUSTOM_DATA -> { + if (hasValidMetaTileEntity()) { + if ((aValue & 0x80) == 0) // Is texture index + mMetaTileEntity.onValueUpdate((byte) (aValue & 0x7F)); + else if (mMetaTileEntity instanceof GT_MetaTileEntity_Hatch) // is texture page and hatch + ((GT_MetaTileEntity_Hatch) mMetaTileEntity).onTexturePageUpdate((byte) (aValue & 0x7F)); + } + } + case GregTechTileClientEvents.CHANGE_COLOR -> { + if (aValue > 16 || aValue < 0) aValue = 0; + mColor = (byte) aValue; + } + case GregTechTileClientEvents.CHANGE_REDSTONE_OUTPUT -> { + mSidedRedstone[0] = (byte) ((aValue & 1) == 1 ? 15 : 0); + mSidedRedstone[1] = (byte) ((aValue & 2) == 2 ? 15 : 0); + mSidedRedstone[2] = (byte) ((aValue & 4) == 4 ? 15 : 0); + mSidedRedstone[3] = (byte) ((aValue & 8) == 8 ? 15 : 0); + mSidedRedstone[4] = (byte) ((aValue & 16) == 16 ? 15 : 0); + mSidedRedstone[5] = (byte) ((aValue & 32) == 32 ? 15 : 0); + } + case GregTechTileClientEvents.DO_SOUND -> { + if (hasValidMetaTileEntity() && mTickTimer > 20) + mMetaTileEntity.doSound((byte) aValue, xCoord + 0.5, yCoord + 0.5, zCoord + 0.5); + } + case GregTechTileClientEvents.START_SOUND_LOOP -> { + if (hasValidMetaTileEntity() && mTickTimer > 20) + mMetaTileEntity.startSoundLoop((byte) aValue, xCoord + 0.5, yCoord + 0.5, zCoord + 0.5); + } + case GregTechTileClientEvents.STOP_SOUND_LOOP -> { + if (hasValidMetaTileEntity() && mTickTimer > 20) + mMetaTileEntity.stopSoundLoop((byte) aValue, xCoord + 0.5, yCoord + 0.5, zCoord + 0.5); + } + case GregTechTileClientEvents.CHANGE_LIGHT -> mLightValue = (byte) aValue; + } + } + return true; + } + + @Override + public ArrayList<String> getDebugInfo(EntityPlayer aPlayer, int aLogLevel) { + final ArrayList<String> tList = new ArrayList<>(); + if (aLogLevel > 2) { + tList.add( + "Meta-ID: " + EnumChatFormatting.BLUE + + mID + + EnumChatFormatting.RESET + + (canAccessData() ? EnumChatFormatting.GREEN + " valid" + EnumChatFormatting.RESET + : EnumChatFormatting.RED + " invalid" + EnumChatFormatting.RESET) + + (mMetaTileEntity == null + ? EnumChatFormatting.RED + " MetaTileEntity == null!" + EnumChatFormatting.RESET + : " ")); + } + if (aLogLevel > 1 && mMetaTileEntity != null) { + if (hasTimeStatisticsStarted) { + double tAverageTime = 0; + double tWorstTime = 0; + int amountOfZero = 0; + for (int tTime : mTimeStatistics) { + tAverageTime += tTime; + if (tTime > tWorstTime) { + tWorstTime = tTime; + } + if (tTime == 0) { + amountOfZero += 1; + } + // Uncomment this line to print out tick-by-tick times. + // tList.add("tTime " + tTime); + } + // tick time zero means it has not been updated yet + int samples = mTimeStatistics.length - amountOfZero; + if (samples > 0) { + tList.add( + "Average CPU load of ~" + GT_Utility.formatNumbers(tAverageTime / samples) + + "ns over " + + GT_Utility.formatNumbers(samples) + + " ticks with worst time of " + + GT_Utility.formatNumbers(tWorstTime) + + "ns."); + } + } else { + startTimeStatistics(); + tList.add("Just started tick time statistics."); + } + tList.add( + "Recorded " + GT_Utility.formatNumbers(mMetaTileEntity.mSoundRequests) + + " sound requests in " + + GT_Utility.formatNumbers(mTickTimer - mLastCheckTick) + + " ticks."); + mLastCheckTick = mTickTimer; + mMetaTileEntity.mSoundRequests = 0; + if (mLagWarningCount > 0) { + tList.add( + "Caused " + (mLagWarningCount >= 10 ? "more than 10" : mLagWarningCount) + + " Lag Spike Warnings (anything taking longer than " + + GregTech_API.MILLISECOND_THRESHOLD_UNTIL_LAG_WARNING + + "ms) on the Server."); + } + tList.add( + "Is" + (mMetaTileEntity.isAccessAllowed(aPlayer) ? " " + : EnumChatFormatting.RED + " not " + EnumChatFormatting.RESET) + "accessible for you"); + } + if (aLogLevel > 0) { + if (getSteamCapacity() > 0 && hasSteamEngineUpgrade()) tList.add( + GT_Utility.formatNumbers(getStoredSteam()) + " of " + + GT_Utility.formatNumbers(getSteamCapacity()) + + " Steam"); + tList.add( + "Machine is " + (mActive ? EnumChatFormatting.GREEN + "active" + EnumChatFormatting.RESET + : EnumChatFormatting.RED + "inactive" + EnumChatFormatting.RESET)); + if (!mHasEnoughEnergy) tList + .add(EnumChatFormatting.RED + "ATTENTION: This Device needs more power." + EnumChatFormatting.RESET); + } + if (joinedIc2Enet) tList.add("Joined IC2 ENet"); + return mMetaTileEntity.getSpecialDebugInfo(this, aPlayer, aLogLevel, tList); + } + + @Override + public boolean isGivingInformation() { + if (canAccessData()) return mMetaTileEntity.isGivingInformation(); + return false; + } + + @Override + public ForgeDirection getBackFacing() { + return mFacing.getOpposite(); + } + + @Override + public ForgeDirection getFrontFacing() { + return mFacing; + } + + @Override + public void setFrontFacing(ForgeDirection aFacing) { + if (isValidFacing(aFacing)) { + mFacing = aFacing; + mMetaTileEntity.onFacingChange(); + + doEnetUpdate(); + cableUpdateDelay = 10; + + if (mMetaTileEntity.shouldTriggerBlockUpdate()) { + // If we're triggering a block update this will call onMachineBlockUpdate() + GregTech_API.causeMachineUpdate(worldObj, xCoord, yCoord, zCoord); + } else { + // If we're not trigger a cascading one, call the update here. + onMachineBlockUpdate(); + } + } + } + + @Override + public int getSizeInventory() { + if (canAccessData()) return mMetaTileEntity.getSizeInventory(); + return 0; + } + + @Override + public ItemStack getStackInSlot(int aIndex) { + if (canAccessData()) return mMetaTileEntity.getStackInSlot(aIndex); + return null; + } + + @Override + public void setInventorySlotContents(int aIndex, ItemStack aStack) { + mInventoryChanged = true; + if (canAccessData()) { + markDirty(); + mMetaTileEntity.setInventorySlotContents( + aIndex, + worldObj.isRemote ? aStack : GT_OreDictUnificator.setStack(true, aStack)); + } + } + + @Override + public String getInventoryName() { + if (canAccessData()) return mMetaTileEntity.getInventoryName(); + if (GregTech_API.METATILEENTITIES[mID] != null) return GregTech_API.METATILEENTITIES[mID].getInventoryName(); + return ""; + } + + @Override + public int getInventoryStackLimit() { + if (canAccessData()) return mMetaTileEntity.getInventoryStackLimit(); + return 64; + } + + @Override + public void openInventory() { + if (canAccessData()) mMetaTileEntity.onOpenGUI(); + } + + @Override + public void closeInventory() { + if (canAccessData()) mMetaTileEntity.onCloseGUI(); + } + + @Override + public boolean isUseableByPlayer(EntityPlayer aPlayer) { + return canAccessData() && playerOwnsThis(aPlayer, false) + && mTickTimer > 1 + && getTileEntityOffset(0, 0, 0) == this + && aPlayer.getDistanceSq(xCoord + 0.5, yCoord + 0.5, zCoord + 0.5) < 64 + && mMetaTileEntity.isAccessAllowed(aPlayer); + } + + @Override + public void validate() { + super.validate(); + mTickTimer = 0; + } + + @Override + public void invalidate() { + tileEntityInvalid = false; + leaveEnet(); + if (canAccessData()) { + invalidateAE(); + mMetaTileEntity.onRemoval(); + mMetaTileEntity.setBaseMetaTileEntity(null); + } + super.invalidate(); + } + + @Override + public void onChunkUnload() { + if (canAccessData()) { + mMetaTileEntity.onUnload(); + } + + super.onChunkUnload(); + onChunkUnloadAE(); + } + + @Override + public boolean hasCustomInventoryName() { + return false; + } + + @Override + public ItemStack getStackInSlotOnClosing(int slot) { + final ItemStack stack = getStackInSlot(slot); + if (stack != null) setInventorySlotContents(slot, null); + return stack; + } + + /** + * Checks validity of meta tile and delegates to it + */ + @Override + public void onMachineBlockUpdate() { + if (canAccessData()) mMetaTileEntity.onMachineBlockUpdate(); + cableUpdateDelay = 10; + } + + /** + * Checks validity of meta tile and delegates to it + */ + @Override + public boolean isMachineBlockUpdateRecursive() { + return canAccessData() && mMetaTileEntity.isMachineBlockUpdateRecursive(); + } + + @Override + public int getProgress() { + return canAccessData() ? mMetaTileEntity.getProgresstime() : 0; + } + + @Override + public int getMaxProgress() { + return canAccessData() ? mMetaTileEntity.maxProgresstime() : 0; + } + + @Override + public boolean increaseProgress(int aProgressAmountInTicks) { + return canAccessData() && mMetaTileEntity.increaseProgress(aProgressAmountInTicks) != aProgressAmountInTicks; + } + + @Override + public boolean hasThingsToDo() { + return getMaxProgress() > 0; + } + + @Override + public void enableWorking() { + if (!mWorks) mWorkUpdate = true; + mWorks = true; + mWasShutdown = false; + } + + @Override + public void disableWorking() { + mWorks = false; + if (hasValidMetaTileEntity()) { + mMetaTileEntity.onDisableWorking(); + } + } + + @Override + public boolean isAllowedToWork() { + return mWorks; + } + + @Override + public boolean hasWorkJustBeenEnabled() { + return mWorkUpdate; + } + + @Override + public byte getWorkDataValue() { + return mWorkData; + } + + @Override + public void setWorkDataValue(byte aValue) { + mWorkData = aValue; + } + + @Override + public int getMetaTileID() { + return mID; + } + + @Override + public int setMetaTileID(short aID) { + return mID = aID; + } + + @Override + public boolean isActive() { + return mActive; + } + + @Override + public void setActive(boolean aActive) { + mActive = aActive; + if (hasValidMetaTileEntity()) { + mMetaTileEntity.onSetActive(aActive); + } + } + + @Override + public long getTimer() { + return mTickTimer; + } + + @Override + public boolean decreaseStoredEnergyUnits(long aEnergy, boolean aIgnoreTooLessEnergy) { + if (!canAccessData()) return false; + return mHasEnoughEnergy = decreaseStoredEU(aEnergy, aIgnoreTooLessEnergy) || decreaseStoredSteam(aEnergy, false) + || (aIgnoreTooLessEnergy && (decreaseStoredSteam(aEnergy, true))); + } + + @Override + public boolean increaseStoredEnergyUnits(long aEnergy, boolean aIgnoreTooMuchEnergy) { + if (!canAccessData()) return false; + if (getStoredEU() < getEUCapacity() || aIgnoreTooMuchEnergy) { + setStoredEU(mMetaTileEntity.getEUVar() + aEnergy); + return true; + } + return false; + } + + @Override + public boolean inputEnergyFrom(ForgeDirection side) { + return inputEnergyFrom(side, true); + } + + @Override + public boolean inputEnergyFrom(ForgeDirection side, boolean waitForActive) { + if (side == ForgeDirection.UNKNOWN) return true; + if (isServerSide() && waitForActive) return mActiveEUInputs[side.ordinal()] && !mReleaseEnergy; + return isEnergyInputSide(side); + } + + @Override + public boolean outputsEnergyTo(ForgeDirection side) { + return outputsEnergyTo(side, true); + } + + @Override + public boolean outputsEnergyTo(ForgeDirection side, boolean waitForActive) { + if (side == ForgeDirection.UNKNOWN) return true; + if (isServerSide() && waitForActive) return (mActiveEUOutputs[side.ordinal()]) || mReleaseEnergy; + return isEnergyOutputSide(side); + } + + @Override + public boolean isEnetOutput() { + return mMetaTileEntity != null && mMetaTileEntity.isEnetOutput(); + } + + @Override + public boolean isEnetInput() { + return mMetaTileEntity != null && mMetaTileEntity.isEnetInput(); + } + + public void generatePowerNodes() { + if (isServerSide() && (isEnetInput() || isEnetOutput())) { + final int time = MinecraftServer.getServer() + .getTickCounter(); + for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) { + if (outputsEnergyTo(side, false) || inputEnergyFrom(side, false)) { + final IGregTechTileEntity TE = getIGregTechTileEntityAtSide(side); + if (TE instanceof BaseMetaPipeEntity) { + final Node node = ((BaseMetaPipeEntity) TE).getNode(); + if (node == null) { + new GenerateNodeMapPower((BaseMetaPipeEntity) TE); + } else if (node.mCreationTime != time) { + GenerateNodeMap.clearNodeMap(node, -1); + new GenerateNodeMapPower((BaseMetaPipeEntity) TE); + } + } + } + } + } + } + + @Override + public long getOutputAmperage() { + if (canAccessData() && mMetaTileEntity.isElectric()) return mMetaTileEntity.maxAmperesOut(); + return 0; + } + + @Override + public long getOutputVoltage() { + if (canAccessData() && mMetaTileEntity.isElectric() && mMetaTileEntity.isEnetOutput()) + return mMetaTileEntity.maxEUOutput(); + return 0; + } + + @Override + public long getInputAmperage() { + if (canAccessData() && mMetaTileEntity.isElectric()) return mMetaTileEntity.maxAmperesIn(); + return 0; + } + + @Override + public long getInputVoltage() { + if (canAccessData() && mMetaTileEntity.isElectric()) return mMetaTileEntity.maxEUInput(); + return Integer.MAX_VALUE; + } + + @Override + public boolean increaseStoredSteam(long aEnergy, boolean aIgnoreTooMuchEnergy) { + if (!canAccessData()) return false; + if (mMetaTileEntity.getSteamVar() < getSteamCapacity() || aIgnoreTooMuchEnergy) { + setStoredSteam(mMetaTileEntity.getSteamVar() + aEnergy); + return true; + } + return false; + } + + @Override + public long getUniversalEnergyStored() { + return Math.max(getStoredEU(), getStoredSteam()); + } + + @Override + public long getUniversalEnergyCapacity() { + return Math.max(getEUCapacity(), getSteamCapacity()); + } + + @Override + public long getStoredEU() { + if (canAccessData()) return Math.min(mMetaTileEntity.getEUVar(), getEUCapacity()); + return 0; + } + + @Override + public long getEUCapacity() { + if (canAccessData()) return mMetaTileEntity.maxEUStore(); + return 0; + } + + @Override + public long getStoredSteam() { + if (canAccessData()) return Math.min(mMetaTileEntity.getSteamVar(), getSteamCapacity()); + return 0; + } + + @Override + public long getSteamCapacity() { + if (canAccessData()) return mMetaTileEntity.maxSteamStore(); + return 0; + } + + @Override + public ITexture[] getTexture(Block aBlock, ForgeDirection side) { + final ITexture coverTexture = getCoverTexture(side); + final ITexture[] textureUncovered = hasValidMetaTileEntity() + ? mMetaTileEntity + .getTexture(this, side, mFacing, (byte) (mColor - 1), mActive, getOutputRedstoneSignal(side) > 0) + : Textures.BlockIcons.ERROR_RENDERING; + final ITexture[] textureCovered; + if (coverTexture != null) { + textureCovered = Arrays.copyOf(textureUncovered, textureUncovered.length + 1); + textureCovered[textureUncovered.length] = coverTexture; + return textureCovered; + } else { + return textureUncovered; + } + } + + private boolean isEnergyInputSide(ForgeDirection side) { + if (side != ForgeDirection.UNKNOWN) { + if (!getCoverInfoAtSide(side).letsEnergyIn()) return false; + if (isInvalid() || mReleaseEnergy) return false; + if (canAccessData() && mMetaTileEntity.isElectric() && mMetaTileEntity.isEnetInput()) + return mMetaTileEntity.isInputFacing(side); + } + return false; + } + + private boolean isEnergyOutputSide(ForgeDirection side) { + if (side != ForgeDirection.UNKNOWN) { + if (!getCoverInfoAtSide(side).letsEnergyOut()) return false; + if (isInvalid() || mReleaseEnergy) return mReleaseEnergy; + if (canAccessData() && mMetaTileEntity.isElectric() && mMetaTileEntity.isEnetOutput()) + return mMetaTileEntity.isOutputFacing(side); + } + return false; + } + + @Override + protected boolean hasValidMetaTileEntity() { + return mMetaTileEntity != null && mMetaTileEntity.getBaseMetaTileEntity() == this; + } + + @Override + protected boolean canAccessData() { + return !isDead && hasValidMetaTileEntity(); + } + + public boolean setStoredEU(long aEnergy) { + if (!canAccessData()) return false; + if (aEnergy < 0) aEnergy = 0; + mMetaTileEntity.setEUVar(aEnergy); + return true; + } + + public boolean setStoredSteam(long aEnergy) { + if (!canAccessData()) return false; + if (aEnergy < 0) aEnergy = 0; + mMetaTileEntity.setSteamVar(aEnergy); + return true; + } + + public boolean decreaseStoredEU(long aEnergy, boolean aIgnoreTooLessEnergy) { + if (!canAccessData()) { + return false; + } + if (mMetaTileEntity.getEUVar() - aEnergy >= 0 || aIgnoreTooLessEnergy) { + setStoredEU(mMetaTileEntity.getEUVar() - aEnergy); + if (mMetaTileEntity.getEUVar() < 0) { + setStoredEU(0); + return false; + } + return true; + } + return false; + } + + public boolean decreaseStoredSteam(long aEnergy, boolean aIgnoreTooLessEnergy) { + if (!canAccessData()) return false; + if (mMetaTileEntity.getSteamVar() - aEnergy >= 0 || aIgnoreTooLessEnergy) { + setStoredSteam(mMetaTileEntity.getSteamVar() - aEnergy); + if (mMetaTileEntity.getSteamVar() < 0) { + setStoredSteam(0); + return false; + } + return true; + } + return false; + } + + public boolean playerOwnsThis(EntityPlayer aPlayer, boolean aCheckPrecicely) { + if (!canAccessData()) return false; + if (aCheckPrecicely || privateAccess() || (mOwnerName.length() == 0)) + if ((mOwnerName.length() == 0) && isServerSide()) { + setOwnerName(aPlayer.getDisplayName()); + setOwnerUuid(aPlayer.getUniqueID()); + } else return !privateAccess() || aPlayer.getDisplayName() + .equals("Player") || mOwnerName.equals("Player") || mOwnerName.equals(aPlayer.getDisplayName()); + return true; + } + + public boolean privateAccess() { + if (!canAccessData()) return mLockUpgrade; + return mLockUpgrade || mMetaTileEntity.ownerControl(); + } + + @Nullable + @Override + public ICleanroom getCleanroom() { + if (canAccessData()) { + return mMetaTileEntity.getCleanroom(); + } + return null; + } + + @Override + public void setCleanroom(ICleanroom cleanroom) { + if (canAccessData()) { + mMetaTileEntity.setCleanroom(cleanroom); + } + } + + public void doEnergyExplosion() { + if (getUniversalEnergyCapacity() > 0 && getUniversalEnergyStored() >= getUniversalEnergyCapacity() / 5) { + GT_Log.exp.println( + "Energy Explosion, injected " + getUniversalEnergyStored() + + "EU >= " + + getUniversalEnergyCapacity() / 5D + + "Capacity of the Machine!"); + + doExplosion( + oOutput * (getUniversalEnergyStored() >= getUniversalEnergyCapacity() ? 4 + : getUniversalEnergyStored() >= getUniversalEnergyCapacity() / 2 ? 2 : 1)); + GT_Mod.achievements.issueAchievement( + this.getWorldObj() + .getPlayerEntityByName(mOwnerName), + "electricproblems"); + } + } + + @Override + public void doExplosion(long aAmount) { + if (canAccessData()) { + // This is only for Electric Machines + if (GregTech_API.sMachineWireFire && mMetaTileEntity.isElectric()) { + try { + mReleaseEnergy = true; + IEnergyConnected.Util.emitEnergyToNetwork(V[5], Math.max(1, getStoredEU() / V[5]), this); + } catch (Exception ignored) {} + } + mReleaseEnergy = false; + // Normal Explosion Code + mMetaTileEntity.onExplosion(); + if (GT_Mod.gregtechproxy.mExplosionItemDrop) { + for (int i = 0; i < this.getSizeInventory(); i++) { + final ItemStack tItem = this.getStackInSlot(i); + if ((tItem != null) && (tItem.stackSize > 0) && (this.isValidSlot(i))) { + dropItems(tItem); + this.setInventorySlotContents(i, null); + } + } + } + if (mRecipeStuff != null) { + for (int i = 0; i < 9; i++) { + if (this.getRandomNumber(100) < 50) { + dropItems(GT_Utility.loadItem(mRecipeStuff, "Ingredient." + i)); + } + } + } + + GT_Pollution.addPollution((TileEntity) this, GT_Mod.gregtechproxy.mPollutionOnExplosion); + mMetaTileEntity.doExplosion(aAmount); + } + } + + public void dropItems(ItemStack tItem) { + if (tItem == null) return; + final EntityItem tItemEntity = new EntityItem( + this.worldObj, + this.xCoord + XSTR_INSTANCE.nextFloat() * 0.8F + 0.1F, + this.yCoord + XSTR_INSTANCE.nextFloat() * 0.8F + 0.1F, + this.zCoord + XSTR_INSTANCE.nextFloat() * 0.8F + 0.1F, + new ItemStack(tItem.getItem(), tItem.stackSize, tItem.getItemDamage())); + if (tItem.hasTagCompound()) { + tItemEntity.getEntityItem() + .setTagCompound( + (NBTTagCompound) tItem.getTagCompound() + .copy()); + } + tItemEntity.motionX = (XSTR_INSTANCE.nextGaussian() * 0.0500000007450581D); + tItemEntity.motionY = (XSTR_INSTANCE.nextGaussian() * 0.0500000007450581D + 0.2000000029802322D); + tItemEntity.motionZ = (XSTR_INSTANCE.nextGaussian() * 0.0500000007450581D); + tItemEntity.hurtResistantTime = 999999; + tItemEntity.lifespan = 60000; + try { + ENTITY_ITEM_HEALTH_FIELD.setInt(tItemEntity, 99999999); + } catch (Exception ignored) {} + this.worldObj.spawnEntityInWorld(tItemEntity); + tItem.stackSize = 0; + } + + @Override + public ArrayList<ItemStack> getDrops() { + final ItemStack rStack = new ItemStack(GregTech_API.sBlockMachines, 1, mID); + final NBTTagCompound tNBT = new NBTTagCompound(); + if (mRecipeStuff != null && !mRecipeStuff.hasNoTags()) tNBT.setTag("GT.CraftingComponents", mRecipeStuff); + if (mMuffler) tNBT.setBoolean("mMuffler", mMuffler); + if (mLockUpgrade) tNBT.setBoolean("mLockUpgrade", mLockUpgrade); + if (mSteamConverter) tNBT.setBoolean("mSteamConverter", mSteamConverter); + if (mColor > 0) tNBT.setByte("mColor", mColor); + if (mOtherUpgrades > 0) tNBT.setByte("mOtherUpgrades", mOtherUpgrades); + + writeCoverNBT(tNBT, true); + + if (hasValidMetaTileEntity()) mMetaTileEntity.setItemNBT(tNBT); + if (!tNBT.hasNoTags()) rStack.setTagCompound(tNBT); + + onBaseTEDestroyed(); + return new ArrayList<>(Collections.singletonList(rStack)); + } + + @Override + public boolean shouldDropItemAt(int index) { + return this.mMetaTileEntity == null || this.mMetaTileEntity.shouldDropItemAt(index); + } + + public int getUpgradeCount() { + return (mMuffler ? 1 : 0) + (mLockUpgrade ? 1 : 0) + (mSteamConverter ? 1 : 0) + mOtherUpgrades; + } + + @Override + public boolean onRightclick(EntityPlayer aPlayer, ForgeDirection side, float aX, float aY, float aZ) { + if (isClientSide()) { + // Configure Cover, sneak can also be: screwdriver, wrench, side cutter, soldering iron + if (aPlayer.isSneaking()) { + final ForgeDirection tSide = (getCoverIDAtSide(side) == 0) + ? GT_Utility.determineWrenchingSide(side, aX, aY, aZ) + : side; + return (getCoverBehaviorAtSideNew(tSide).hasCoverGUI()); + } else if (getCoverBehaviorAtSideNew(side).onCoverRightclickClient(side, this, aPlayer, aX, aY, aZ)) { + return true; + } + + if (!getCoverInfoAtSide(side).isGUIClickable()) return false; + } + + if (isServerSide()) { + if (!privateAccess() || aPlayer.getDisplayName() + .equalsIgnoreCase(getOwnerName())) { + final ItemStack tCurrentItem = aPlayer.inventory.getCurrentItem(); + if (tCurrentItem != null) { + if (getColorization() >= 0 + && GT_Utility.areStacksEqual(new ItemStack(Items.water_bucket, 1), tCurrentItem)) { + tCurrentItem.func_150996_a(Items.bucket); + setColorization((byte) (getColorization() >= 16 ? -2 : -1)); + return true; + } + if (GT_Utility.isStackInList(tCurrentItem, GregTech_API.sWrenchList)) { + if (aPlayer.isSneaking() && mMetaTileEntity instanceof GT_MetaTileEntity_BasicMachine + && ((GT_MetaTileEntity_BasicMachine) mMetaTileEntity) + .setMainFacing(GT_Utility.determineWrenchingSide(side, aX, aY, aZ))) { + GT_ModHandler.damageOrDechargeItem(tCurrentItem, 1, 1000, aPlayer); + GT_Utility.sendSoundToPlayers( + worldObj, + SoundResource.IC2_TOOLS_WRENCH, + 1.0F, + -1, + xCoord, + yCoord, + zCoord); + cableUpdateDelay = 10; + } else if (mMetaTileEntity.onWrenchRightClick( + side, + GT_Utility.determineWrenchingSide(side, aX, aY, aZ), + aPlayer, + aX, + aY, + aZ, + tCurrentItem)) { + GT_ModHandler.damageOrDechargeItem(tCurrentItem, 1, 1000, aPlayer); + GT_Utility.sendSoundToPlayers( + worldObj, + SoundResource.IC2_TOOLS_WRENCH, + 1.0F, + -1, + xCoord, + yCoord, + zCoord); + cableUpdateDelay = 10; + } + return true; + } + + if (GT_Utility.isStackInList(tCurrentItem, GregTech_API.sScrewdriverList)) { + if (GT_ModHandler.damageOrDechargeItem(tCurrentItem, 1, 200, aPlayer)) { + setCoverDataAtSide( + side, + getCoverBehaviorAtSideNew(side).onCoverScrewdriverClick( + side, + getCoverIDAtSide(side), + getComplexCoverDataAtSide(side), + this, + aPlayer, + aX, + aY, + aZ)); + mMetaTileEntity.onScrewdriverRightClick(side, aPlayer, aX, aY, aZ, tCurrentItem); + GT_Utility.sendSoundToPlayers( + worldObj, + SoundResource.IC2_TOOLS_WRENCH, + 1.0F, + -1, + xCoord, + yCoord, + zCoord); + } + return true; + } + + if (GT_Utility.isStackInList(tCurrentItem, GregTech_API.sHardHammerList)) { + if (GT_ModHandler.damageOrDechargeItem(tCurrentItem, 1, 1000, aPlayer)) { + mInputDisabled = !mInputDisabled; + if (mInputDisabled) mOutputDisabled = !mOutputDisabled; + GT_Utility.sendChatToPlayer( + aPlayer, + GT_Utility.trans("086", "Auto-Input: ") + (mInputDisabled + ? GT_Utility.trans("087", "Disabled") + : GT_Utility.trans("088", "Enabled") + GT_Utility.trans("089", " Auto-Output: ") + + (mOutputDisabled ? GT_Utility.trans("087", "Disabled") + : GT_Utility.trans("088", "Enabled")))); + GT_Utility.sendSoundToPlayers( + worldObj, + SoundResource.RANDOM_ANVIL_USE, + 1.0F, + -1, + xCoord, + yCoord, + zCoord); + } + return true; + } + + if (GT_Utility.isStackInList(tCurrentItem, GregTech_API.sSoftHammerList)) { + if (GT_ModHandler.damageOrDechargeItem(tCurrentItem, 1, 1000, aPlayer)) { + if (mWorks) disableWorking(); + else enableWorking(); + { + String tChat = GT_Utility.trans("090", "Machine Processing: ") + + (isAllowedToWork() ? GT_Utility.trans("088", "Enabled") + : GT_Utility.trans("087", "Disabled")); + if (getMetaTileEntity() != null && getMetaTileEntity().hasAlternativeModeText()) + tChat = getMetaTileEntity().getAlternativeModeText(); + GT_Utility.sendChatToPlayer(aPlayer, tChat); + } + GT_Utility.sendSoundToPlayers( + worldObj, + SoundResource.IC2_TOOLS_RUBBER_TRAMPOLINE, + 1.0F, + -1, + xCoord, + yCoord, + zCoord); + } + return true; + } + + if (GT_Utility.isStackInList(tCurrentItem, GregTech_API.sSolderingToolList)) { + final ForgeDirection tSide = GT_Utility.determineWrenchingSide(side, aX, aY, aZ); + if (mMetaTileEntity.onSolderingToolRightClick(side, tSide, aPlayer, aX, aY, aZ, tCurrentItem)) { + // logic handled internally + GT_Utility.sendSoundToPlayers( + worldObj, + SoundResource.IC2_TOOLS_BATTERY_USE, + 1.0F, + -1, + xCoord, + yCoord, + zCoord); + } else if (GT_ModHandler.useSolderingIron(tCurrentItem, aPlayer)) { + mStrongRedstone ^= tSide.flag; + GT_Utility.sendChatToPlayer( + aPlayer, + GT_Utility.trans("091", "Redstone Output at Side ") + tSide + + GT_Utility.trans("092", " set to: ") + + ((mStrongRedstone & tSide.flag) != 0 ? GT_Utility.trans("093", "Strong") + : GT_Utility.trans("094", "Weak"))); + GT_Utility.sendSoundToPlayers( + worldObj, + SoundResource.IC2_TOOLS_BATTERY_USE, + 3.0F, + -1, + xCoord, + yCoord, + zCoord); + issueBlockUpdate(); + } + doEnetUpdate(); + cableUpdateDelay = 10; + return true; + } + + if (GT_Utility.isStackInList(tCurrentItem, GregTech_API.sWireCutterList)) { + final ForgeDirection tSide = GT_Utility.determineWrenchingSide(side, aX, aY, aZ); + if (mMetaTileEntity.onWireCutterRightClick(side, tSide, aPlayer, aX, aY, aZ, tCurrentItem)) { + // logic handled internally + GT_Utility.sendSoundToPlayers( + worldObj, + SoundResource.IC2_TOOLS_WRENCH, + 1.0F, + -1, + xCoord, + yCoord, + zCoord); + } + doEnetUpdate(); + cableUpdateDelay = 10; + return true; + } + + ForgeDirection coverSide = side; + if (getCoverIDAtSide(side) == 0) coverSide = GT_Utility.determineWrenchingSide(side, aX, aY, aZ); + + if (getCoverIDAtSide(coverSide) == 0) { + if (GT_Utility.isStackInList(tCurrentItem, GregTech_API.sCovers.keySet())) { + final GT_CoverBehaviorBase<?> coverBehavior = GregTech_API + .getCoverBehaviorNew(tCurrentItem); + if (coverBehavior.isCoverPlaceable(coverSide, tCurrentItem, this) + && mMetaTileEntity.allowCoverOnSide(coverSide, new GT_ItemStack(tCurrentItem))) { + + setCoverItemAtSide(coverSide, tCurrentItem); + coverBehavior.onPlayerAttach(aPlayer, tCurrentItem, this, coverSide); + + if (!aPlayer.capabilities.isCreativeMode) tCurrentItem.stackSize--; + GT_Utility.sendSoundToPlayers( + worldObj, + SoundResource.IC2_TOOLS_WRENCH, + 1.0F, + -1, + xCoord, + yCoord, + zCoord); + sendClientData(); + } + return true; + } + } else { + if (GT_Utility.isStackInList(tCurrentItem, GregTech_API.sCrowbarList)) { + if (GT_ModHandler.damageOrDechargeItem(tCurrentItem, 1, 1000, aPlayer)) { + GT_Utility.sendSoundToPlayers( + worldObj, + SoundResource.RANDOM_BREAK, + 1.0F, + -1, + xCoord, + yCoord, + zCoord); + dropCover(coverSide, side, false); + } + return true; + } else if (GT_Utility.isStackInList(tCurrentItem, GregTech_API.sJackhammerList)) { + // Configuration of delicate electronics calls for a tool with precision and subtlety. + if (GT_ModHandler.damageOrDechargeItem(tCurrentItem, 1, 1000, aPlayer)) { + final CoverInfo info = getCoverInfoAtSide(coverSide); + if (info != CoverInfo.EMPTY_INFO) { + final GT_CoverBehaviorBase<?> behavior = info.getCoverBehavior(); + if (behavior.allowsTickRateAddition()) { + info.onCoverJackhammer(aPlayer); + GT_Utility.sendSoundToPlayers( + worldObj, + SoundResource.IC2_TOOLS_DRILL_DRILL_SOFT, + 1.0F, + 1, + xCoord, + yCoord, + zCoord); + + } else { + GT_Utility.sendChatToPlayer( + aPlayer, + StatCollector.translateToLocal("gt.cover.info.chat.tick_rate_not_allowed")); + } + return true; + } + } + } + } + // End item != null + } else if (aPlayer.isSneaking()) { // Sneak click, no tool -> open cover config if possible. + side = (getCoverIDAtSide(side) == 0) ? GT_Utility.determineWrenchingSide(side, aX, aY, aZ) : side; + return getCoverIDAtSide(side) > 0 && getCoverBehaviorAtSideNew(side).onCoverShiftRightClick( + side, + getCoverIDAtSide(side), + getComplexCoverDataAtSide(side), + this, + aPlayer); + } + + if (getCoverBehaviorAtSideNew(side).onCoverRightClick( + side, + getCoverIDAtSide(side), + getComplexCoverDataAtSide(side), + this, + aPlayer, + aX, + aY, + aZ)) return true; + + if (!getCoverInfoAtSide(side).isGUIClickable()) return false; + + if (isUpgradable() && tCurrentItem != null) { + if (ItemList.Upgrade_Muffler.isStackEqual(aPlayer.inventory.getCurrentItem())) { + if (addMufflerUpgrade()) { + GT_Utility.sendSoundToPlayers( + worldObj, + SoundResource.RANDOM_CLICK, + 1.0F, + -1, + xCoord, + yCoord, + zCoord); + if (!aPlayer.capabilities.isCreativeMode) aPlayer.inventory.getCurrentItem().stackSize--; + } + return true; + } + if (ItemList.Upgrade_Lock.isStackEqual(aPlayer.inventory.getCurrentItem())) { + if (isUpgradable() && !mLockUpgrade) { + mLockUpgrade = true; + setOwnerName(aPlayer.getDisplayName()); + setOwnerUuid(aPlayer.getUniqueID()); + GT_Utility.sendSoundToPlayers( + worldObj, + SoundResource.RANDOM_CLICK, + 1.0F, + -1, + xCoord, + yCoord, + zCoord); + if (!aPlayer.capabilities.isCreativeMode) aPlayer.inventory.getCurrentItem().stackSize--; + } + return true; + } + } + } + } + + try { + if (!aPlayer.isSneaking() && hasValidMetaTileEntity()) + return mMetaTileEntity.onRightclick(this, aPlayer, side, aX, aY, aZ); + } catch (Throwable e) { + GT_Log.err.println( + "Encountered Exception while rightclicking TileEntity, the Game should've crashed now, but I prevented that. Please report immediately to GregTech Intergalactical!!!"); + e.printStackTrace(GT_Log.err); + e.printStackTrace(); + } + + return false; + } + + @Override + public void onLeftclick(EntityPlayer aPlayer) { + try { + if (aPlayer != null && hasValidMetaTileEntity()) mMetaTileEntity.onLeftclick(this, aPlayer); + } catch (Throwable e) { + GT_Log.err.println( + "Encountered Exception while leftclicking TileEntity, the Game should've crashed now, but I prevented that. Please report immediately to GregTech Intergalactical!!!"); + e.printStackTrace(GT_Log.err); + } + } + + @Override + public boolean isDigitalChest() { + if (canAccessData()) return mMetaTileEntity.isDigitalChest(); + return false; + } + + @Override + public ItemStack[] getStoredItemData() { + if (canAccessData()) return mMetaTileEntity.getStoredItemData(); + return null; + } + + @Override + public void setItemCount(int aCount) { + if (canAccessData()) mMetaTileEntity.setItemCount(aCount); + } + + @Override + public int getMaxItemCount() { + if (canAccessData()) return mMetaTileEntity.getMaxItemCount(); + return 0; + } + + /** + * Can put aStack into Slot + */ + @Override + public boolean isItemValidForSlot(int aIndex, ItemStack aStack) { + return canAccessData() && mMetaTileEntity.isItemValidForSlot(aIndex, aStack); + } + + /** + * returns all valid Inventory Slots, no matter which Side (Unless it's covered). The Side Stuff is done in the + * following two Functions. + */ + @Override + public int[] getAccessibleSlotsFromSide(int ordinalSide) { + final CoverInfo coverInfo = getCoverInfoAtSide(ForgeDirection.getOrientation(ordinalSide)); + if (canAccessData() && (coverInfo.letsItemsOut(-1) || coverInfo.letsItemsIn(-1))) + return mMetaTileEntity.getAccessibleSlotsFromSide(ordinalSide); + return GT_Values.emptyIntArray; + } + + /** + * Can put aStack into Slot at Side + */ + @Override + public boolean canInsertItem(int slotIndex, ItemStack stack, int ordinalSide) { + return canAccessData() && (mRunningThroughTick || !mInputDisabled) + && getCoverInfoAtSide(ForgeDirection.getOrientation(ordinalSide)).letsItemsIn(slotIndex) + && mMetaTileEntity.canInsertItem(slotIndex, stack, ordinalSide); + } + + /** + * Can pull stack out of Slot from Side + */ + @Override + public boolean canExtractItem(int slotIndex, ItemStack stack, int ordinalSide) { + final ForgeDirection side = ForgeDirection.getOrientation(ordinalSide); + return canAccessData() && (mRunningThroughTick || !mOutputDisabled) + && getCoverBehaviorAtSideNew(side) + .letsItemsOut(side, getCoverIDAtSide(side), getComplexCoverDataAtSide(side), slotIndex, this) + && mMetaTileEntity.canExtractItem(slotIndex, stack, ordinalSide); + } + + @Override + public boolean isUpgradable() { + return canAccessData() && getUpgradeCount() < 8; + } + + @Override + public byte getGeneralRS(ForgeDirection side) { + if (mMetaTileEntity == null) return 0; + return mMetaTileEntity.allowGeneralRedstoneOutput() ? mSidedRedstone[side.ordinal()] : 0; + } + + @Override + public boolean isSteamEngineUpgradable() { + return isUpgradable() && !hasSteamEngineUpgrade() && getSteamCapacity() > 0; + } + + @Override + public boolean addSteamEngineUpgrade() { + if (isSteamEngineUpgradable()) { + issueBlockUpdate(); + mSteamConverter = true; + return true; + } + return false; + } + + @Override + public boolean hasSteamEngineUpgrade() { + if (canAccessData() && mMetaTileEntity.isSteampowered()) return true; + return mSteamConverter; + } + + @Override + public boolean hasMufflerUpgrade() { + return mMuffler; + } + + @Override + public boolean isMufflerUpgradable() { + return isUpgradable() && !hasMufflerUpgrade(); + } + + @Override + public boolean addMufflerUpgrade() { + if (isMufflerUpgradable()) return mMuffler = true; + return false; + } + + @Override + public void markInventoryBeenModified() { + mInventoryChanged = true; + } + + @Override + public int getErrorDisplayID() { + return mDisplayErrorCode; + } + + @Override + public void setErrorDisplayID(int aErrorID) { + mDisplayErrorCode = aErrorID; + } + + @Override + public IMetaTileEntity getMetaTileEntity() { + return hasValidMetaTileEntity() ? mMetaTileEntity : null; + } + + @Override + public void setMetaTileEntity(IMetaTileEntity aMetaTileEntity) { + if (aMetaTileEntity instanceof MetaTileEntity || aMetaTileEntity == null) + mMetaTileEntity = (MetaTileEntity) aMetaTileEntity; + else { + GT_FML_LOGGER.error( + "Unknown meta tile entity set! Class {}, inventory name {}.", + aMetaTileEntity.getClass(), + aMetaTileEntity.getInventoryName()); + } + } + + public byte getLightValue() { + return mLightValue; + } + + @Override + public void setLightValue(byte aLightValue) { + mLightValue = (byte) (aLightValue & 15); + } + + @Override + public long getAverageElectricInput() { + long rEU = 0; + for (int i = 0; i < mAverageEUInput.length; ++i) if (i != mAverageEUInputIndex) rEU += mAverageEUInput[i]; + return rEU / (mAverageEUInput.length - 1); + } + + @Override + public long getAverageElectricOutput() { + long rEU = 0; + for (int i = 0; i < mAverageEUOutput.length; ++i) if (i != mAverageEUOutputIndex) rEU += mAverageEUOutput[i]; + return rEU / (mAverageEUOutput.length - 1); + } + + @Override + protected void updateOutputRedstoneSignal(ForgeDirection side) { + if (mMetaTileEntity.hasSidedRedstoneOutputBehavior()) { + setOutputRedstoneSignal(side, (byte) 0); + } else { + setOutputRedstoneSignal(side, (byte) 15); + } + } + + @Override + public String getOwnerName() { + if (GT_Utility.isStringInvalid(mOwnerName)) return "Player"; + return mOwnerName; + } + + @Override + public String setOwnerName(String aName) { + if (GT_Utility.isStringInvalid(aName)) return mOwnerName = "Player"; + return mOwnerName = aName; + } + + @Override + public UUID getOwnerUuid() { + return mOwnerUuid; + } + + @Override + public void setOwnerUuid(UUID uuid) { + mOwnerUuid = uuid; + } + + @Override + public byte getComparatorValue(ForgeDirection side) { + return canAccessData() ? mMetaTileEntity.getComparatorValue(side) : 0; + } + + @Override + public ItemStack decrStackSize(int aIndex, int aAmount) { + if (canAccessData()) { + mInventoryChanged = true; + return mMetaTileEntity.decrStackSize(aIndex, aAmount); + } + return null; + } + + @Override + public long injectEnergyUnits(ForgeDirection side, long aVoltage, long aAmperage) { + if (!canAccessData() || !mMetaTileEntity.isElectric() + || !inputEnergyFrom(side) + || aAmperage <= 0 + || aVoltage <= 0 + || getStoredEU() >= getEUCapacity() + || mMetaTileEntity.maxAmperesIn() <= mAcceptedAmperes) return 0; + if (aVoltage > getInputVoltage()) { + GT_Log.exp + .println("Energy Explosion, injected " + aVoltage + "EU/t in a " + getInputVoltage() + "EU/t Machine!"); + doExplosion(aVoltage); + return 0; + } + if (increaseStoredEnergyUnits( + aVoltage * (aAmperage = Math.min( + aAmperage, + Math.min( + mMetaTileEntity.maxAmperesIn() - mAcceptedAmperes, + 1 + ((getEUCapacity() - getStoredEU()) / aVoltage)))), + true)) { + mAverageEUInput[mAverageEUInputIndex] += aVoltage * aAmperage; + mAcceptedAmperes += aAmperage; + return aAmperage; + } + return 0; + } + + @Override + public boolean drainEnergyUnits(ForgeDirection side, long aVoltage, long aAmperage) { + if (!canAccessData() || !mMetaTileEntity.isElectric() + || !outputsEnergyTo(side) + || getStoredEU() - (aVoltage * aAmperage) < mMetaTileEntity.getMinimumStoredEU()) return false; + if (decreaseStoredEU(aVoltage * aAmperage, false)) { + mAverageEUOutput[mAverageEUOutputIndex] += aVoltage * aAmperage; + return true; + } + return false; + } + + @Override + public boolean acceptsRotationalEnergy(ForgeDirection side) { + if (!canAccessData() || getCoverIDAtSide(side) != 0) return false; + return mMetaTileEntity.acceptsRotationalEnergy(side); + } + + @Override + public boolean injectRotationalEnergy(ForgeDirection side, long aSpeed, long aEnergy) { + if (!canAccessData() || getCoverIDAtSide(side) != 0) return false; + return mMetaTileEntity.injectRotationalEnergy(side, aSpeed, aEnergy); + } + + @Override + public int fill(ForgeDirection side, FluidStack aFluid, boolean doFill) { + if (mTickTimer > 5 && canAccessData() + && (mRunningThroughTick || !mInputDisabled) + && (side == ForgeDirection.UNKNOWN || (mMetaTileEntity.isLiquidInput(side) + && getCoverInfoAtSide(side).letsFluidIn(aFluid == null ? null : aFluid.getFluid())))) + return mMetaTileEntity.fill(side, aFluid, doFill); + return 0; + } + + @Override + public FluidStack drain(ForgeDirection side, int maxDrain, boolean doDrain) { + if (mTickTimer > 5 && canAccessData() + && (mRunningThroughTick || !mOutputDisabled) + && (side == ForgeDirection.UNKNOWN + || (mMetaTileEntity.isLiquidOutput(side) && getCoverInfoAtSide(side).letsFluidOut( + mMetaTileEntity.getFluid() == null ? null + : mMetaTileEntity.getFluid() + .getFluid())))) + return mMetaTileEntity.drain(side, maxDrain, doDrain); + return null; + } + + @Override + public FluidStack drain(ForgeDirection side, FluidStack aFluid, boolean doDrain) { + if (mTickTimer > 5 && canAccessData() + && (mRunningThroughTick || !mOutputDisabled) + && (side == ForgeDirection.UNKNOWN || (mMetaTileEntity.isLiquidOutput(side) + && getCoverInfoAtSide(side).letsFluidOut(aFluid == null ? null : aFluid.getFluid())))) + return mMetaTileEntity.drain(side, aFluid, doDrain); + return null; + } + + @Override + public boolean canFill(ForgeDirection side, Fluid aFluid) { + if (mTickTimer > 5 && canAccessData() + && (mRunningThroughTick || !mInputDisabled) + && (side == ForgeDirection.UNKNOWN + || (mMetaTileEntity.isLiquidInput(side) && getCoverInfoAtSide(side).letsFluidIn(aFluid)))) + return mMetaTileEntity.canFill(side, aFluid); + return false; + } + + @Override + public boolean canDrain(ForgeDirection side, Fluid aFluid) { + if (mTickTimer > 5 && canAccessData() + && (mRunningThroughTick || !mOutputDisabled) + && (side == ForgeDirection.UNKNOWN + || (mMetaTileEntity.isLiquidOutput(side) && getCoverInfoAtSide(side).letsFluidOut(aFluid)))) + return mMetaTileEntity.canDrain(side, aFluid); + return false; + } + + @Override + public FluidTankInfo[] getTankInfo(ForgeDirection side) { + if (canAccessData() && (side == ForgeDirection.UNKNOWN + || (mMetaTileEntity.isLiquidInput(side) && getCoverInfoAtSide(side).letsFluidIn(null)) + || (mMetaTileEntity.isLiquidOutput(side) && getCoverInfoAtSide(side).letsFluidOut(null)))) + return mMetaTileEntity.getTankInfo(side); + return new FluidTankInfo[] {}; + } + + public double getOutputEnergyUnitsPerTick() { + return oOutput; + } + + public boolean isTeleporterCompatible(ForgeDirection side) { + return canAccessData() && mMetaTileEntity.isTeleporterCompatible(); + } + + public double demandedEnergyUnits() { + if (mReleaseEnergy || !canAccessData() || !mMetaTileEntity.isEnetInput()) return 0; + return getEUCapacity() - getStoredEU(); + } + + public double injectEnergyUnits(ForgeDirection aDirection, double aAmount) { + return injectEnergyUnits(aDirection, (int) aAmount, 1) > 0 ? 0 : aAmount; + } + + public boolean acceptsEnergyFrom(TileEntity aEmitter, ForgeDirection aDirection) { + return inputEnergyFrom(aDirection); + } + + public boolean emitsEnergyTo(TileEntity aReceiver, ForgeDirection aDirection) { + return outputsEnergyTo(aDirection); + } + + public double getOfferedEnergy() { + return (canAccessData() && getStoredEU() - mMetaTileEntity.getMinimumStoredEU() >= oOutput) + ? Math.max(0, oOutput) + : 0; + } + + public void drawEnergy(double amount) { + mAverageEUOutput[mAverageEUOutputIndex] += amount; + decreaseStoredEU((int) amount, true); + } + + public int injectEnergy(ForgeDirection aForgeDirection, int aAmount) { + return injectEnergyUnits(aForgeDirection, aAmount, 1) > 0 ? 0 : aAmount; + } + + public int addEnergy(int aEnergy) { + if (!canAccessData()) return 0; + if (aEnergy > 0) increaseStoredEnergyUnits(aEnergy, true); + else decreaseStoredEU(-aEnergy, true); + return (int) Math.min(Integer.MAX_VALUE, mMetaTileEntity.getEUVar()); + } + + public boolean isAddedToEnergyNet() { + return false; + } + + public int demandsEnergy() { + if (mReleaseEnergy || !canAccessData() || !mMetaTileEntity.isEnetInput()) return 0; + return getCapacity() - getStored(); + } + + public int getCapacity() { + return (int) Math.min(Integer.MAX_VALUE, getEUCapacity()); + } + + public int getStored() { + return (int) Math.min(Integer.MAX_VALUE, Math.min(getStoredEU(), getCapacity())); + } + + public void setStored(int aEU) { + if (canAccessData()) setStoredEU(aEU); + } + + public int getMaxSafeInput() { + return (int) Math.min(Integer.MAX_VALUE, getInputVoltage()); + } + + public int getMaxEnergyOutput() { + if (mReleaseEnergy) return Integer.MAX_VALUE; + return getOutput(); + } + + public int getOutput() { + return (int) Math.min(Integer.MAX_VALUE, oOutput); + } + + public int injectEnergy(Direction aDirection, int aAmount) { + return injectEnergyUnits(aDirection.toForgeDirection(), aAmount, 1) > 0 ? 0 : aAmount; + } + + public boolean isTeleporterCompatible(Direction ignoredDirection) { + return canAccessData() && mMetaTileEntity.isTeleporterCompatible(); + } + + public boolean acceptsEnergyFrom(TileEntity ignoredTileEntity, Direction aDirection) { + return inputEnergyFrom(aDirection.toForgeDirection()); + } + + public boolean emitsEnergyTo(TileEntity ignoredTileEntity, Direction aDirection) { + return outputsEnergyTo(aDirection.toForgeDirection()); + } + + @Override + public boolean addStackToSlot(int slotIndex, ItemStack stack) { + if (GT_Utility.isStackInvalid(stack)) return true; + if (slotIndex < 0 || slotIndex >= getSizeInventory()) return false; + final ItemStack toStack = getStackInSlot(slotIndex); + if (GT_Utility.isStackInvalid(toStack)) { + setInventorySlotContents(slotIndex, stack); + return true; + } + final ItemStack fromStack = GT_OreDictUnificator.get(stack); + if (GT_Utility.areStacksEqual(toStack, fromStack) && toStack.stackSize + fromStack.stackSize + <= Math.min(fromStack.getMaxStackSize(), getInventoryStackLimit())) { + toStack.stackSize += fromStack.stackSize; + markDirty(); + return true; + } + return false; + } + + @Override + public boolean addStackToSlot(int aIndex, ItemStack aStack, int aAmount) { + return addStackToSlot(aIndex, GT_Utility.copyAmount(aAmount, aStack)); + } + + @Override + public byte getColorization() { + return (byte) (mColor - 1); + } + + @Override + public byte setColorization(byte aColor) { + if (aColor > 15 || aColor < -1) aColor = -1; + mColor = (byte) (aColor + 1); + if (canAccessData()) mMetaTileEntity.onColorChangeServer(aColor); + return mColor; + } + + @Override + public float getBlastResistance(ForgeDirection side) { + return canAccessData() ? Math.max(0, getMetaTileEntity().getExplosionResistance(side)) : 10.0F; + } + + @Override + public void onBlockDestroyed() { + if (canAccessData()) getMetaTileEntity().onBlockDestroyed(); + } + + @Override + public boolean isUniversalEnergyStored(long aEnergyAmount) { + if (getUniversalEnergyStored() >= aEnergyAmount) return true; + mHasEnoughEnergy = false; + return false; + } + + @Override + public String[] getInfoData() { + { + if (canAccessData()) return getMetaTileEntity().getInfoData(); + return new String[] {}; + } + } + + @Override + public int getLightOpacity() { + return mMetaTileEntity == null ? getLightValue() > 0 ? 0 : 255 : mMetaTileEntity.getLightOpacity(); + } + + @Override + public void addCollisionBoxesToList(World aWorld, int aX, int aY, int aZ, AxisAlignedBB inputAABB, + List<AxisAlignedBB> outputAABB, Entity collider) { + mMetaTileEntity.addCollisionBoxesToList(aWorld, aX, aY, aZ, inputAABB, outputAABB, collider); + } + + @Override + public AxisAlignedBB getCollisionBoundingBoxFromPool(World aWorld, int aX, int aY, int aZ) { + return mMetaTileEntity.getCollisionBoundingBoxFromPool(aWorld, aX, aY, aZ); + } + + @Override + public void onEntityCollidedWithBlock(World aWorld, int aX, int aY, int aZ, Entity collider) { + mMetaTileEntity.onEntityCollidedWithBlock(aWorld, aX, aY, aZ, collider); + } + + /** + * Shifts the machine Inventory index according to the change in Input/Output Slots. This is NOT done automatically. + * If you want to change slot count for a machine this method needs to be adapted. Currently this method only works + * for GT_MetaTileEntity_BasicMachine + * + * @param slotIndex The original Inventory index + * @param nbtVersion The GregTech version in which the original Inventory Index was saved. + * @return The corrected Inventory index + */ + @Override + protected int migrateInventoryIndex(int slotIndex, int nbtVersion) { + final int oldInputSize; + final int newInputSize; + final int oldOutputSize; + final int newOutputSize; + final int chemistryUpdateVersion = GT_Mod.calculateTotalGTVersion(509, 31); + final int configCircuitAdditionVersion = GT_Mod.calculateTotalGTVersion(509, 40); + final int wireAdditionVersion = GT_Mod.calculateTotalGTVersion(509, 41); + final int disassemblerRemoveVersion = GT_Mod.calculateTotalGTVersion(509, 42, 44); + if (nbtVersion < 1000000) nbtVersion *= 1000; + // 4 is old GT_MetaTileEntity_BasicMachine.OTHER_SLOT_COUNT + if (nbtVersion < configCircuitAdditionVersion && getMetaTileEntity() instanceof GT_MetaTileEntity_BasicMachine + && slotIndex >= 4) slotIndex += 1; + if (mID >= 211 && mID <= 218) { // Assembler + if (nbtVersion < chemistryUpdateVersion) { + oldInputSize = 2; + oldOutputSize = 1; + } else { + return slotIndex; + } + newInputSize = 6; + newOutputSize = 1; + + } else if (mID >= 421 && mID <= 428) { // Chemical Reactor + if (nbtVersion < chemistryUpdateVersion) { + oldInputSize = 2; + oldOutputSize = 1; + } else { + return slotIndex; + } + newInputSize = 2; + newOutputSize = 2; + + } else if (mID >= 531 && mID <= 538) { // Distillery + if (nbtVersion < chemistryUpdateVersion) { + oldInputSize = 1; + oldOutputSize = 0; + } else { + return slotIndex; + } + newInputSize = 1; + newOutputSize = 1; + } else if (mID >= 581 && mID <= 588) { // Mixer + if (nbtVersion < chemistryUpdateVersion) { + oldInputSize = 4; + oldOutputSize = 1; + } else { + return slotIndex; + } + newInputSize = 6; + newOutputSize = 1; + + } else if (mID >= 351 && mID <= 355 || mID >= 11050 && mID <= 11056) { // wire mill + if (nbtVersion < wireAdditionVersion) { + oldInputSize = 1; + oldOutputSize = 1; + } else { + return slotIndex; + } + newInputSize = 2; + newOutputSize = 1; + + } else if (mID >= 654 && mID <= 655 || mID >= 11070 && mID <= 11076) { // arc furnace + if (nbtVersion < disassemblerRemoveVersion) { + oldInputSize = 1; + oldOutputSize = 4; + } else { + return slotIndex; + } + newInputSize = 1; + newOutputSize = 9; + + } else { + return slotIndex; + } + + int indexShift = 0; + if (slotIndex >= GT_MetaTileEntity_BasicMachine.OTHER_SLOT_COUNT + oldInputSize) { + indexShift += newInputSize - oldInputSize; + } + if (slotIndex >= GT_MetaTileEntity_BasicMachine.OTHER_SLOT_COUNT + oldInputSize + oldOutputSize) { + indexShift += newOutputSize - oldOutputSize; + } + return slotIndex + indexShift; + } + + @Override + public IGridNode getGridNode(ForgeDirection forgeDirection) { + final AENetworkProxy gp = getProxy(); + return gp != null ? gp.getNode() : null; + } + + @Override + public AECableType getCableConnectionType(ForgeDirection forgeDirection) { + return mMetaTileEntity == null ? AECableType.NONE : mMetaTileEntity.getCableConnectionType(forgeDirection); + } + + @Override + public void securityBreak() {} + + @Override + public IGridNode getActionableNode() { + final AENetworkProxy gp = getProxy(); + return gp != null ? gp.getNode() : null; + } + + @Override + public AENetworkProxy getProxy() { + return mMetaTileEntity == null ? null : mMetaTileEntity.getProxy(); + } + + @Override + public DimensionalCoord getLocation() { + return new DimensionalCoord(this); + } + + @Override + public void gridChanged() { + if (mMetaTileEntity != null) mMetaTileEntity.gridChanged(); + } + + @TileEvent(TileEventType.WORLD_NBT_READ) + public void readFromNBT_AENetwork(final NBTTagCompound data) { + final AENetworkProxy gp = getProxy(); + if (gp != null) getProxy().readFromNBT(data); + } + + @TileEvent(TileEventType.WORLD_NBT_WRITE) + public void writeToNBT_AENetwork(final NBTTagCompound data) { + final AENetworkProxy gp = getProxy(); + if (gp != null) gp.writeToNBT(data); + } + + void onChunkUnloadAE() { + final AENetworkProxy gp = getProxy(); + if (gp != null) gp.onChunkUnload(); + } + + void invalidateAE() { + final AENetworkProxy gp = getProxy(); + if (gp != null) gp.invalidate(); + } + + @Override + public boolean wasShutdown() { + return mWasShutdown; + } + + @Override + public void setShutdownStatus(boolean newStatus) { + mWasShutdown = newStatus; + } + + @Override + public void setShutDownReason(@NotNull ShutDownReason reason) { + lastShutDownReason = reason; + } + + @Override + public @NotNull ShutDownReason getLastShutDownReason() { + return lastShutDownReason; + } + + @Override + public IAlignment getAlignment() { + return getMetaTileEntity() instanceof IAlignmentProvider + ? ((IAlignmentProvider) getMetaTileEntity()).getAlignment() + : getMetaTileEntity() instanceof IAlignment ? (IAlignment) getMetaTileEntity() : null; + } + + @Nullable + @Override + public IConstructable getConstructable() { + return getMetaTileEntity() instanceof IConstructable ? (IConstructable) getMetaTileEntity() : null; + } + + @Override + public int[] getTimeStatistics() { + return mTimeStatistics; + } + + @Override + public void startTimeStatistics() { + hasTimeStatisticsStarted = true; + } + + @Nullable + @Override + public List<ItemStack> getItemsForHoloGlasses() { + if (canAccessData()) { + return mMetaTileEntity.getItemsForHoloGlasses(); + } + return null; + } + + @Override + public String getCustomName() { + return getMetaTileEntity() instanceof ICustomNameObject customNameObject ? customNameObject.getCustomName() + : null; + } + + @Override + public boolean hasCustomName() { + return getMetaTileEntity() instanceof ICustomNameObject customNameObject && customNameObject.hasCustomName(); + } + + @Override + public void setCustomName(String name) { + if (getMetaTileEntity() instanceof ICustomNameObject customNameObject) customNameObject.setCustomName(name); + } +} diff --git a/src/main/java/gregtech/api/metatileentity/BaseTileEntity.java b/src/main/java/gregtech/api/metatileentity/BaseTileEntity.java new file mode 100644 index 0000000000..d8b0086f0f --- /dev/null +++ b/src/main/java/gregtech/api/metatileentity/BaseTileEntity.java @@ -0,0 +1,979 @@ +package gregtech.api.metatileentity; + +import static gregtech.api.enums.GT_Values.COMPASS_DIRECTIONS; +import static gregtech.api.enums.GT_Values.GT; +import static gregtech.api.enums.GT_Values.NW; +import static gregtech.api.enums.GT_Values.SIDE_DOWN; +import static gregtech.api.enums.GT_Values.SIDE_UP; + +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Supplier; + +import javax.annotation.Nullable; + +import net.minecraft.block.Block; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.Blocks; +import net.minecraft.inventory.IInventory; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.ChunkCoordinates; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.MathHelper; +import net.minecraft.util.StatCollector; +import net.minecraft.world.World; +import net.minecraft.world.biome.BiomeGenBase; +import net.minecraft.world.chunk.Chunk; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.common.util.ForgeDirection; +import net.minecraftforge.fluids.IFluidHandler; + +import com.gtnewhorizons.modularui.api.drawable.IDrawable; +import com.gtnewhorizons.modularui.api.drawable.ItemDrawable; +import com.gtnewhorizons.modularui.api.forge.ItemStackHandler; +import com.gtnewhorizons.modularui.api.math.Alignment; +import com.gtnewhorizons.modularui.api.screen.ITileWithModularUI; +import com.gtnewhorizons.modularui.api.screen.ModularUIContext; +import com.gtnewhorizons.modularui.api.screen.ModularWindow; +import com.gtnewhorizons.modularui.api.screen.UIBuildContext; +import com.gtnewhorizons.modularui.common.internal.network.NetworkUtils; +import com.gtnewhorizons.modularui.common.internal.wrapper.BaseSlot; +import com.gtnewhorizons.modularui.common.widget.DrawableWidget; +import com.gtnewhorizons.modularui.common.widget.MultiChildWidget; +import com.gtnewhorizons.modularui.common.widget.SlotGroup; +import com.gtnewhorizons.modularui.common.widget.SlotWidget; +import com.gtnewhorizons.modularui.common.widget.TextWidget; + +import cpw.mods.fml.common.FMLCommonHandler; +import cpw.mods.fml.relauncher.Side; +import gregtech.GT_Mod; +import gregtech.api.enums.Dyes; +import gregtech.api.enums.GT_Values; +import gregtech.api.gui.modularui.GT_UITextures; +import gregtech.api.gui.modularui.GUITextureSet; +import gregtech.api.interfaces.IConfigurationCircuitSupport; +import gregtech.api.interfaces.modularui.IAddGregtechLogo; +import gregtech.api.interfaces.modularui.IAddInventorySlots; +import gregtech.api.interfaces.modularui.IGetGUITextureSet; +import gregtech.api.interfaces.tileentity.IGTEnet; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.interfaces.tileentity.IHasWorldObjectAndCoords; +import gregtech.api.interfaces.tileentity.IIC2Enet; +import gregtech.api.net.GT_Packet_Block_Event; +import gregtech.api.net.GT_Packet_SetConfigurationCircuit; +import gregtech.api.util.GT_TooltipDataCache; +import gregtech.api.util.GT_Util; +import gregtech.api.util.GT_Utility; +import gregtech.common.gui.modularui.uifactory.SelectItemUIFactory; +import ic2.api.energy.event.EnergyTileLoadEvent; +import ic2.api.energy.event.EnergyTileUnloadEvent; + +/** + * The Functions my old TileEntities and my BaseMetaTileEntities have in common. + * <p/> + * Basically everything a TileEntity should have. + */ +public abstract class BaseTileEntity extends TileEntity implements IHasWorldObjectAndCoords, IIC2Enet, IGTEnet, + ITileWithModularUI, IAddGregtechLogo, IGetGUITextureSet, IAddInventorySlots { + + protected boolean mInventoryChanged = false; + + /** + * Buffers adjacent TileEntities for faster access + * <p/> + * "this" means that there is no TileEntity, while "null" means that it doesn't know if there is even a TileEntity + * and still needs to check that if needed. + */ + private final TileEntity[] mBufferedTileEntities = new TileEntity[6]; + /** + * If this TileEntity checks for the Chunk to be loaded before returning World based values. The AdvPump hacks this + * to false to ensure everything runs properly even when far Chunks are not actively loaded. But anything else + * should not cause worfin' Chunks, uhh I mean orphan Chunks. + */ + public boolean ignoreUnloadedChunks = true; + /** + * This Variable checks if this TileEntity is dead, because Minecraft is too stupid to have proper TileEntity + * unloading. + */ + public boolean isDead = false; + + private final ChunkCoordinates mReturnedCoordinates = new ChunkCoordinates(); + + public static ForgeDirection getSideForPlayerPlacing(Entity aPlayer, ForgeDirection defaultFacing, + boolean[] aAllowedFacings) { + if (aPlayer != null) { + if (aPlayer.rotationPitch >= 65 && aAllowedFacings[SIDE_UP]) return ForgeDirection.UP; + if (aPlayer.rotationPitch <= -65 && aAllowedFacings[SIDE_DOWN]) return ForgeDirection.DOWN; + final byte rFacing = COMPASS_DIRECTIONS[MathHelper.floor_double(0.5D + 4.0F * aPlayer.rotationYaw / 360.0F) + & 0x3]; + if (aAllowedFacings[rFacing]) return ForgeDirection.getOrientation(rFacing); + } + for (final ForgeDirection dir : ForgeDirection.VALID_DIRECTIONS) if (aAllowedFacings[dir.ordinal()]) return dir; + return defaultFacing; + } + + private void clearNullMarkersFromTileEntityBuffer() { + for (int i = 0; i < mBufferedTileEntities.length; i++) + if (mBufferedTileEntities[i] == this) mBufferedTileEntities[i] = null; + } + + /** + * Called automatically when the Coordinates of this TileEntity have been changed + */ + protected final void clearTileEntityBuffer() { + Arrays.fill(mBufferedTileEntities, null); + } + + @Override + public final World getWorld() { + return worldObj; + } + + @Override + public final int getXCoord() { + return xCoord; + } + + @Override + public final short getYCoord() { + return (short) yCoord; + } + + @Override + public final int getZCoord() { + return zCoord; + } + + @Override + public ChunkCoordinates getCoords() { + mReturnedCoordinates.posX = xCoord; + mReturnedCoordinates.posY = yCoord; + mReturnedCoordinates.posZ = zCoord; + return mReturnedCoordinates; + } + + @Override + public final int getOffsetX(ForgeDirection side, int aMultiplier) { + return xCoord + side.offsetX * aMultiplier; + } + + @Override + public final short getOffsetY(ForgeDirection side, int aMultiplier) { + return (short) (yCoord + side.offsetY * aMultiplier); + } + + @Override + public final int getOffsetZ(ForgeDirection side, int aMultiplier) { + return zCoord + side.offsetZ * aMultiplier; + } + + @Override + public final boolean isServerSide() { + if (worldObj == null) { + return FMLCommonHandler.instance() + .getEffectiveSide() == Side.SERVER; + } + return !worldObj.isRemote; + } + + @Override + public final boolean isClientSide() { + if (worldObj == null) { + return FMLCommonHandler.instance() + .getEffectiveSide() == Side.CLIENT; + } + return worldObj.isRemote; + } + + @Override + @Deprecated + public final boolean openGUI(EntityPlayer aPlayer) { + return openGUI(aPlayer, 0); + } + + @Override + @Deprecated + public final boolean openGUI(EntityPlayer aPlayer, int aID) { + if (aPlayer == null) return false; + aPlayer.openGui(GT, aID, worldObj, xCoord, yCoord, zCoord); + return true; + } + + @Override + public boolean isInvalidTileEntity() { + return isInvalid(); + } + + @Override + public int getRandomNumber(int aRange) { + return ThreadLocalRandom.current() + .nextInt(aRange); + } + + @Override + public final BiomeGenBase getBiome(int aX, int aZ) { + return worldObj.getBiomeGenForCoords(aX, aZ); + } + + @Override + public final BiomeGenBase getBiome() { + return getBiome(xCoord, zCoord); + } + + @Override + public final Block getBlockOffset(int aX, int aY, int aZ) { + return getBlock(xCoord + aX, yCoord + aY, zCoord + aZ); + } + + @Override + public final Block getBlockAtSide(ForgeDirection side) { + return getBlockAtSideAndDistance(side, 1); + } + + @Override + public final Block getBlockAtSideAndDistance(ForgeDirection side, int aDistance) { + return getBlock(getOffsetX(side, aDistance), getOffsetY(side, aDistance), getOffsetZ(side, aDistance)); + } + + @Override + public final byte getMetaIDOffset(int aX, int aY, int aZ) { + return getMetaID(xCoord + aX, yCoord + aY, zCoord + aZ); + } + + @Override + public final byte getMetaIDAtSide(ForgeDirection side) { + return getMetaIDAtSideAndDistance(side, 1); + } + + @Override + public final byte getMetaIDAtSideAndDistance(ForgeDirection side, int aDistance) { + return getMetaID(getOffsetX(side, aDistance), getOffsetY(side, aDistance), getOffsetZ(side, aDistance)); + } + + @Override + public final byte getLightLevelOffset(int aX, int aY, int aZ) { + return getLightLevel(xCoord + aX, yCoord + aY, zCoord + aZ); + } + + @Override + public final byte getLightLevelAtSide(ForgeDirection side) { + return getLightLevelAtSideAndDistance(side, 1); + } + + @Override + public final byte getLightLevelAtSideAndDistance(ForgeDirection side, int aDistance) { + return getLightLevel(getOffsetX(side, aDistance), getOffsetY(side, aDistance), getOffsetZ(side, aDistance)); + } + + @Override + public final boolean getOpacityOffset(int aX, int aY, int aZ) { + return getOpacity(xCoord + aX, yCoord + aY, zCoord + aZ); + } + + @Override + public final boolean getOpacityAtSide(ForgeDirection side) { + return getOpacityAtSideAndDistance(side, 1); + } + + @Override + public final boolean getOpacityAtSideAndDistance(ForgeDirection side, int aDistance) { + return getOpacity(getOffsetX(side, aDistance), getOffsetY(side, aDistance), getOffsetZ(side, aDistance)); + } + + @Override + public final boolean getSkyOffset(int aX, int aY, int aZ) { + return getSky(xCoord + aX, yCoord + aY, zCoord + aZ); + } + + @Override + public final boolean getSkyAtSide(ForgeDirection side) { + return getSkyAtSideAndDistance(side, 1); + } + + @Override + public final boolean getSkyAtSideAndDistance(ForgeDirection side, int aDistance) { + return getSky(getOffsetX(side, aDistance), getOffsetY(side, aDistance), getOffsetZ(side, aDistance)); + } + + @Override + public final boolean getAirOffset(int aX, int aY, int aZ) { + return getAir(xCoord + aX, yCoord + aY, zCoord + aZ); + } + + @Override + public final boolean getAirAtSide(ForgeDirection side) { + return getAirAtSideAndDistance(side, 1); + } + + @Override + public final boolean getAirAtSideAndDistance(ForgeDirection side, int aDistance) { + return getAir(getOffsetX(side, aDistance), getOffsetY(side, aDistance), getOffsetZ(side, aDistance)); + } + + @Override + public final TileEntity getTileEntityOffset(int aX, int aY, int aZ) { + return getTileEntity(xCoord + aX, yCoord + aY, zCoord + aZ); + } + + @Override + public final TileEntity getTileEntityAtSideAndDistance(ForgeDirection side, int aDistance) { + if (aDistance == 1) return getTileEntityAtSide(side); + return getTileEntity(getOffsetX(side, aDistance), getOffsetY(side, aDistance), getOffsetZ(side, aDistance)); + } + + @Override + public final IInventory getIInventory(int aX, int aY, int aZ) { + final TileEntity tTileEntity = getTileEntity(aX, aY, aZ); + if (tTileEntity instanceof IInventory) return (IInventory) tTileEntity; + return null; + } + + @Override + public final IInventory getIInventoryOffset(int aX, int aY, int aZ) { + final TileEntity tTileEntity = getTileEntityOffset(aX, aY, aZ); + if (tTileEntity instanceof IInventory) return (IInventory) tTileEntity; + return null; + } + + @Override + public final IInventory getIInventoryAtSide(ForgeDirection side) { + final TileEntity tTileEntity = getTileEntityAtSide(side); + if (tTileEntity instanceof IInventory) return (IInventory) tTileEntity; + return null; + } + + @Override + public final IInventory getIInventoryAtSideAndDistance(ForgeDirection side, int aDistance) { + final TileEntity tTileEntity = getTileEntityAtSideAndDistance(side, aDistance); + if (tTileEntity instanceof IInventory) return (IInventory) tTileEntity; + return null; + } + + @Override + public final IFluidHandler getITankContainer(int aX, int aY, int aZ) { + final TileEntity tTileEntity = getTileEntity(aX, aY, aZ); + if (tTileEntity instanceof IFluidHandler) return (IFluidHandler) tTileEntity; + return null; + } + + @Override + public final IFluidHandler getITankContainerOffset(int aX, int aY, int aZ) { + final TileEntity tTileEntity = getTileEntityOffset(aX, aY, aZ); + if (tTileEntity instanceof IFluidHandler) return (IFluidHandler) tTileEntity; + return null; + } + + @Override + public final IFluidHandler getITankContainerAtSide(ForgeDirection side) { + final TileEntity tTileEntity = getTileEntityAtSide(side); + if (tTileEntity instanceof IFluidHandler) return (IFluidHandler) tTileEntity; + return null; + } + + @Override + public final IFluidHandler getITankContainerAtSideAndDistance(ForgeDirection side, int aDistance) { + final TileEntity tTileEntity = getTileEntityAtSideAndDistance(side, aDistance); + if (tTileEntity instanceof IFluidHandler) return (IFluidHandler) tTileEntity; + return null; + } + + @Override + public final IGregTechTileEntity getIGregTechTileEntity(int aX, int aY, int aZ) { + final TileEntity tTileEntity = getTileEntity(aX, aY, aZ); + if (tTileEntity instanceof IGregTechTileEntity) return (IGregTechTileEntity) tTileEntity; + return null; + } + + @Override + public final IGregTechTileEntity getIGregTechTileEntityOffset(int aX, int aY, int aZ) { + final TileEntity tTileEntity = getTileEntityOffset(aX, aY, aZ); + if (tTileEntity instanceof IGregTechTileEntity) return (IGregTechTileEntity) tTileEntity; + return null; + } + + @Override + public final IGregTechTileEntity getIGregTechTileEntityAtSide(ForgeDirection side) { + final TileEntity tTileEntity = getTileEntityAtSide(side); + if (tTileEntity instanceof IGregTechTileEntity) return (IGregTechTileEntity) tTileEntity; + return null; + } + + @Override + public final IGregTechTileEntity getIGregTechTileEntityAtSideAndDistance(ForgeDirection side, int aDistance) { + final TileEntity tTileEntity = getTileEntityAtSideAndDistance(side, aDistance); + if (tTileEntity instanceof IGregTechTileEntity) return (IGregTechTileEntity) tTileEntity; + return null; + } + + @Override + public final Block getBlock(int aX, int aY, int aZ) { + if (ignoreUnloadedChunks && crossedChunkBorder(aX, aZ) && !worldObj.blockExists(aX, aY, aZ)) return Blocks.air; + return worldObj.getBlock(aX, aY, aZ); + } + + public Block getBlock(ChunkCoordinates aCoords) { + if (worldObj == null) return Blocks.air; + if (ignoreUnloadedChunks && crossedChunkBorder(aCoords) + && !worldObj.blockExists(aCoords.posX, aCoords.posY, aCoords.posZ)) return Blocks.air; + return worldObj.getBlock(aCoords.posX, aCoords.posY, aCoords.posZ); + } + + @Override + public final byte getMetaID(int aX, int aY, int aZ) { + if (ignoreUnloadedChunks && crossedChunkBorder(aX, aZ) && !worldObj.blockExists(aX, aY, aZ)) return 0; + return (byte) worldObj.getBlockMetadata(aX, aY, aZ); + } + + @Override + public final byte getLightLevel(int aX, int aY, int aZ) { + if (ignoreUnloadedChunks && crossedChunkBorder(aX, aZ) && !worldObj.blockExists(aX, aY, aZ)) return 0; + return (byte) (worldObj.getLightBrightness(aX, aY, aZ) * 15); + } + + @Override + public final boolean getSky(int aX, int aY, int aZ) { + if (ignoreUnloadedChunks && crossedChunkBorder(aX, aZ) && !worldObj.blockExists(aX, aY, aZ)) return true; + return worldObj.canBlockSeeTheSky(aX, aY, aZ); + } + + @Override + public final boolean getOpacity(int aX, int aY, int aZ) { + if (ignoreUnloadedChunks && crossedChunkBorder(aX, aZ) && !worldObj.blockExists(aX, aY, aZ)) return false; + return GT_Utility.isOpaqueBlock(worldObj, aX, aY, aZ); + } + + @Override + public final boolean getAir(int aX, int aY, int aZ) { + if (ignoreUnloadedChunks && crossedChunkBorder(aX, aZ) && !worldObj.blockExists(aX, aY, aZ)) return true; + return GT_Utility.isBlockAir(worldObj, aX, aY, aZ); + } + + @Override + public TileEntity getTileEntity(int aX, int aY, int aZ) { + if (ignoreUnloadedChunks && crossedChunkBorder(aX, aZ) && !worldObj.blockExists(aX, aY, aZ)) return null; + return worldObj.getTileEntity(aX, aY, aZ); + } + + @Override + public final TileEntity getTileEntityAtSide(ForgeDirection side) { + final int ordinalSide = side.ordinal(); + if (side == ForgeDirection.UNKNOWN || mBufferedTileEntities[ordinalSide] == this) return null; + final int tX = getOffsetX(side, 1); + final int tY = getOffsetY(side, 1); + final int tZ = getOffsetZ(side, 1); + if (crossedChunkBorder(tX, tZ)) { + mBufferedTileEntities[ordinalSide] = null; + if (ignoreUnloadedChunks && !worldObj.blockExists(tX, tY, tZ)) return null; + } + if (mBufferedTileEntities[ordinalSide] == null) { + mBufferedTileEntities[ordinalSide] = worldObj.getTileEntity(tX, tY, tZ); + if (mBufferedTileEntities[ordinalSide] == null) { + mBufferedTileEntities[ordinalSide] = this; + return null; + } + return mBufferedTileEntities[ordinalSide]; + } + if (mBufferedTileEntities[ordinalSide].isInvalid()) { + mBufferedTileEntities[ordinalSide] = null; + return getTileEntityAtSide(side); + } + if (mBufferedTileEntities[ordinalSide].xCoord == tX && mBufferedTileEntities[ordinalSide].yCoord == tY + && mBufferedTileEntities[ordinalSide].zCoord == tZ) { + return mBufferedTileEntities[ordinalSide]; + } + return null; + } + + @Override + public void writeToNBT(NBTTagCompound aNBT) { + super.writeToNBT(aNBT); + } + + @Override + public boolean isDead() { + return isDead || isInvalidTileEntity(); + } + + @Override + public void validate() { + clearNullMarkersFromTileEntityBuffer(); + super.validate(); + } + + @Override + public void invalidate() { + leaveEnet(); + clearNullMarkersFromTileEntityBuffer(); + super.invalidate(); + } + + @Override + public void onChunkUnload() { + leaveEnet(); + clearNullMarkersFromTileEntityBuffer(); + super.onChunkUnload(); + isDead = true; + } + + @Override + public void updateEntity() { + // Well if the TileEntity gets ticked it is alive. + isDead = false; + } + + public final void onAdjacentBlockChange(int ignoredAX, int ignoredAY, int ignoredAZ) { + clearNullMarkersFromTileEntityBuffer(); + } + + public void updateNeighbours(int mStrongRedstone, int oStrongRedstone) { + final Block thisBlock = getBlockOffset(0, 0, 0); + for (final ForgeDirection dir : ForgeDirection.VALID_DIRECTIONS) { + final int x1 = xCoord + dir.offsetX, y1 = yCoord + dir.offsetY, z1 = zCoord + dir.offsetZ; + + if (worldObj.blockExists(x1, y1, z1)) { + worldObj.notifyBlockOfNeighborChange(x1, y1, z1, thisBlock); + + // update if it was / is strong powered. + if (((((mStrongRedstone | oStrongRedstone) >>> dir.ordinal()) & 1) != 0) + && getBlock(x1, y1, z1).isNormalCube()) { + final int skipUpdateSide = dir.getOpposite() + .ordinal(); // Don't update this block. Still updates + // diagonal blocks twice if conditions + // meet. + + for (final ForgeDirection dir2 : ForgeDirection.VALID_DIRECTIONS) { + final int x2 = x1 + dir2.offsetX, y2 = y1 + dir2.offsetY, z2 = z1 + dir2.offsetZ; + if (dir2.ordinal() != skipUpdateSide && worldObj.blockExists(x2, y2, z2)) + worldObj.notifyBlockOfNeighborChange(x2, y2, z2, thisBlock); + } + } + } + } + } + + @Override + public final void sendBlockEvent(byte aID, byte aValue) { + NW.sendPacketToAllPlayersInRange( + worldObj, + new GT_Packet_Block_Event(xCoord, (short) yCoord, zCoord, aID, aValue), + xCoord, + zCoord); + } + + protected boolean crossedChunkBorder(int aX, int aZ) { + return aX >> 4 != xCoord >> 4 || aZ >> 4 != zCoord >> 4; + } + + public final boolean crossedChunkBorder(ChunkCoordinates aCoords) { + return aCoords.posX >> 4 != xCoord >> 4 || aCoords.posZ >> 4 != zCoord >> 4; + } + + public final void setOnFire() { + GT_Utility.setCoordsOnFire(worldObj, xCoord, yCoord, zCoord, false); + } + + public final void setToFire() { + worldObj.setBlock(xCoord, yCoord, zCoord, Blocks.fire); + } + + @Override + public void markDirty() { + // Avoid sending neighbor updates, just mark the chunk as dirty to make sure it gets saved + final Chunk chunk = worldObj.getChunkFromBlockCoords(xCoord, zCoord); + if (chunk != null) { + chunk.setChunkModified(); + } + } + + /** + * Gets items to be displayed for HoloInventory mod. + * + * @return null if default implementation should be used, i.e. {@link IInventory#getStackInSlot}. + * Otherwise, a list of items to be displayed. Null element may be contained. + */ + @Nullable + public List<ItemStack> getItemsForHoloGlasses() { + return null; + } + + @Deprecated + public String trans(String aKey, String aEnglish) { + return GT_Utility.trans(aKey, aEnglish); + } + + protected Supplier<Boolean> getValidator() { + return () -> !this.isDead(); + } + + public boolean useModularUI() { + return false; + } + + /* + * IC2 Energy Compat + */ + protected TileIC2EnergySink ic2EnergySink = null; + protected boolean joinedIc2Enet = false; + + protected void createIc2Sink() { + if (ic2EnergySink == null && isServerSide() && shouldJoinIc2Enet()) { + ic2EnergySink = new TileIC2EnergySink((IGregTechTileEntity) this); + } + } + + @Override + public void doEnetUpdate() { + leaveEnet(); + joinEnet(); + } + + protected void joinEnet() { + if (joinedIc2Enet || !shouldJoinIc2Enet()) return; + + if (ic2EnergySink == null) createIc2Sink(); + + if (ic2EnergySink != null) { + MinecraftForge.EVENT_BUS.post(new EnergyTileLoadEvent(ic2EnergySink)); + joinedIc2Enet = true; + } + } + + protected void leaveEnet() { + if (joinedIc2Enet && ic2EnergySink != null && isServerSide()) { + joinedIc2Enet = false; + MinecraftForge.EVENT_BUS.post(new EnergyTileUnloadEvent(ic2EnergySink)); + } + } + + // === GUI stuff === + + public ItemStackHandler getInventoryHandler() { + return null; + } + + protected GT_TooltipDataCache mTooltipCache = new GT_TooltipDataCache(); + + // Tooltip localization keys + public static final String BATTERY_SLOT_TOOLTIP = "GT5U.machines.battery_slot.tooltip", + BATTERY_SLOT_TOOLTIP_ALT = "GT5U.machines.battery_slot.tooltip.alternative", + UNUSED_SLOT_TOOLTIP = "GT5U.machines.unused_slot.tooltip", + SPECIAL_SLOT_TOOLTIP = "GT5U.machines.special_slot.tooltip", + STALLED_STUTTERING_TOOLTIP = "GT5U.machines.stalled_stuttering.tooltip", + STALLED_VENT_TOOLTIP = "GT5U.machines.stalled_vent.tooltip", + FLUID_TRANSFER_TOOLTIP = "GT5U.machines.fluid_transfer.tooltip", + ITEM_TRANSFER_TOOLTIP = "GT5U.machines.item_transfer.tooltip", POWER_SOURCE_KEY = "GT5U.machines.powersource.", + BUTTON_FORBIDDEN_TOOLTIP = "GT5U.gui.button.forbidden", + NEI_TRANSFER_STEAM_TOOLTIP = "GT5U.machines.nei_transfer.steam.tooltip", + NEI_TRANSFER_VOLTAGE_TOOLTIP = "GT5U.machines.nei_transfer.voltage.tooltip"; + + public static final int TOOLTIP_DELAY = 5; + + /** + * Override this to add {@link com.gtnewhorizons.modularui.api.widget.Widget}s for your UI. + */ + public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) {} + + public void bindPlayerInventoryUI(ModularWindow.Builder builder, UIBuildContext buildContext) { + builder.bindPlayerInventory(buildContext.getPlayer(), 7, getGUITextureSet().getItemSlot()); + } + + public String getLocalName() { + return "Unknown"; + } + + protected void addTitleToUI(ModularWindow.Builder builder) { + addTitleToUI(builder, getLocalName()); + } + + protected void addTitleToUI(ModularWindow.Builder builder, String title) { + if (GT_Mod.gregtechproxy.mTitleTabStyle == 2) { + addTitleItemIconStyle(builder, title); + } else { + addTitleTextStyle(builder, title); + } + } + + protected void addTitleTextStyle(ModularWindow.Builder builder, String title) { + final int TAB_PADDING = 3; + final int TITLE_PADDING = 2; + int titleWidth = 0, titleHeight = 0; + if (NetworkUtils.isClient()) { + final FontRenderer fontRenderer = Minecraft.getMinecraft().fontRenderer; + final List<String> titleLines = fontRenderer + .listFormattedStringToWidth(title, getGUIWidth() - (TAB_PADDING + TITLE_PADDING) * 2); + titleWidth = titleLines.size() > 1 ? getGUIWidth() - (TAB_PADDING + TITLE_PADDING) * 2 + : fontRenderer.getStringWidth(title); + // noinspection PointlessArithmeticExpression + titleHeight = titleLines.size() * fontRenderer.FONT_HEIGHT + (titleLines.size() - 1) * 1; + } + + final DrawableWidget tab = new DrawableWidget(); + final TextWidget text = new TextWidget(title).setDefaultColor(getTitleColor()) + .setTextAlignment(Alignment.CenterLeft) + .setMaxWidth(titleWidth); + if (GT_Mod.gregtechproxy.mTitleTabStyle == 1) { + tab.setDrawable(getGUITextureSet().getTitleTabAngular()) + .setPos(0, -(titleHeight + TAB_PADDING) + 1) + .setSize(getGUIWidth(), titleHeight + TAB_PADDING * 2); + text.setPos(TAB_PADDING + TITLE_PADDING, -titleHeight + TAB_PADDING); + } else { + tab.setDrawable(getGUITextureSet().getTitleTabDark()) + .setPos(0, -(titleHeight + TAB_PADDING * 2) + 1) + .setSize(titleWidth + (TAB_PADDING + TITLE_PADDING) * 2, titleHeight + TAB_PADDING * 2 - 1); + text.setPos(TAB_PADDING + TITLE_PADDING, -titleHeight); + } + builder.widget(tab) + .widget(text); + } + + protected void addTitleItemIconStyle(ModularWindow.Builder builder, String title) { + builder.widget( + new MultiChildWidget().addChild( + new DrawableWidget().setDrawable(getGUITextureSet().getTitleTabNormal()) + .setPos(0, 0) + .setSize(24, 24)) + .addChild( + new ItemDrawable(getStackForm(1)).asWidget() + .setPos(4, 4)) + .addTooltip(title) + .setTooltipShowUpDelay(TOOLTIP_DELAY) + .setPos(0, -24 + 3)); + } + + @Override + public GUITextureSet getGUITextureSet() { + return GUITextureSet.DEFAULT; + } + + protected int getTitleColor() { + return COLOR_TITLE.get(); + } + + @Override + public void addGregTechLogo(ModularWindow.Builder builder) { + builder.widget( + new DrawableWidget().setDrawable(getGUITextureSet().getGregTechLogo()) + .setSize(17, 17) + .setPos(152, 63)); + } + + protected int getGUIWidth() { + return 176; + } + + protected int getGUIHeight() { + return 166; + } + + protected boolean doesBindPlayerInventory() { + return true; + } + + @Override + public void add1by1Slot(ModularWindow.Builder builder, IDrawable... background) { + final ItemStackHandler inventoryHandler = getInventoryHandler(); + if (inventoryHandler == null) return; + + if (background.length == 0) { + background = new IDrawable[] { getGUITextureSet().getItemSlot() }; + } + builder.widget( + SlotGroup.ofItemHandler(inventoryHandler, 1) + .startFromSlot(0) + .endAtSlot(0) + .background(background) + .build() + .setPos(79, 34)); + } + + @Override + public void add2by2Slots(ModularWindow.Builder builder, IDrawable... background) { + final ItemStackHandler inventoryHandler = getInventoryHandler(); + if (inventoryHandler == null) return; + + if (background.length == 0) { + background = new IDrawable[] { getGUITextureSet().getItemSlot() }; + } + builder.widget( + SlotGroup.ofItemHandler(inventoryHandler, 2) + .startFromSlot(0) + .endAtSlot(3) + .background(background) + .build() + .setPos(70, 25)); + } + + @Override + public void add3by3Slots(ModularWindow.Builder builder, IDrawable... background) { + final ItemStackHandler inventoryHandler = getInventoryHandler(); + if (inventoryHandler == null) return; + + if (background.length == 0) { + background = new IDrawable[] { getGUITextureSet().getItemSlot() }; + } + builder.widget( + SlotGroup.ofItemHandler(inventoryHandler, 3) + .startFromSlot(0) + .endAtSlot(8) + .background(background) + .build() + .setPos(61, 16)); + } + + @Override + public void add4by4Slots(ModularWindow.Builder builder, IDrawable... background) { + final ItemStackHandler inventoryHandler = getInventoryHandler(); + if (inventoryHandler == null) return; + + if (background.length == 0) { + background = new IDrawable[] { getGUITextureSet().getItemSlot() }; + } + builder.widget( + SlotGroup.ofItemHandler(inventoryHandler, 4) + .startFromSlot(0) + .endAtSlot(15) + .background(background) + .build() + .setPos(52, 7)); + } + + public void addCoverTabs(ModularWindow.Builder builder, UIBuildContext buildContext) { + /* Do nothing */ + } + + public IConfigurationCircuitSupport getConfigurationCircuitSupport() { + if (!(this instanceof IConfigurationCircuitSupport)) return null; + return (IConfigurationCircuitSupport) this; + } + + protected void addConfigurationCircuitSlot(ModularWindow.Builder builder) { + final ItemStackHandler inventoryHandler = getInventoryHandler(); + if (inventoryHandler == null) return; + + if (!(this instanceof IInventory inv)) return; + + final IConfigurationCircuitSupport ccs = getConfigurationCircuitSupport(); + if (ccs == null) return; + + final AtomicBoolean dialogOpened = new AtomicBoolean(false); + builder.widget(new SlotWidget(new BaseSlot(inventoryHandler, ccs.getCircuitSlot(), true)) { + + @Override + protected void phantomClick(ClickData clickData, ItemStack cursorStack) { + final ItemStack newCircuit; + if (clickData.shift) { + if (clickData.mouseButton == 0) { + if (NetworkUtils.isClient() && !dialogOpened.get()) { + openSelectCircuitDialog(getContext(), dialogOpened); + } + return; + } else { + newCircuit = null; + } + } else { + final List<ItemStack> tCircuits = ccs.getConfigurationCircuits(); + final int index = GT_Utility.findMatchingStackInList(tCircuits, cursorStack); + if (index < 0) { + int curIndex = GT_Utility + .findMatchingStackInList(tCircuits, inv.getStackInSlot(ccs.getCircuitSlot())) + 1; + if (clickData.mouseButton == 0) { + curIndex += 1; + } else { + curIndex -= 1; + } + curIndex = Math.floorMod(curIndex, tCircuits.size() + 1) - 1; + newCircuit = curIndex < 0 ? null : tCircuits.get(curIndex); + } else { + // set to whatever it is + newCircuit = tCircuits.get(index); + } + } + inv.setInventorySlotContents(ccs.getCircuitSlot(), newCircuit); + } + + @Override + protected void phantomScroll(int direction) { + phantomClick(new ClickData(direction > 0 ? 1 : 0, false, false, false)); + } + + @Override + public List<String> getExtraTooltip() { + return Arrays.asList( + EnumChatFormatting.DARK_GRAY + EnumChatFormatting.getTextWithoutFormattingCodes( + StatCollector.translateToLocal("GT5U.machines.select_circuit.tooltip.1")), + EnumChatFormatting.DARK_GRAY + EnumChatFormatting.getTextWithoutFormattingCodes( + StatCollector.translateToLocal("GT5U.machines.select_circuit.tooltip.2")), + EnumChatFormatting.DARK_GRAY + EnumChatFormatting.getTextWithoutFormattingCodes( + StatCollector.translateToLocal("GT5U.machines.select_circuit.tooltip.3"))); + } + }.setOverwriteItemStackTooltip(list -> { + list.removeIf( + line -> line.contains(StatCollector.translateToLocal("gt.integrated_circuit.tooltip.0")) + || line.contains(StatCollector.translateToLocal("gt.integrated_circuit.tooltip.1"))); + return list; + }) + .disableShiftInsert() + .setHandlePhantomActionClient(true) + .setBackground(getGUITextureSet().getItemSlot(), GT_UITextures.OVERLAY_SLOT_INT_CIRCUIT) + .setGTTooltip(() -> mTooltipCache.getData("GT5U.machines.select_circuit.tooltip")) + .setTooltipShowUpDelay(TOOLTIP_DELAY) + .setPos(ccs.getCircuitSlotX() - 1, ccs.getCircuitSlotY() - 1)); + } + + protected void openSelectCircuitDialog(ModularUIContext uiContext, AtomicBoolean dialogOpened) { + final IConfigurationCircuitSupport ccs = getConfigurationCircuitSupport(); + if (ccs == null) return; + + if (!(this instanceof IInventory inv)) return; + + final List<ItemStack> circuits = ccs.getConfigurationCircuits(); + uiContext.openClientWindow( + player -> new SelectItemUIFactory( + StatCollector.translateToLocal("GT5U.machines.select_circuit"), + getStackForm(0), + this::onCircuitSelected, + circuits, + GT_Utility.findMatchingStackInList(circuits, inv.getStackInSlot(ccs.getCircuitSlot()))) + .setAnotherWindow(true, dialogOpened) + .setGuiTint(getGUIColorization()) + .setCurrentGetter(() -> inv.getStackInSlot(ccs.getCircuitSlot())) + .createWindow(new UIBuildContext(player))); + } + + protected void onCircuitSelected(ItemStack selected) { + final IConfigurationCircuitSupport ccs = getConfigurationCircuitSupport(); + if (ccs == null) return; + + if (!(this instanceof IInventory inv)) return; + + GT_Values.NW.sendToServer(new GT_Packet_SetConfigurationCircuit(this, selected)); + // we will not do any validation on client side + // it doesn't get to actually decide what inventory contains anyway + inv.setInventorySlotContents(ccs.getCircuitSlot(), selected); + } + + protected int getTextColorOrDefault(String textType, int defaultColor) { + return defaultColor; + } + + protected Supplier<Integer> COLOR_TITLE = () -> getTextColorOrDefault("title", 0x404040); + protected Supplier<Integer> COLOR_TITLE_WHITE = () -> getTextColorOrDefault("title_white", 0xfafaff); + protected Supplier<Integer> COLOR_TEXT_WHITE = () -> getTextColorOrDefault("text_white", 0xfafaff); + protected Supplier<Integer> COLOR_TEXT_GRAY = () -> getTextColorOrDefault("text_gray", 0x404040); + protected Supplier<Integer> COLOR_TEXT_RED = () -> getTextColorOrDefault("text_red", 0xff0000); + + public int getGUIColorization() { + return GT_Util.getRGBaInt(Dyes.dyeWhite.getRGBA()); + } + + public ItemStack getStackForm(long aAmount) { + return null; + } +} diff --git a/src/main/java/gregtech/api/metatileentity/CommonMetaTileEntity.java b/src/main/java/gregtech/api/metatileentity/CommonMetaTileEntity.java new file mode 100644 index 0000000000..5a2e88b242 --- /dev/null +++ b/src/main/java/gregtech/api/metatileentity/CommonMetaTileEntity.java @@ -0,0 +1,340 @@ +package gregtech.api.metatileentity; + +import static gregtech.GT_Mod.GT_FML_LOGGER; + +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraft.network.Packet; +import net.minecraftforge.common.util.ForgeDirection; + +import com.gtnewhorizons.modularui.api.forge.ItemStackHandler; +import com.gtnewhorizons.modularui.api.screen.ModularWindow; +import com.gtnewhorizons.modularui.api.screen.UIBuildContext; + +import appeng.api.crafting.ICraftingIconProvider; +import gregtech.GT_Mod; +import gregtech.api.GregTech_API; +import gregtech.api.enums.ItemList; +import gregtech.api.gui.modularui.GUITextureSet; +import gregtech.api.interfaces.IConfigurationCircuitSupport; +import gregtech.api.interfaces.metatileentity.IMetaTileEntity; +import gregtech.api.interfaces.modularui.IAddGregtechLogo; +import gregtech.api.interfaces.modularui.IAddUIWidgets; +import gregtech.api.interfaces.modularui.IBindPlayerInventoryUI; +import gregtech.api.interfaces.modularui.IGetTitleColor; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.objects.GT_ItemStack; +import gregtech.api.util.GT_Log; +import gregtech.api.util.GT_Utility; + +public abstract class CommonMetaTileEntity extends CoverableTileEntity + implements IGregTechTileEntity, ICraftingIconProvider { + + protected boolean mNeedsBlockUpdate = true, mNeedsUpdate = true, mSendClientData = false, mInventoryChanged = false; + + protected boolean createNewMetatileEntity(short aID) { + if (aID <= 0 || aID >= GregTech_API.METATILEENTITIES.length || GregTech_API.METATILEENTITIES[aID] == null) { + GT_Log.err.println("MetaID " + aID + " not loadable => locking TileEntity!"); + } else { + if (hasValidMetaTileEntity()) getMetaTileEntity().setBaseMetaTileEntity(null); + GregTech_API.METATILEENTITIES[aID].newMetaEntity(this) + .setBaseMetaTileEntity(this); + mTickTimer = 0; + mID = aID; + return true; + } + return false; + } + + protected void saveMetaTileNBT(NBTTagCompound aNBT) { + try { + if (hasValidMetaTileEntity()) { + aNBT.setInteger("nbtVersion", GT_Mod.NBT_VERSION); + final NBTTagList tItemList = new NBTTagList(); + for (int i = 0; i < getMetaTileEntity().getRealInventory().length; i++) { + final ItemStack tStack = getMetaTileEntity().getRealInventory()[i]; + if (tStack != null) { + final NBTTagCompound tTag = new NBTTagCompound(); + tTag.setInteger("IntSlot", i); + tStack.writeToNBT(tTag); + tItemList.appendTag(tTag); + } + } + aNBT.setTag("Inventory", tItemList); + + try { + getMetaTileEntity().saveNBTData(aNBT); + } catch (Throwable e) { + GT_FML_LOGGER.error("Encountered CRITICAL ERROR while saving MetaTileEntity."); + GT_Mod.logStackTrace(e); + } + } + } catch (Throwable e) { + GT_FML_LOGGER.error("Encountered CRITICAL ERROR while saving MetaTileEntity."); + GT_Mod.logStackTrace(e); + } + } + + protected void loadMetaTileNBT(NBTTagCompound aNBT) { + final int nbtVersion = aNBT.getInteger("nbtVersion"); + if (mID != 0 && createNewMetatileEntity(mID)) { + final NBTTagList tItemList = aNBT.getTagList("Inventory", 10); + for (int i = 0; i < tItemList.tagCount(); i++) { + final NBTTagCompound tTag = tItemList.getCompoundTagAt(i); + final int tSlot = migrateInventoryIndex(tTag.getInteger("IntSlot"), nbtVersion); + if (tSlot >= 0 && tSlot < getMetaTileEntity().getRealInventory().length) { + ItemStack loadedStack = GT_Utility.loadItem(tTag); + // We move away from fluid display item in TEs + if (loadedStack != null && loadedStack.getItem() == ItemList.Display_Fluid.getItem()) { + loadedStack = null; + } + getMetaTileEntity().getRealInventory()[tSlot] = loadedStack; + } + } + + try { + getMetaTileEntity().loadNBTData(aNBT); + } catch (Throwable e) { + GT_FML_LOGGER.error("Encountered Exception while loading MetaTileEntity."); + GT_Mod.logStackTrace(e); + } + } + } + + /** + * Shifts the machine Inventory index according to the change in Input/Output Slots. Default implementation does not + * do anything to the slotIndex. + */ + protected int migrateInventoryIndex(int slotIndex, int nbtVersion) { + return slotIndex; + } + + @Override + public void markDirty() { + super.markDirty(); + mInventoryChanged = true; + } + + @Override + public boolean hasInventoryBeenModified() { + return mInventoryChanged; + } + + @Override + public boolean isValidSlot(int aIndex) { + if (canAccessData()) return getMetaTileEntity().isValidSlot(aIndex); + return false; + } + + @Override + public Packet getDescriptionPacket() { + issueClientUpdate(); + return null; + } + + @Override + public void issueTextureUpdate() { + mNeedsUpdate = true; + } + + @Override + public void issueClientUpdate() { + mSendClientData = true; + } + + @Override + public void issueBlockUpdate() { + mNeedsBlockUpdate = true; + } + + @Override + public boolean isValidFacing(ForgeDirection side) { + if (canAccessData()) return getMetaTileEntity().isFacingValid(side); + return false; + } + + protected boolean canAccessData() { + return !isDead && hasValidMetaTileEntity(); + } + + protected abstract boolean hasValidMetaTileEntity(); + + @Override + public String[] getDescription() { + if (canAccessData()) return getMetaTileEntity().getDescription(); + return new String[0]; + } + + @Override + public boolean isStillValid() { + return hasValidMetaTileEntity(); + } + + @Override + public boolean allowCoverOnSide(ForgeDirection side, GT_ItemStack aCoverID) { + return hasValidMetaTileEntity() && getMetaTileEntity().allowCoverOnSide(side, aCoverID); + } + + @Override + public void issueCoverUpdate(ForgeDirection side) { + super.issueCoverUpdate(side); + issueClientUpdate(); + } + + /* + * IC2 Energy Compat + */ + @Override + public boolean shouldJoinIc2Enet() { + final IMetaTileEntity meta = getMetaTileEntity(); + return meta != null && meta.shouldJoinIc2Enet(); + } + + /* + * Modular UI Support + */ + + @Override + public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) { + if (hasValidMetaTileEntity() && getMetaTileEntity() instanceof IAddUIWidgets) { + ((IAddUIWidgets) getMetaTileEntity()).addUIWidgets(builder, buildContext); + return; + } + super.addUIWidgets(builder, buildContext); + } + + @Override + public void bindPlayerInventoryUI(ModularWindow.Builder builder, UIBuildContext buildContext) { + if (hasValidMetaTileEntity() && getMetaTileEntity() instanceof IBindPlayerInventoryUI) { + ((IBindPlayerInventoryUI) getMetaTileEntity()).bindPlayerInventoryUI(builder, buildContext); + return; + } + super.bindPlayerInventoryUI(builder, buildContext); + } + + @Override + public IConfigurationCircuitSupport getConfigurationCircuitSupport() { + if (hasValidMetaTileEntity() && getMetaTileEntity() instanceof IConfigurationCircuitSupport) { + return (IConfigurationCircuitSupport) getMetaTileEntity(); + } + return null; + } + + @Override + public ItemStackHandler getInventoryHandler() { + if (hasValidMetaTileEntity()) { + return getMetaTileEntity().getInventoryHandler(); + } + return null; + } + + @Override + public boolean useModularUI() { + return hasValidMetaTileEntity() && getMetaTileEntity().useModularUI(); + } + + @Override + public String getLocalName() { + if (hasValidMetaTileEntity()) return getMetaTileEntity().getLocalName(); + return super.getLocalName(); + } + + @Override + protected int getGUIWidth() { + if (hasValidMetaTileEntity()) return getMetaTileEntity().getGUIWidth(); + + return super.getGUIWidth(); + } + + @Override + protected int getGUIHeight() { + if (hasValidMetaTileEntity()) return getMetaTileEntity().getGUIHeight(); + + return super.getGUIHeight(); + } + + @Override + protected boolean doesBindPlayerInventory() { + if (hasValidMetaTileEntity()) return getMetaTileEntity().doesBindPlayerInventory(); + + return super.doesBindPlayerInventory(); + } + + @Override + public void addGregTechLogo(ModularWindow.Builder builder) { + if (hasValidMetaTileEntity() && getMetaTileEntity() instanceof IAddGregtechLogo) { + ((IAddGregtechLogo) getMetaTileEntity()).addGregTechLogo(builder); + return; + } + super.addGregTechLogo(builder); + } + + @Override + public ItemStack getStackForm(long aAmount) { + if (hasValidMetaTileEntity()) { + return getMetaTileEntity().getStackForm(aAmount); + } + return super.getStackForm(aAmount); + } + + @Override + public int getTitleColor() { + if (hasValidMetaTileEntity() && getMetaTileEntity() instanceof IGetTitleColor) { + return ((IGetTitleColor) getMetaTileEntity()).getTitleColor(); + } + return super.getTitleColor(); + } + + @Override + public int getGUIColorization() { + if (hasValidMetaTileEntity()) { + return getMetaTileEntity().getGUIColorization(); + } + return super.getGUIColorization(); + } + + @Override + protected int getTextColorOrDefault(String textType, int defaultColor) { + if (hasValidMetaTileEntity()) { + return getMetaTileEntity().getTextColorOrDefault(textType, defaultColor); + } + return defaultColor; + } + + @Override + public GUITextureSet getGUITextureSet() { + if (hasValidMetaTileEntity()) { + return getMetaTileEntity().getGUITextureSet(); + } + return super.getGUITextureSet(); + } + + @Override + public ItemStack getMachineCraftingIcon() { + return getMetaTileEntity() != null ? getMetaTileEntity().getMachineCraftingIcon() : null; + } + + @Override + public ModularWindow createWindow(UIBuildContext buildContext) { + if (!useModularUI()) return null; + + buildContext.setValidator(getValidator()); + final ModularWindow.Builder builder = ModularWindow.builder(getGUIWidth(), getGUIHeight()); + builder.setBackground(getGUITextureSet().getMainBackground()); + builder.setGuiTint(getGUIColorization()); + if (doesBindPlayerInventory()) { + bindPlayerInventoryUI(builder, buildContext); + } + addUIWidgets(builder, buildContext); + addTitleToUI(builder); + addCoverTabs(builder, buildContext); + final IConfigurationCircuitSupport csc = getConfigurationCircuitSupport(); + if (csc != null && csc.allowSelectCircuit()) { + addConfigurationCircuitSlot(builder); + } else { + addGregTechLogo(builder); + } + return builder.build(); + } +} diff --git a/src/main/java/gregtech/api/metatileentity/CoverableTileEntity.java b/src/main/java/gregtech/api/metatileentity/CoverableTileEntity.java new file mode 100644 index 0000000000..277b79c777 --- /dev/null +++ b/src/main/java/gregtech/api/metatileentity/CoverableTileEntity.java @@ -0,0 +1,803 @@ +package gregtech.api.metatileentity; + +import static gregtech.api.enums.GT_Values.E; +import static gregtech.api.enums.GT_Values.NW; +import static gregtech.api.util.GT_LanguageManager.FACES; +import static gregtech.api.util.GT_LanguageManager.getTranslation; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.IntStream; + +import net.minecraft.client.Minecraft; +import net.minecraft.entity.item.EntityItem; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraft.network.PacketBuffer; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.StatCollector; +import net.minecraft.world.World; +import net.minecraftforge.common.util.ForgeDirection; +import net.minecraftforge.fluids.FluidRegistry; + +import org.jetbrains.annotations.NotNull; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.io.ByteStreams; +import com.gtnewhorizons.modularui.api.drawable.IDrawable; +import com.gtnewhorizons.modularui.api.drawable.ItemDrawable; +import com.gtnewhorizons.modularui.api.math.MainAxisAlignment; +import com.gtnewhorizons.modularui.api.screen.ModularWindow; +import com.gtnewhorizons.modularui.api.screen.UIBuildContext; +import com.gtnewhorizons.modularui.api.widget.Widget; +import com.gtnewhorizons.modularui.common.widget.ButtonWidget; +import com.gtnewhorizons.modularui.common.widget.Column; +import com.gtnewhorizons.modularui.common.widget.FakeSyncWidget; +import com.gtnewhorizons.modularui.common.widget.MultiChildWidget; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import gregtech.GT_Mod; +import gregtech.api.GregTech_API; +import gregtech.api.enums.GT_Values; +import gregtech.api.enums.Textures; +import gregtech.api.gui.modularui.GUITextureSet; +import gregtech.api.interfaces.ITexture; +import gregtech.api.interfaces.tileentity.ICoverable; +import gregtech.api.interfaces.tileentity.IGregtechWailaProvider; +import gregtech.api.net.GT_Packet_RequestCoverData; +import gregtech.api.net.GT_Packet_SendCoverData; +import gregtech.api.net.GT_Packet_TileEntityCoverGUI; +import gregtech.api.objects.GT_ItemStack; +import gregtech.api.util.GT_CoverBehavior; +import gregtech.api.util.GT_CoverBehaviorBase; +import gregtech.api.util.ISerializableObject; +import gregtech.common.GT_Client; +import gregtech.common.covers.CoverInfo; +import gregtech.common.covers.GT_Cover_Fluidfilter; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import mcp.mobius.waila.api.IWailaConfigHandler; +import mcp.mobius.waila.api.IWailaDataAccessor; + +public abstract class CoverableTileEntity extends BaseTileEntity implements ICoverable, IGregtechWailaProvider { + + public static final String[] COVER_DATA_NBT_KEYS = Arrays.stream(ForgeDirection.VALID_DIRECTIONS) + .mapToInt(Enum::ordinal) + .mapToObj(i -> "mCoverData" + i) + .toArray(String[]::new); + + // New Cover Information + protected final CoverInfo[] coverInfos = new CoverInfo[] { null, null, null, null, null, null }; + private byte validCoversMask; + + protected byte[] mSidedRedstone = new byte[] { 15, 15, 15, 15, 15, 15 }; + protected boolean mRedstone = false; + protected byte mStrongRedstone = 0; + + protected short mID = 0; + public long mTickTimer = 0; + private Map<ForgeDirection, ISerializableObject> clientCoverData = new HashMap<>(); + + protected void writeCoverNBT(NBTTagCompound aNBT, boolean isDrop) { + final NBTTagList tList = new NBTTagList(); + final int[] coverSides = new int[] { 0, 0, 0, 0, 0, 0 }; + + for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) { + final CoverInfo coverInfo = getCoverInfoAtSide(side); + if (!coverInfo.isValid()) continue; + + // Backwards compat, in case of a revert... for now + tList.appendTag(coverInfo.writeToNBT(new NBTTagCompound())); + aNBT.setTag( + COVER_DATA_NBT_KEYS[side.ordinal()], + coverInfo.getCoverData() + .saveDataToNBT()); + } + if (tList.tagCount() > 0) { + aNBT.setTag(GT_Values.NBT.COVERS, tList); + // Backwards compat, in case of a revert... for now + aNBT.setIntArray("mCoverSides", coverSides); + } + + if (mStrongRedstone > 0) aNBT.setByte("mStrongRedstone", mStrongRedstone); + + if (!isDrop) { + aNBT.setByteArray("mRedstoneSided", mSidedRedstone); + aNBT.setBoolean("mRedstone", mRedstone); + } + } + + protected void readCoverNBT(NBTTagCompound aNBT) { + mRedstone = aNBT.getBoolean("mRedstone"); + mSidedRedstone = aNBT.hasKey("mRedstoneSided") ? aNBT.getByteArray("mRedstoneSided") + : new byte[] { 15, 15, 15, 15, 15, 15 }; + mStrongRedstone = aNBT.getByte("mStrongRedstone"); + + if (aNBT.hasKey(GT_Values.NBT.COVERS)) { + readCoverInfoNBT(aNBT); + } else if (aNBT.hasKey("mCoverSides")) { + readLegacyCoverInfoNBT(aNBT); + } + } + + public void readCoverInfoNBT(NBTTagCompound aNBT) { + final NBTTagList tList = aNBT.getTagList(GT_Values.NBT.COVERS, 10); + for (byte i = 0; i < tList.tagCount(); i++) { + final NBTTagCompound tNBT = tList.getCompoundTagAt(i); + final CoverInfo coverInfo = new CoverInfo(this, tNBT); + this.setCoverInfoAtSide(coverInfo.getSide(), coverInfo); + if (coverInfo.isDataNeededOnClient()) issueCoverUpdate(ForgeDirection.getOrientation(i)); + } + } + + public void readLegacyCoverInfoNBT(NBTTagCompound aNBT) { + final int[] coverIDs = aNBT.hasKey("mCoverSides") ? aNBT.getIntArray("mCoverSides") + : new int[] { 0, 0, 0, 0, 0, 0 }; + final boolean hasOldCoverData = (aNBT.hasKey("mCoverData", 11) && aNBT.getIntArray("mCoverData").length == 6); + final int[] tOldData = hasOldCoverData ? aNBT.getIntArray("mCoverData") : new int[] {}; + + for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) { + final int ordinalSide = side.ordinal(); + if (coverIDs[ordinalSide] == 0) continue; + + final CoverInfo coverInfo = new CoverInfo(side, coverIDs[ordinalSide], this, null); + final GT_CoverBehaviorBase<?> coverBehavior = coverInfo.getCoverBehavior(); + if (coverBehavior == GregTech_API.sNoBehavior) continue; + + ISerializableObject coverData = null; + if (hasOldCoverData) { + if (coverBehavior instanceof GT_Cover_Fluidfilter) { + final String filterKey = String.format("fluidFilter%d", ordinalSide); + if (aNBT.hasKey(filterKey)) { + coverData = coverInfo.getCoverBehavior() + .createDataObject( + (tOldData[ordinalSide] & 7) + | (FluidRegistry.getFluidID(aNBT.getString(filterKey)) << 3)); + } + } else { + coverData = coverBehavior.createDataObject(tOldData[ordinalSide]); + } + } else { + if (aNBT.hasKey(COVER_DATA_NBT_KEYS[ordinalSide])) + coverData = coverBehavior.createDataObject(aNBT.getTag(COVER_DATA_NBT_KEYS[ordinalSide])); + } + + if (coverData != null) coverInfo.setCoverData(coverData); + setCoverInfoAtSide(side, coverInfo); + if (coverInfo.isDataNeededOnClient()) issueCoverUpdate(side); + } + } + + public abstract boolean isStillValid(); + + protected boolean doCoverThings() { + byte validCoversMask = this.validCoversMask; + if (validCoversMask == 0) return true; + + for (int i = Integer.numberOfTrailingZeros(validCoversMask); i < 6; i++) { + if (((validCoversMask >>> i) & 1) == 0) continue; + if (!tickCoverAtSide(ForgeDirection.VALID_DIRECTIONS[i])) return false; + } + + return true; + } + + public boolean tickCoverAtSide(ForgeDirection side) { + return tickCoverAtSide(side, mTickTimer); + } + + /** + * @return {@code false} if the tile is no longer valid after ticking the cover + */ + public boolean tickCoverAtSide(ForgeDirection side, long aTickTimer) { + final CoverInfo coverInfo = getCoverInfoAtSide(side); + if (!coverInfo.isValid()) return true; + final int tCoverTickRate = coverInfo.getTickRate(); + if (tCoverTickRate > 0 && aTickTimer % tCoverTickRate == 0) { + final byte tRedstone = coverInfo.isRedstoneSensitive(aTickTimer) ? getInputRedstoneSignal(side) : 0; + coverInfo.setCoverData(coverInfo.doCoverThings(aTickTimer, tRedstone)); + return isStillValid(); + } + + return true; + } + + public abstract boolean allowCoverOnSide(ForgeDirection side, GT_ItemStack aCoverID); + + protected void checkDropCover() { + for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) { + final int coverId = getCoverIDAtSide(side); + if (coverId != 0 && !allowCoverOnSide(side, new GT_ItemStack(coverId))) dropCover(side, side, true); + } + } + + protected void updateCoverBehavior() { + for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) { + final CoverInfo coverInfo = getCoverInfoAtSide(side); + if (coverInfo.isValid()) coverInfo.updateCoverBehavior(); + } + } + + @Override + public void issueCoverUpdate(ForgeDirection side) { + // If we've got a null worldObj we're getting called as a part of readingNBT from a non tickable MultiTileEntity + // on chunk load before the world is set, so we'll want to send a cover update. + final CoverInfo coverInfo = getCoverInfoAtSide(side); + if (worldObj == null || (isServerSide() && coverInfo.isDataNeededOnClient())) coverInfo.setNeedsUpdate(true); + } + + public final ITexture getCoverTexture(ForgeDirection side) { + final CoverInfo coverInfo = getCoverInfoAtSide(side); + if (!coverInfo.isValid()) return null; + if (GT_Mod.instance.isClientSide() && (GT_Client.hideValue & 0x1) != 0) { + return Textures.BlockIcons.HIDDEN_TEXTURE[0]; // See through + } + final ITexture coverTexture = (!(this instanceof BaseMetaPipeEntity)) ? coverInfo.getSpecialCoverFGTexture() + : coverInfo.getSpecialCoverTexture(); + + return coverTexture != null ? coverTexture : GregTech_API.sCovers.get(new GT_ItemStack(getCoverIDAtSide(side))); + } + + protected void requestCoverDataIfNeeded() { + if (worldObj == null || !worldObj.isRemote) return; + for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) { + final CoverInfo coverInfo = getCoverInfoAtSide(side); + if (coverInfo.isDataNeededOnClient()) NW.sendToServer(new GT_Packet_RequestCoverData(coverInfo, this)); + } + } + + @Override + public void setCoverIdAndDataAtSide(ForgeDirection side, int aId, ISerializableObject aData) { + if (setCoverIDAtSideNoUpdate(side, aId, aData)) { + issueCoverUpdate(side); + issueBlockUpdate(); + } + } + + @Override + public void setCoverIDAtSide(ForgeDirection side, int aID) { + setCoverIdAndDataAtSide(side, aID, null); + } + + @Override + public boolean setCoverIDAtSideNoUpdate(ForgeDirection side, int aID) { + return setCoverIDAtSideNoUpdate(side, aID, null); + } + + public boolean setCoverIDAtSideNoUpdate(ForgeDirection side, int aID, ISerializableObject aData) { + final CoverInfo oldCoverInfo = getCoverInfoAtSide(side); + if (side != ForgeDirection.UNKNOWN && oldCoverInfo.getCoverID() != aID) { + if (aID == 0 && isClientSide()) oldCoverInfo.onDropped(); + setCoverInfoAtSide(side, new CoverInfo(side, aID, this, aData)); + return true; + } + return false; + } + + @Override + @Deprecated + public void setCoverDataAtSide(ForgeDirection side, int aData) { + final CoverInfo coverInfo = getCoverInfoAtSide(side); + if (coverInfo.isValid() && coverInfo.getCoverData() instanceof ISerializableObject.LegacyCoverData) + coverInfo.setCoverData(new ISerializableObject.LegacyCoverData(aData)); + } + + @Override + public void setCoverDataAtSide(ForgeDirection side, ISerializableObject aData) { + final CoverInfo coverInfo = getCoverInfoAtSide(side); + if (coverInfo.isValid() && coverInfo.getCoverBehavior() + .cast(aData) != null) coverInfo.setCoverData(aData); + } + + @Override + @Deprecated + public GT_CoverBehavior getCoverBehaviorAtSide(ForgeDirection side) { + final GT_CoverBehaviorBase<?> behavior = getCoverInfoAtSide(side).getCoverBehavior(); + if (behavior instanceof GT_CoverBehavior) return (GT_CoverBehavior) behavior; + return GregTech_API.sNoBehavior; + } + + @Override + public void setCoverItemAtSide(ForgeDirection side, ItemStack aCover) { + GregTech_API.getCoverBehaviorNew(aCover) + .placeCover(side, aCover, this); + } + + @Override + public int getCoverIDAtSide(ForgeDirection side) { + return getCoverInfoAtSide(side).getCoverID(); + } + + @Override + public ItemStack getCoverItemAtSide(ForgeDirection side) { + return getCoverInfoAtSide(side).getDisplayStack(); + } + + @Override + public boolean canPlaceCoverIDAtSide(ForgeDirection side, int aID) { + return getCoverIDAtSide(side) == 0; + } + + @Override + public boolean canPlaceCoverItemAtSide(ForgeDirection side, ItemStack aCover) { + return getCoverIDAtSide(side) == 0; + } + + @Override + @Deprecated + public int getCoverDataAtSide(ForgeDirection side) { + final ISerializableObject coverData = getCoverInfoAtSide(side).getCoverData(); + if (coverData instanceof ISerializableObject.LegacyCoverData) { + return ((ISerializableObject.LegacyCoverData) coverData).get(); + } + return 0; + } + + @Override + public ISerializableObject getComplexCoverDataAtSide(ForgeDirection side) { + return getCoverInfoAtSide(side).getCoverData(); + } + + @Override + public GT_CoverBehaviorBase<?> getCoverBehaviorAtSideNew(ForgeDirection side) { + return getCoverInfoAtSide(side).getCoverBehavior(); + } + + public final void setCoverInfoAtSide(ForgeDirection side, CoverInfo coverInfo) { + if (side != ForgeDirection.UNKNOWN) { + coverInfos[side.ordinal()] = coverInfo; + + validCoversMask &= (byte) ~side.flag; + if (coverInfo.isValid()) validCoversMask |= side.flag; + } + } + + @Override + public final CoverInfo getCoverInfoAtSide(ForgeDirection side) { + final int ordinalSide = side.ordinal(); + if (side != ForgeDirection.UNKNOWN) { + CoverInfo coverInfo = coverInfos[ordinalSide]; + if (coverInfo == null) coverInfo = (coverInfos[ordinalSide] = new CoverInfo(side, this)); + return coverInfo; + } + return CoverInfo.EMPTY_INFO; + } + + public void clearCoverInfoAtSide(ForgeDirection side) { + if (side != ForgeDirection.UNKNOWN) { + setCoverIDAtSide(side, 0); + } + } + + @Override + public boolean dropCover(ForgeDirection side, ForgeDirection droppedSide, boolean aForced) { + final CoverInfo coverInfo = getCoverInfoAtSide(side); + if (!coverInfo.isValid()) return false; + if (!coverInfo.onCoverRemoval(aForced) && !aForced) return false; + final ItemStack tStack = coverInfo.getDrop(); + if (tStack != null) { + coverInfo.onDropped(); + final EntityItem tEntity = new EntityItem( + worldObj, + getOffsetX(droppedSide, 1) + 0.5, + getOffsetY(droppedSide, 1) + 0.5, + getOffsetZ(droppedSide, 1) + 0.5, + tStack); + tEntity.motionX = 0; + tEntity.motionY = 0; + tEntity.motionZ = 0; + worldObj.spawnEntityInWorld(tEntity); + } + clearCoverInfoAtSide(side); + updateOutputRedstoneSignal(side); + + return true; + } + + protected void onBaseTEDestroyed() { + for (ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) { + final CoverInfo coverInfo = getCoverInfoAtSide(side); + if (coverInfo.isValid()) coverInfo.onBaseTEDestroyed(); + } + } + + @Override + public void setOutputRedstoneSignal(ForgeDirection side, byte strength) { + final byte cappedStrength = (byte) Math.min(Math.max(0, strength), 15); + if (side == ForgeDirection.UNKNOWN) return; + + final int ordinalSide = side.ordinal(); + if (mSidedRedstone[ordinalSide] != cappedStrength || (mStrongRedstone & (1 << ordinalSide)) > 0) { + mSidedRedstone[ordinalSide] = cappedStrength; + issueBlockUpdate(); + } + } + + @Override + public void setStrongOutputRedstoneSignal(ForgeDirection side, byte strength) { + mStrongRedstone |= (byte) side.flag; + setOutputRedstoneSignal(side, strength); + } + + @Override + public void setInternalOutputRedstoneSignal(ForgeDirection side, byte aStrength) { + if (!getCoverBehaviorAtSideNew(side) + .manipulatesSidedRedstoneOutput(side, getCoverIDAtSide(side), getComplexCoverDataAtSide(side), this)) + setOutputRedstoneSignal(side, aStrength); + } + + @Override + public boolean getRedstone() { + return Arrays.stream(ForgeDirection.VALID_DIRECTIONS) + .anyMatch(this::getRedstone); + } + + @Override + public boolean getRedstone(ForgeDirection side) { + return getInternalInputRedstoneSignal(side) > 0; + } + + @Override + public byte getStrongestRedstone() { + return Arrays.stream(ForgeDirection.VALID_DIRECTIONS) + .map(this::getInternalInputRedstoneSignal) + .max(Comparator.comparing(Byte::valueOf)) + .orElse((byte) 0); + } + + @Override + public byte getStrongOutputRedstoneSignal(ForgeDirection side) { + final int ordinalSide = side.ordinal(); + return side != ForgeDirection.UNKNOWN && (mStrongRedstone & (1 << ordinalSide)) != 0 + ? (byte) (mSidedRedstone[ordinalSide] & 15) + : 0; + } + + @Override + public void setGenericRedstoneOutput(boolean aOnOff) { + mRedstone = aOnOff; + } + + @Override + public byte getInternalInputRedstoneSignal(ForgeDirection side) { + return (byte) (getCoverBehaviorAtSideNew(side).getRedstoneInput( + side, + getInputRedstoneSignal(side), + getCoverIDAtSide(side), + getComplexCoverDataAtSide(side), + this) & 15); + } + + @Override + public byte getInputRedstoneSignal(ForgeDirection side) { + return (byte) (worldObj + .getIndirectPowerLevelTo(getOffsetX(side, 1), getOffsetY(side, 1), getOffsetZ(side, 1), side.ordinal()) + & 15); + } + + @Override + public byte getOutputRedstoneSignal(ForgeDirection side) { + return getCoverBehaviorAtSideNew(side) + .manipulatesSidedRedstoneOutput(side, getCoverIDAtSide(side), getComplexCoverDataAtSide(side), this) + ? mSidedRedstone[side.ordinal()] + : getGeneralRS(side); + } + + protected void updateOutputRedstoneSignal(ForgeDirection side) { + setOutputRedstoneSignal(side, (byte) 0); + } + + @Override + public void receiveCoverData(ForgeDirection coverSide, int aCoverID, int aCoverData) { + if (coverSide == ForgeDirection.UNKNOWN) return; + final CoverInfo oldCoverInfo = getCoverInfoAtSide(coverSide); + if (!oldCoverInfo.isValid()) return; + + setCoverIDAtSideNoUpdate(coverSide, aCoverID); + setCoverDataAtSide(coverSide, aCoverData); + } + + @Override + public void receiveCoverData(ForgeDirection coverSide, int aCoverID, ISerializableObject aCoverData, + EntityPlayerMP aPlayer) { + if (coverSide == ForgeDirection.UNKNOWN) return; + + final CoverInfo oldCoverInfo = getCoverInfoAtSide(coverSide); + + if (!oldCoverInfo.isValid()) return; + oldCoverInfo.preDataChanged(aCoverID, aCoverData); + setCoverIDAtSideNoUpdate(coverSide, aCoverID, aCoverData); + setCoverDataAtSide(coverSide, aCoverData); + + if (isClientSide()) { + getCoverInfoAtSide(coverSide).onDataChanged(); + } + } + + protected void sendCoverDataIfNeeded() { + if (worldObj == null || worldObj.isRemote) return; + for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) { + final CoverInfo coverInfo = getCoverInfoAtSide(side); + if (coverInfo.needsUpdate()) { + NW.sendPacketToAllPlayersInRange( + worldObj, + new GT_Packet_SendCoverData(coverInfo, this), + xCoord, + zCoord); + coverInfo.setNeedsUpdate(false); + } + } + } + + @Override + public void getWailaBody(ItemStack itemStack, List<String> currentTip, IWailaDataAccessor accessor, + IWailaConfigHandler config) { + final NBTTagCompound tag = accessor.getNBTData(); + final ForgeDirection currentFacing = accessor.getSide(); + + final NBTTagList tList = tag.getTagList(GT_Values.NBT.COVERS, 10); + for (byte i = 0; i < tList.tagCount(); i++) { + final NBTTagCompound tNBT = tList.getCompoundTagAt(i); + final CoverInfo coverInfo = new CoverInfo(this, tNBT); + if (!coverInfo.isValid() || coverInfo.getCoverBehavior() == GregTech_API.sNoBehavior) continue; + + final ItemStack coverStack = coverInfo.getDisplayStack(); + if (coverStack != null) { + currentTip.add( + StatCollector.translateToLocalFormatted( + "GT5U.waila.cover", + currentFacing == coverInfo.getSide() + ? StatCollector.translateToLocal("GT5U.waila.cover.current_facing") + : StatCollector.translateToLocal( + "GT5U.interface.coverTabs." + coverInfo.getSide() + .toString() + .toLowerCase()), + coverStack.getDisplayName())); + final String behaviorDesc = coverInfo.getBehaviorDescription(); + if (!Objects.equals(behaviorDesc, E)) currentTip.add(behaviorDesc); + } + } + + // No super implementation + // super.getWailaBody(itemStack, currenttip, accessor, config); + } + + @Override + public void getWailaNBTData(EntityPlayerMP player, TileEntity tile, NBTTagCompound tag, World world, int x, int y, + int z) { + // No super implementation + // super.getWailaNBTData(player, tile, tag, world, x, y, z); + + // While we have some cover data on the client (enough to render it); we don't have all the information we want, + // such as details on the fluid filter, so send it all here. + writeCoverNBT(tag, false); + } + + /** + * Add installed cover information, generally called from ItemBlock + * + * @param aNBT - NBTTagCompound from the stack + * @param aList - List to add the information to + */ + public static void addInstalledCoversInformation(NBTTagCompound aNBT, List<String> aList) { + if (aNBT == null || aList == null) return; + final NBTTagList tList = aNBT.getTagList(GT_Values.NBT.COVERS, 10); + for (byte i = 0; i < tList.tagCount(); i++) { + final NBTTagCompound tNBT = tList.getCompoundTagAt(i); + final CoverInfo coverInfo = new CoverInfo(null, tNBT); + if (!coverInfo.isValid() || coverInfo.getCoverBehavior() == GregTech_API.sNoBehavior) continue; + + final ItemStack coverStack = coverInfo.getDisplayStack(); + if (coverStack != null) { + aList.add( + String.format( + "Cover on %s side: %s", + getTranslation( + FACES[coverInfo.getSide() + .ordinal()]), + coverStack.getDisplayName())); + } + } + + if (aNBT.hasKey("mCoverSides")) { + final int[] mCoverSides = aNBT.getIntArray("mCoverSides"); + if (mCoverSides != null && mCoverSides.length == 6) { + for (final ForgeDirection tSide : ForgeDirection.VALID_DIRECTIONS) { + final int i = tSide.ordinal(); + final int coverId = mCoverSides[i]; + if (coverId == 0) continue; + final GT_CoverBehaviorBase<?> behavior = GregTech_API.getCoverBehaviorNew(coverId); + if (behavior == null || behavior == GregTech_API.sNoBehavior) continue; + if (!aNBT.hasKey(CoverableTileEntity.COVER_DATA_NBT_KEYS[i])) continue; + final ISerializableObject dataObject = behavior + .createDataObject(aNBT.getTag(CoverableTileEntity.COVER_DATA_NBT_KEYS[i])); + final ItemStack coverStack = behavior.getDisplayStack(coverId, dataObject); + if (coverStack != null) { + aList.add( + String + .format("Cover on %s side: %s", getTranslation(FACES[i]), coverStack.getDisplayName())); + } + } + } + } + } + + protected ModularWindow createCoverWindow(EntityPlayer player, ForgeDirection side) { + return getCoverInfoAtSide(side).createWindow(player); + } + + protected static final int COVER_WINDOW_ID_START = 1; + + @Override + public void addCoverTabs(ModularWindow.Builder builder, UIBuildContext buildContext) { + final int COVER_TAB_LEFT = -16, COVER_TAB_TOP = 1, COVER_TAB_HEIGHT = 20, COVER_TAB_WIDTH = 18, + COVER_TAB_SPACING = 2, ICON_SIZE = 16; + final boolean flipHorizontally = GT_Mod.gregtechproxy.mCoverTabsFlipped; + + final Column columnWidget = new Column(); + builder.widget(columnWidget); + final int xPos = flipHorizontally ? (getGUIWidth() - COVER_TAB_LEFT - COVER_TAB_WIDTH) : COVER_TAB_LEFT; + if (GT_Mod.gregtechproxy.mCoverTabsVisible) { + columnWidget.setPos(xPos, COVER_TAB_TOP) + .setEnabled( + widget -> ((Column) widget).getChildren() + .stream() + .anyMatch(Widget::isEnabled)); + } else { + columnWidget.setEnabled(false); + } + columnWidget.setAlignment(MainAxisAlignment.SPACE_BETWEEN) + .setSpace(COVER_TAB_SPACING); + + for (ForgeDirection direction : ForgeDirection.VALID_DIRECTIONS) { + buildContext.addSyncedWindow( + direction.ordinal() + COVER_WINDOW_ID_START, + player -> createCoverWindow(player, direction)); + columnWidget.addChild(new MultiChildWidget().addChild(new ButtonWidget() { + + @Override + public IDrawable[] getBackground() { + final List<IDrawable> backgrounds = new ArrayList<>(); + final GUITextureSet tabIconSet = getGUITextureSet(); + + if (getCoverBehaviorAtSideNew(direction).hasCoverGUI()) { + if (isHovering()) { + backgrounds.add( + flipHorizontally ? tabIconSet.getCoverTabHighlightFlipped() + : tabIconSet.getCoverTabHighlight()); + } else { + backgrounds.add( + flipHorizontally ? tabIconSet.getCoverTabNormalFlipped() + : tabIconSet.getCoverTabNormal()); + } + } else { + backgrounds.add( + flipHorizontally ? tabIconSet.getCoverTabDisabledFlipped() + : tabIconSet.getCoverTabDisabled()); + } + return backgrounds.toArray(new IDrawable[] {}); + } + }.setOnClick((clickData, widget) -> onTabClicked(clickData, widget, direction)) + .dynamicTooltip(() -> getCoverTabTooltip(direction, clientCoverData.get(direction))) + .setSize(COVER_TAB_WIDTH, COVER_TAB_HEIGHT)) + .addChild( + new ItemDrawable(() -> getCoverItemAtSide(direction)).asWidget() + .setPos( + (COVER_TAB_WIDTH - ICON_SIZE) / 2 + (flipHorizontally ? -1 : 1), + (COVER_TAB_HEIGHT - ICON_SIZE) / 2)) + .setEnabled(widget -> getCoverItemAtSide(direction) != null)); + } + + builder.widget( + new FakeSyncWidget<>( + this::collectCoverData, + data -> clientCoverData = data, + this::writeClientCoverData, + this::readClientCoverData)); + } + + @SideOnly(Side.CLIENT) + protected List<String> getCoverTabTooltip(ForgeDirection side, ISerializableObject coverData) { + final String[] SIDE_TOOLTIPS = new String[] { "GT5U.interface.coverTabs.down", "GT5U.interface.coverTabs.up", + "GT5U.interface.coverTabs.north", "GT5U.interface.coverTabs.south", "GT5U.interface.coverTabs.west", + "GT5U.interface.coverTabs.east" }; + final CoverInfo coverInfo = getCoverInfoAtSide(side); + final ItemStack coverItem = coverInfo.getDisplayStack(); + if (coverItem == null) return Collections.emptyList(); + final boolean coverHasGUI = coverInfo.hasCoverGUI(); + + final List<String> tooltip = coverItem.getTooltip(Minecraft.getMinecraft().thePlayer, true); + final ImmutableList.Builder<String> builder = ImmutableList.builder(); + builder.add( + (coverHasGUI ? EnumChatFormatting.UNDERLINE : EnumChatFormatting.DARK_GRAY) + + StatCollector.translateToLocal(SIDE_TOOLTIPS[side.ordinal()]) + + (coverHasGUI ? EnumChatFormatting.RESET + ": " : ": " + EnumChatFormatting.RESET) + + tooltip.get(0)); + builder.addAll(coverInfo.getAdditionalTooltip(coverData)); + builder.addAll( + IntStream.range(1, tooltip.size()) + .mapToObj(index -> EnumChatFormatting.GRAY + tooltip.get(index)) + .iterator()); + return builder.build(); + } + + protected void onTabClicked(Widget.ClickData ignoredClickData, Widget widget, ForgeDirection side) { + if (isClientSide()) return; + final CoverInfo coverInfo = getCoverInfoAtSide(side); + if (coverInfo.useModularUI()) { + widget.getContext() + .openSyncedWindow(side.ordinal() + COVER_WINDOW_ID_START); + } else { + final GT_Packet_TileEntityCoverGUI packet = new GT_Packet_TileEntityCoverGUI( + coverInfo, + getWorld().provider.dimensionId, + widget.getContext() + .getPlayer() + .getEntityId(), + 0); + GT_Values.NW.sendToPlayer( + packet, + (EntityPlayerMP) widget.getContext() + .getPlayer()); + } + } + + @NotNull + private Map<ForgeDirection, ISerializableObject> collectCoverData() { + final ImmutableMap.Builder<ForgeDirection, ISerializableObject> builder = ImmutableMap.builder(); + for (final ForgeDirection direction : ForgeDirection.VALID_DIRECTIONS) { + final CoverInfo coverInfo = getCoverInfoAtSide(direction); + if (coverInfo.isValid()) { + builder.put(direction, coverInfo.getCoverData()); + } + } + + return builder.build(); + } + + private void writeClientCoverData(@NotNull PacketBuffer buffer, + @NotNull Map<ForgeDirection, ISerializableObject> dataMap) { + buffer.writeInt(dataMap.size()); + dataMap.forEach((direction, serializableObject) -> { + final ByteBuf individualBuffer = Unpooled.buffer(); + serializableObject.writeToByteBuf(individualBuffer); + + buffer.writeByte(direction.ordinal()); + buffer.writeInt(individualBuffer.array().length); + buffer.writeBytes(individualBuffer.array()); + }); + } + + @NotNull + private Map<ForgeDirection, ISerializableObject> readClientCoverData(@NotNull PacketBuffer buffer) { + ImmutableMap.Builder<ForgeDirection, ISerializableObject> builder = ImmutableMap.builder(); + final int size = buffer.readInt(); + for (int i = 0; i < size; i++) { + final ForgeDirection direction = ForgeDirection.getOrientation(buffer.readByte()); + final int length = buffer.readInt(); + final byte[] object = buffer.readBytes(length) + .array(); + + // noinspection UnstableApiUsage + builder.put( + direction, + getCoverInfoAtSide(direction).getCoverBehavior() + .createDataObject() + .readFromPacket(ByteStreams.newDataInput(object), null)); + } + + return builder.build(); + } +} diff --git a/src/main/java/gregtech/api/metatileentity/GregTechTileClientEvents.java b/src/main/java/gregtech/api/metatileentity/GregTechTileClientEvents.java new file mode 100644 index 0000000000..2f560c5f15 --- /dev/null +++ b/src/main/java/gregtech/api/metatileentity/GregTechTileClientEvents.java @@ -0,0 +1,13 @@ +package gregtech.api.metatileentity; + +public final class GregTechTileClientEvents { + + public static final byte CHANGE_COMMON_DATA = 0; + public static final byte CHANGE_CUSTOM_DATA = 1; + public static final byte CHANGE_COLOR = 2; + public static final byte CHANGE_REDSTONE_OUTPUT = 3; + public static final byte DO_SOUND = 4; + public static final byte START_SOUND_LOOP = 5; + public static final byte STOP_SOUND_LOOP = 6; + public static final byte CHANGE_LIGHT = 7; +} diff --git a/src/main/java/gregtech/api/metatileentity/MetaPipeEntity.java b/src/main/java/gregtech/api/metatileentity/MetaPipeEntity.java new file mode 100644 index 0000000000..a7e05355a4 --- /dev/null +++ b/src/main/java/gregtech/api/metatileentity/MetaPipeEntity.java @@ -0,0 +1,1029 @@ +package gregtech.api.metatileentity; + +import static gregtech.api.enums.GT_Values.GT; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Objects; + +import net.minecraft.block.Block; +import net.minecraft.client.renderer.RenderBlocks; +import net.minecraft.client.renderer.texture.IIconRegister; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.init.Blocks; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.AxisAlignedBB; +import net.minecraft.world.IBlockAccess; +import net.minecraft.world.World; +import net.minecraftforge.common.util.ForgeDirection; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.FluidTankInfo; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import gnu.trove.list.TIntList; +import gnu.trove.list.array.TIntArrayList; +import gregtech.api.GregTech_API; +import gregtech.api.enums.Dyes; +import gregtech.api.enums.GT_Values; +import gregtech.api.enums.Textures; +import gregtech.api.interfaces.ITexture; +import gregtech.api.interfaces.metatileentity.IConnectable; +import gregtech.api.interfaces.metatileentity.IMetaTileEntity; +import gregtech.api.interfaces.tileentity.IColoredTileEntity; +import gregtech.api.interfaces.tileentity.ICoverable; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.objects.GT_ItemStack; +import gregtech.api.util.GT_Config; +import gregtech.api.util.GT_CoverBehavior; +import gregtech.api.util.GT_CoverBehaviorBase; +import gregtech.api.util.GT_LanguageManager; +import gregtech.api.util.GT_Util; +import gregtech.api.util.GT_Utility; +import gregtech.api.util.ISerializableObject; +import gregtech.api.util.WorldSpawnedEventBuilder; +import gregtech.common.GT_Client; +import gregtech.common.covers.CoverInfo; + +/** + * NEVER INCLUDE THIS FILE IN YOUR MOD!!! + * <p/> + * Extend this Class to add a new MetaPipe Call the Constructor with the desired ID at the load-phase (not preload and + * also not postload!) Implement the newMetaEntity-Method to return a new ready instance of your MetaTileEntity + * <p/> + * Call the Constructor like the following example inside the Load Phase, to register it. "new + * GT_MetaTileEntity_E_Furnace(54, "GT_E_Furnace", "Automatic E-Furnace");" + */ +public abstract class MetaPipeEntity implements IMetaTileEntity, IConnectable { + + /** + * The Inventory of the MetaTileEntity. Amount of Slots can be larger than 256. HAYO! + */ + public final ItemStack[] mInventory; + /** + * This variable tells, which directions the Block is connected to. It is a Bitmask. + */ + public byte mConnections = 0; + + protected boolean mCheckConnections = false; + /** + * Only assigned for the MetaTileEntity in the List! Also only used to get the localized Name for the ItemStack and + * for getInvName. + */ + public String mName; + + public boolean doTickProfilingInThisTick = true; + /** + * accessibility to this Field is no longer given, see below + */ + private IGregTechTileEntity mBaseMetaTileEntity; + + /** + * This registers your Machine at the List. Use only ID's larger than 2048 - the ones lower are reserved by GT. + * See also the list in the API package - it has a description that contains all the reservations. + * <p> + * The constructor can be overloaded as follows: + * <blockquote> + * + * <pre> + * + * public GT_MetaTileEntity_EBench(int id, String name, String nameRegional) { + * super(id, name, nameRegional); + * } + * </pre> + * + * </blockquote> + * + * @param aID the machine ID + */ + public MetaPipeEntity(int aID, String aBasicName, String aRegionalName, int aInvSlotCount) { + this(aID, aBasicName, aRegionalName, aInvSlotCount, true); + } + + public MetaPipeEntity(int aID, String aBasicName, String aRegionalName, int aInvSlotCount, boolean aAddInfo) { + if (GregTech_API.sPostloadStarted || !GregTech_API.sPreloadStarted) + throw new IllegalAccessError("This Constructor has to be called in the load Phase"); + if (GregTech_API.METATILEENTITIES[aID] == null) { + GregTech_API.METATILEENTITIES[aID] = this; + } else { + throw new IllegalArgumentException("MetaMachine-Slot Nr. " + aID + " is already occupied!"); + } + mName = aBasicName.replaceAll(" ", "_") + .toLowerCase(Locale.ENGLISH); + setBaseMetaTileEntity(new BaseMetaPipeEntity()); + getBaseMetaTileEntity().setMetaTileID((short) aID); + GT_LanguageManager.addStringLocalization("gt.blockmachines." + mName + ".name", aRegionalName); + mInventory = new ItemStack[aInvSlotCount]; + + if (aAddInfo && GT.isClientSide()) { + addInfo(aID); + } + } + + protected final void addInfo(int aID) { + if (!GT.isClientSide()) return; + + ItemStack tStack = new ItemStack(GregTech_API.sBlockMachines, 1, aID); + Objects.requireNonNull(tStack.getItem()) + .addInformation(tStack, null, new ArrayList<>(), true); + } + + /** + * This is the normal Constructor. + */ + public MetaPipeEntity(String aName, int aInvSlotCount) { + mInventory = new ItemStack[aInvSlotCount]; + mName = aName; + } + + /** + * For Pipe Rendering + */ + public abstract float getThickNess(); + + /** + * For Pipe Rendering + */ + public abstract boolean renderInside(ForgeDirection side); + + public boolean isDisplaySecondaryDescription() { + return false; + } + + @Override + public ITexture[] getTexture(IGregTechTileEntity baseMetaTileEntity, ForgeDirection side, ForgeDirection facing, + int colorIndex, boolean active, boolean redstoneLevel) { + return Textures.BlockIcons.ERROR_RENDERING; + } + + public ITexture[] getTexture(IGregTechTileEntity baseMetaTileEntity, ForgeDirection sideDirection, int connections, + int colorIndex, boolean active, boolean redstoneLevel) { + return Textures.BlockIcons.ERROR_RENDERING; + } + + @Override + public IGregTechTileEntity getBaseMetaTileEntity() { + return mBaseMetaTileEntity; + } + + @Override + public void setBaseMetaTileEntity(IGregTechTileEntity aBaseMetaTileEntity) { + if (mBaseMetaTileEntity != null && aBaseMetaTileEntity == null) { + mBaseMetaTileEntity.getMetaTileEntity() + .inValidate(); + mBaseMetaTileEntity.setMetaTileEntity(null); + } + mBaseMetaTileEntity = aBaseMetaTileEntity; + if (mBaseMetaTileEntity != null) { + mBaseMetaTileEntity.setMetaTileEntity(this); + } + } + + @Override + public ItemStack getStackForm(long aAmount) { + return new ItemStack(GregTech_API.sBlockMachines, (int) aAmount, getBaseMetaTileEntity().getMetaTileID()); + } + + public boolean isCoverOnSide(BaseMetaPipeEntity aPipe, EntityLivingBase aEntity) { + ForgeDirection side = ForgeDirection.UNKNOWN; + double difference = aEntity.posY - (double) aPipe.yCoord; + if (difference > 0.6 && difference < 0.99) { + side = ForgeDirection.UP; + } + if (difference < -1.5 && difference > -1.99) { + side = ForgeDirection.DOWN; + } + difference = aEntity.posZ - (double) aPipe.zCoord; + if (difference < -0.05 && difference > -0.4) { + side = ForgeDirection.NORTH; + } + if (difference > 1.05 && difference < 1.4) { + side = ForgeDirection.SOUTH; + } + difference = aEntity.posX - (double) aPipe.xCoord; + if (difference < -0.05 && difference > -0.4) { + side = ForgeDirection.WEST; + } + if (difference > 1.05 && difference < 1.4) { + side = ForgeDirection.EAST; + } + boolean tCovered = side != ForgeDirection.UNKNOWN && mBaseMetaTileEntity.getCoverIDAtSide(side) > 0; + if (isConnectedAtSide(side)) { + tCovered = true; + } + // GT_FML_LOGGER.info("Cover: "+mBaseMetaTileEntity.getCoverIDAtSide(aSide)); + // toDo: filter cover ids that actually protect against temperature (rubber/plastic maybe?, more like asbestos) + return tCovered; + } + + @Override + public void onServerStart() { + /* Do nothing */ + } + + @Override + public void onWorldSave(File aSaveDirectory) { + /* Do nothing */ + } + + @Override + public void onWorldLoad(File aSaveDirectory) { + /* Do nothing */ + } + + @Override + public void onConfigLoad(GT_Config aConfig) { + /* Do nothing */ + } + + @Override + public void setItemNBT(NBTTagCompound aNBT) { + /* Do nothing */ + } + + @Override + @SideOnly(Side.CLIENT) + public void registerIcons(IIconRegister aBlockIconRegister) { + /* Do nothing */ + } + + @Override + public boolean allowCoverOnSide(ForgeDirection side, GT_ItemStack aCoverID) { + return true; + } + + @Deprecated + public void onScrewdriverRightClick(ForgeDirection side, EntityPlayer aPlayer, float aX, float aY, float aZ) { + /* Do nothing */ + } + + @Deprecated + public boolean onWrenchRightClick(ForgeDirection side, ForgeDirection wrenchingSide, EntityPlayer entityPlayer, + float aX, float aY, float aZ) { + return false; + } + + @Deprecated + public boolean onWireCutterRightClick(ForgeDirection side, ForgeDirection wrenchingSide, EntityPlayer aPlayer, + float aX, float aY, float aZ) { + return false; + } + + @Deprecated + public boolean onSolderingToolRightClick(ForgeDirection side, ForgeDirection wrenchingSide, EntityPlayer aPlayer, + float aX, float aY, float aZ) { + return false; + } + + @Override + public void onScrewdriverRightClick(ForgeDirection side, EntityPlayer aPlayer, float aX, float aY, float aZ, + ItemStack aTool) { + onScrewdriverRightClick(side, aPlayer, aX, aY, aZ); + } + + @Override + public boolean onWrenchRightClick(ForgeDirection side, ForgeDirection wrenchingSide, EntityPlayer aPlayer, float aX, + float aY, float aZ, ItemStack aTool) { + return onWrenchRightClick(side, wrenchingSide, aPlayer, aX, aY, aZ); + } + + @Override + public boolean onWireCutterRightClick(ForgeDirection side, ForgeDirection wrenchingSide, EntityPlayer aPlayer, + float aX, float aY, float aZ, ItemStack aTool) { + return onWireCutterRightClick(side, wrenchingSide, aPlayer, aX, aY, aZ); + } + + @Override + public boolean onSolderingToolRightClick(ForgeDirection side, ForgeDirection wrenchingSide, EntityPlayer aPlayer, + float aX, float aY, float aZ, ItemStack aTool) { + return onSolderingToolRightClick(side, wrenchingSide, aPlayer, aX, aY, aZ); + } + + @Override + public void onExplosion() { + /* Do nothing */ + } + + @Override + public void onFirstTick(IGregTechTileEntity aBaseMetaTileEntity) { + /* Do nothing */ + } + + @Override + public void onPreTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) { + /* Do nothing */ + } + + @Override + public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) { + if (aBaseMetaTileEntity.isClientSide() && GT_Client.changeDetected == 4) { + /* + * Client tick counter that is set to 5 on hiding pipes and covers. It triggers a texture update next client + * tick when reaching 4, with provision for 3 more update tasks, spreading client change detection related + * work and network traffic on different ticks, until it reaches 0. + */ + aBaseMetaTileEntity.issueTextureUpdate(); + } + } + + @Override + public void inValidate() { + /* Do nothing */ + } + + @Override + public void onRemoval() { + /* Do nothing */ + } + + @Override + public void initDefaultModes(NBTTagCompound aNBT) { + /* Do nothing */ + } + + /** + * When a GUI is opened + */ + public void onOpenGUI() { + /* Do nothing */ + } + + /** + * When a GUI is closed + */ + public void onCloseGUI() { + /* Do nothing */ + } + + /** + * Called when a Player rightclicks the Machine. Sneaky rightclicks are not getting passed to this! + */ + @Override + public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer, ForgeDirection side, + float aX, float aY, float aZ) { + return false; + } + + @Override + public void onLeftclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer) { + /* Do nothing */ + } + + @Override + public void onValueUpdate(byte aValue) { + /* Do nothing */ + } + + @Override + public byte getUpdateData() { + return 0; + } + + @Override + public void doSound(byte aIndex, double aX, double aY, double aZ) { + /* Do nothing */ + } + + @Override + public void startSoundLoop(byte aIndex, double aX, double aY, double aZ) { + /* Do nothing */ + } + + @Override + public void stopSoundLoop(byte aValue, double aX, double aY, double aZ) { + /* Do nothing */ + } + + @Override + public final void sendSound(byte aIndex) { + if (!getBaseMetaTileEntity().hasMufflerUpgrade()) + getBaseMetaTileEntity().sendBlockEvent(GregTechTileClientEvents.DO_SOUND, aIndex); + } + + @Override + public final void sendLoopStart(byte aIndex) { + if (!getBaseMetaTileEntity().hasMufflerUpgrade()) + getBaseMetaTileEntity().sendBlockEvent(GregTechTileClientEvents.START_SOUND_LOOP, aIndex); + } + + @Override + public final void sendLoopEnd(byte aIndex) { + if (!getBaseMetaTileEntity().hasMufflerUpgrade()) + getBaseMetaTileEntity().sendBlockEvent(GregTechTileClientEvents.STOP_SOUND_LOOP, aIndex); + } + + @Override + public boolean isFacingValid(ForgeDirection facing) { + return false; + } + + @Override + public boolean isAccessAllowed(EntityPlayer aPlayer) { + return true; + } + + @Override + public boolean isValidSlot(int aIndex) { + return true; + } + + @Override + public boolean shouldDropItemAt(int index) { + return true; + } + + @Override + public boolean setStackToZeroInsteadOfNull(int aIndex) { + return false; + } + + @Override + public ArrayList<String> getSpecialDebugInfo(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer, + int aLogLevel, ArrayList<String> aList) { + return aList; + } + + @Override + public boolean isLiquidInput(ForgeDirection side) { + return false; + } + + @Override + public boolean isLiquidOutput(ForgeDirection side) { + return false; + } + + /** + * gets the contained Liquid + */ + @Override + public FluidStack getFluid() { + return null; + } + + /** + * tries to fill this Tank + */ + @Override + public int fill(FluidStack resource, boolean doFill) { + return 0; + } + + /** + * tries to empty this Tank + */ + @Override + public FluidStack drain(int maxDrain, boolean doDrain) { + return null; + } + + /** + * Tank pressure + */ + public int getTankPressure() { + return 0; + } + + /** + * Liquid Capacity + */ + @Override + public int getCapacity() { + return 0; + } + + /** + * Progress this machine has already made + */ + public int getProgresstime() { + return 0; + } + + /** + * Progress this Machine has to do to produce something + */ + public int maxProgresstime() { + return 0; + } + + /** + * Increases the Progress, returns the overflown Progress. + */ + public int increaseProgress(int aProgress) { + return 0; + } + + @Override + public void onMachineBlockUpdate() { + /* Do nothing */ + } + + @Override + public void receiveClientEvent(byte aEventID, byte aValue) { + /* Do nothing */ + } + + @Override + public boolean isSimpleMachine() { + return false; + } + + @Override + public byte getComparatorValue(ForgeDirection side) { + return 0; + } + + @Override + public boolean acceptsRotationalEnergy(ForgeDirection side) { + return false; + } + + @Override + public boolean injectRotationalEnergy(ForgeDirection side, long aSpeed, long aEnergy) { + return false; + } + + @Override + public String getSpecialVoltageToolTip() { + return null; + } + + @Override + public boolean isGivingInformation() { + return false; + } + + @Override + public String[] getInfoData() { + return new String[] {}; + } + + public boolean isDigitalChest() { + return false; + } + + public ItemStack[] getStoredItemData() { + return null; + } + + public void setItemCount(int aCount) { + /* Do nothing */ + } + + public int getMaxItemCount() { + return 0; + } + + @Override + public int getSizeInventory() { + return mInventory.length; + } + + @Override + public ItemStack getStackInSlot(int slotIndex) { + if (slotIndex >= 0 && slotIndex < mInventory.length) return mInventory[slotIndex]; + return null; + } + + @Override + public void setInventorySlotContents(int aIndex, ItemStack aStack) { + if (aIndex >= 0 && aIndex < mInventory.length) mInventory[aIndex] = aStack; + } + + @Override + public String getInventoryName() { + if (GregTech_API.METATILEENTITIES[getBaseMetaTileEntity().getMetaTileID()] != null) + return GregTech_API.METATILEENTITIES[getBaseMetaTileEntity().getMetaTileID()].getMetaName(); + return ""; + } + + @Override + public int getInventoryStackLimit() { + return 64; + } + + @Override + public boolean isItemValidForSlot(int aIndex, ItemStack aStack) { + return getBaseMetaTileEntity().isValidSlot(aIndex); + } + + @Override + public ItemStack decrStackSize(int aIndex, int aAmount) { + ItemStack tStack = getStackInSlot(aIndex), rStack = GT_Utility.copyOrNull(tStack); + if (tStack != null) { + if (tStack.stackSize <= aAmount) { + if (setStackToZeroInsteadOfNull(aIndex)) tStack.stackSize = 0; + else setInventorySlotContents(aIndex, null); + } else { + rStack = tStack.splitStack(aAmount); + if (tStack.stackSize == 0 && !setStackToZeroInsteadOfNull(aIndex)) + setInventorySlotContents(aIndex, null); + } + } + return rStack; + } + + @Override + public int[] getAccessibleSlotsFromSide(int ordinalSide) { + final TIntList tList = new TIntArrayList(); + final IGregTechTileEntity tTileEntity = getBaseMetaTileEntity(); + final CoverInfo tileCoverInfo = tTileEntity.getCoverInfoAtSide(ForgeDirection.getOrientation(ordinalSide)); + final boolean tSkip = tileCoverInfo.letsItemsIn(-2) || tileCoverInfo.letsItemsOut(-2); + for (int i = 0; i < getSizeInventory(); i++) { + if (isValidSlot(i) && (tSkip || tileCoverInfo.letsItemsOut(i) || tileCoverInfo.letsItemsIn(i))) { + tList.add(i); + } + } + return tList.toArray(); + } + + @Override + public boolean canInsertItem(int slotIndex, ItemStack itemStack, int ordinalSide) { + return isValidSlot(slotIndex) && itemStack != null + && slotIndex < mInventory.length + && (mInventory[slotIndex] == null || GT_Utility.areStacksEqual(itemStack, mInventory[slotIndex])) + && allowPutStack(getBaseMetaTileEntity(), slotIndex, ForgeDirection.getOrientation(ordinalSide), itemStack); + } + + @Override + public boolean canExtractItem(int slotIndex, ItemStack itemStack, int ordinalSide) { + return isValidSlot(slotIndex) && itemStack != null + && slotIndex < mInventory.length + && allowPullStack( + getBaseMetaTileEntity(), + slotIndex, + ForgeDirection.getOrientation(ordinalSide), + itemStack); + } + + @Override + public boolean canFill(ForgeDirection side, Fluid aFluid) { + return fill(side, new FluidStack(aFluid, 1), false) == 1; + } + + @Override + public boolean canDrain(ForgeDirection side, Fluid aFluid) { + return drain(side, new FluidStack(aFluid, 1), false) != null; + } + + @Override + public FluidTankInfo[] getTankInfo(ForgeDirection side) { + if (getCapacity() <= 0 && !getBaseMetaTileEntity().hasSteamEngineUpgrade()) return new FluidTankInfo[] {}; + return new FluidTankInfo[] { getInfo() }; + } + + public int fill_default(ForgeDirection side, FluidStack aFluid, boolean doFill) { + return fill(aFluid, doFill); + } + + @Override + public int fill(ForgeDirection side, FluidStack aFluid, boolean doFill) { + return fill_default(side, aFluid, doFill); + } + + @Override + public FluidStack drain(ForgeDirection side, FluidStack aFluid, boolean doDrain) { + if (getFluid() != null && aFluid != null && getFluid().isFluidEqual(aFluid)) + return drain(aFluid.amount, doDrain); + return null; + } + + @Override + public FluidStack drain(ForgeDirection side, int maxDrain, boolean doDrain) { + return drain(maxDrain, doDrain); + } + + @Override + public int getFluidAmount() { + return 0; + } + + @Override + public FluidTankInfo getInfo() { + return new FluidTankInfo(this); + } + + @Override + public String getMetaName() { + return mName; + } + + @Override + public ItemStack getStackInSlotOnClosing(int i) { + return null; + } + + @Override + public boolean doTickProfilingMessageDuringThisTick() { + return doTickProfilingInThisTick; + } + + @Override + public boolean isUseableByPlayer(EntityPlayer entityplayer) { + return false; + } + + @Override + public boolean connectsToItemPipe(ForgeDirection side) { + return false; + } + + @Override + public void openInventory() { + // + } + + @Override + public void closeInventory() { + // + } + + @Override + public Object getServerGUI(int aID, InventoryPlayer aPlayerInventory, IGregTechTileEntity aBaseMetaTileEntity) { + return null; + } + + @Override + public Object getClientGUI(int aID, InventoryPlayer aPlayerInventory, IGregTechTileEntity aBaseMetaTileEntity) { + return null; + } + + @Override + public float getExplosionResistance(ForgeDirection side) { + return (mConnections & IConnectable.HAS_FOAM) != 0 ? 50.0F : 5.0F; + } + + @Override + public ItemStack[] getRealInventory() { + return mInventory; + } + + @Override + public boolean hasCustomInventoryName() { + return false; + } + + @Override + public void markDirty() { + // + } + + @Override + public void onColorChangeServer(byte aColor) { + setCheckConnections(); + } + + @Override + public void onColorChangeClient(byte aColor) { + // Do nothing apparently + } + + public void setCheckConnections() { + mCheckConnections = true; + } + + public long injectEnergyUnits(ForgeDirection side, long aVoltage, long aAmperage) { + return 0; + } + + @Override + @SideOnly(Side.CLIENT) + public boolean renderInInventory(Block aBlock, int aMeta, RenderBlocks aRenderer) { + return false; + } + + @Override + @SideOnly(Side.CLIENT) + public boolean renderInWorld(IBlockAccess aWorld, int aX, int aY, int aZ, Block aBlock, RenderBlocks aRenderer) { + return false; + } + + @Override + public void doExplosion(long aExplosionPower) { + float tStrength = GT_Values.getExplosionPowerForVoltage(aExplosionPower); + int tX = getBaseMetaTileEntity().getXCoord(), tY = getBaseMetaTileEntity().getYCoord(), + tZ = getBaseMetaTileEntity().getZCoord(); + World tWorld = getBaseMetaTileEntity().getWorld(); + tWorld.setBlock(tX, tY, tZ, Blocks.air); + if (GregTech_API.sMachineExplosions) { + new WorldSpawnedEventBuilder.ExplosionEffectEventBuilder().setStrength(tStrength) + .setSmoking(true) + .setPosition(tX + 0.5, tY + 0.5, tZ + 0.5) + .setWorld(tWorld) + .run(); + } + } + + @Override + public int getLightOpacity() { + return 0; + } + + @Override + public void addCollisionBoxesToList(World aWorld, int aX, int aY, int aZ, AxisAlignedBB inputAABB, + List<AxisAlignedBB> outputAABB, Entity collider) { + AxisAlignedBB axisalignedbb1 = getCollisionBoundingBoxFromPool(aWorld, aX, aY, aZ); + if (axisalignedbb1 != null && inputAABB.intersectsWith(axisalignedbb1)) outputAABB.add(axisalignedbb1); + } + + @Override + public AxisAlignedBB getCollisionBoundingBoxFromPool(World aWorld, int aX, int aY, int aZ) { + return AxisAlignedBB.getBoundingBox(aX, aY, aZ, aX + 1, aY + 1, aZ + 1); + } + + @Override + public void onEntityCollidedWithBlock(World aWorld, int aX, int aY, int aZ, Entity collider) { + // + } + + @Override + public void onCreated(ItemStack aStack, World aWorld, EntityPlayer aPlayer) { + // + } + + @Override + public boolean allowGeneralRedstoneOutput() { + return false; + } + + @Override + public boolean hasAlternativeModeText() { + return false; + } + + @Override + public String getAlternativeModeText() { + return ""; + } + + @Deprecated + public String trans(String aKey, String aEnglish) { + return GT_Utility.trans(aKey, aEnglish); + } + + protected boolean connectableColor(TileEntity tTileEntity) { + // Determine if two entities are connectable based on their colorization: + // Uncolored can connect to anything + // If both are colored they must be the same color to connect. + if (tTileEntity instanceof IColoredTileEntity) { + if (getBaseMetaTileEntity().getColorization() >= 0) { + final byte tColor = ((IColoredTileEntity) tTileEntity).getColorization(); + return tColor < 0 || tColor == getBaseMetaTileEntity().getColorization(); + } + } + + return true; + } + + @Override + public int connect(ForgeDirection side) { + if (side == ForgeDirection.UNKNOWN) return 0; + + final ForgeDirection oppositeSide = side.getOpposite(); + final IGregTechTileEntity baseMetaTile = getBaseMetaTileEntity(); + if (baseMetaTile == null || !baseMetaTile.isServerSide()) return 0; + + final CoverInfo coverInfo = baseMetaTile.getCoverInfoAtSide(side); + + final boolean alwaysLookConnected = coverInfo.alwaysLookConnected(); + final boolean letsIn = letsIn(coverInfo); + final boolean letsOut = letsOut(coverInfo); + + // Careful - tTileEntity might be null, and that's ok -- so handle it + final TileEntity tTileEntity = baseMetaTile.getTileEntityAtSide(side); + if (!connectableColor(tTileEntity)) return 0; + + if ((alwaysLookConnected || letsIn || letsOut)) { + // Are we trying to connect to a pipe? let's do it! + final IMetaTileEntity tPipe = tTileEntity instanceof IGregTechTileEntity + ? ((IGregTechTileEntity) tTileEntity).getMetaTileEntity() + : null; + if (getClass().isInstance(tPipe) || (tPipe != null && tPipe.getClass() + .isInstance(this))) { + connectAtSide(side); + if (!((IConnectable) tPipe).isConnectedAtSide(oppositeSide)) { + // Make sure pipes all get together -- connect back to us if we're connecting to a pipe + ((IConnectable) tPipe).connect(oppositeSide); + } + return 1; + } else if ((getGT6StyleConnection() && baseMetaTile.getAirAtSide(side)) || canConnect(side, tTileEntity)) { + // Allow open connections to Air, if the GT6 style pipe/cables are enabled, so that it'll connect to + // the next block placed down next to it + connectAtSide(side); + return 1; + } + if (!baseMetaTile.getWorld() + .getChunkProvider() + .chunkExists(baseMetaTile.getOffsetX(side, 1) >> 4, baseMetaTile.getOffsetZ(side, 1) >> 4)) { + // Target chunk unloaded + return -1; + } + } + return 0; + } + + protected void checkConnections() { + // Verify connections around us. If GT6 style cables are not enabled then revert to old behavior and try + // connecting to everything around us + for (ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) { + if ((!getGT6StyleConnection() || isConnectedAtSide(side)) && connect(side) == 0) { + disconnect(side); + } + } + mCheckConnections = false; + } + + private void connectAtSide(ForgeDirection side) { + mConnections |= side.flag; + } + + @Override + public void disconnect(ForgeDirection side) { + if (side == ForgeDirection.UNKNOWN) return; + mConnections &= ~side.flag; + final ForgeDirection oppositeSide = side.getOpposite(); + IGregTechTileEntity tTileEntity = getBaseMetaTileEntity().getIGregTechTileEntityAtSide(side); + IMetaTileEntity tPipe = tTileEntity == null ? null : tTileEntity.getMetaTileEntity(); + if ((this.getClass() + .isInstance(tPipe) + || (tPipe != null && tPipe.getClass() + .isInstance(this))) + && ((IConnectable) tPipe).isConnectedAtSide(oppositeSide)) { + ((IConnectable) tPipe).disconnect(oppositeSide); + } + } + + @Override + public boolean isConnectedAtSide(ForgeDirection sideDirection) { + return (mConnections & sideDirection.flag) != 0; + } + + public boolean letsIn(GT_CoverBehavior coverBehavior, ForgeDirection side, int aCoverID, int aCoverVariable, + ICoverable aTileEntity) { + return false; + } + + public boolean letsIn(CoverInfo coverInfo) { + return false; + } + + public boolean letsOut(GT_CoverBehavior coverBehavior, ForgeDirection side, int aCoverID, int aCoverVariable, + ICoverable aTileEntity) { + return false; + } + + public boolean letsOut(CoverInfo coverInfo) { + return false; + } + + public boolean letsIn(GT_CoverBehaviorBase<?> coverBehavior, ForgeDirection side, int aCoverID, + ISerializableObject aCoverVariable, ICoverable aTileEntity) { + return false; + } + + public boolean letsOut(GT_CoverBehaviorBase<?> coverBehavior, ForgeDirection side, int aCoverID, + ISerializableObject aCoverVariable, ICoverable aTileEntity) { + return false; + } + + public boolean canConnect(ForgeDirection side, TileEntity tTileEntity) { + return false; + } + + public boolean getGT6StyleConnection() { + return false; + } + + @Override + public boolean shouldJoinIc2Enet() { + return false; + } + + @Override + public boolean isMachineBlockUpdateRecursive() { + return false; + } + + public void reloadLocks() {} + + @Override + public int getGUIColorization() { + Dyes dye = Dyes.dyeWhite; + if (GregTech_API.sColoredGUI) { + if (GregTech_API.sMachineMetalGUI) { + dye = Dyes.MACHINE_METAL; + } else if (getBaseMetaTileEntity() != null) { + dye = Dyes.getDyeFromIndex(getBaseMetaTileEntity().getColorization()); + } + } + return GT_Util.getRGBInt(dye.getRGBA()); + } +} diff --git a/src/main/java/gregtech/api/metatileentity/MetaTileEntity.java b/src/main/java/gregtech/api/metatileentity/MetaTileEntity.java new file mode 100644 index 0000000000..0a56f8467f --- /dev/null +++ b/src/main/java/gregtech/api/metatileentity/MetaTileEntity.java @@ -0,0 +1,1326 @@ +package gregtech.api.metatileentity; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.function.Supplier; + +import net.minecraft.block.Block; +import net.minecraft.client.renderer.RenderBlocks; +import net.minecraft.client.renderer.texture.IIconRegister; +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.init.Blocks; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.AxisAlignedBB; +import net.minecraft.world.IBlockAccess; +import net.minecraft.world.World; +import net.minecraftforge.common.util.ForgeDirection; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.FluidTankInfo; + +import org.jetbrains.annotations.Nullable; + +import com.gtnewhorizons.modularui.api.forge.ItemStackHandler; + +import appeng.api.implementations.IPowerChannelState; +import appeng.api.networking.energy.IEnergyGrid; +import appeng.api.networking.pathing.IPathingGrid; +import appeng.api.util.AECableType; +import appeng.core.localization.WailaText; +import appeng.me.helpers.AENetworkProxy; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import gnu.trove.list.TIntList; +import gnu.trove.list.array.TIntArrayList; +import gregtech.api.GregTech_API; +import gregtech.api.enums.Dyes; +import gregtech.api.enums.GT_Values; +import gregtech.api.enums.SoundResource; +import gregtech.api.enums.SteamVariant; +import gregtech.api.gui.GT_GUIColorOverride; +import gregtech.api.gui.modularui.GUITextureSet; +import gregtech.api.interfaces.ICleanroom; +import gregtech.api.interfaces.ICleanroomReceiver; +import gregtech.api.interfaces.IConfigurationCircuitSupport; +import gregtech.api.interfaces.metatileentity.IMetaTileEntity; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.metatileentity.implementations.GT_MetaPipeEntity_Cable; +import gregtech.api.objects.GT_ItemStack; +import gregtech.api.util.GT_Config; +import gregtech.api.util.GT_LanguageManager; +import gregtech.api.util.GT_Log; +import gregtech.api.util.GT_ModHandler; +import gregtech.api.util.GT_TooltipDataCache; +import gregtech.api.util.GT_Util; +import gregtech.api.util.GT_Utility; +import gregtech.common.GT_Client; +import gregtech.common.covers.CoverInfo; +import mcp.mobius.waila.api.IWailaConfigHandler; +import mcp.mobius.waila.api.IWailaDataAccessor; + +/** + * NEVER INCLUDE THIS FILE IN YOUR MOD!!! + * <p/> + * Extend this Class to add a new MetaMachine Call the Constructor with the desired ID at the load-phase (not preload + * and also not postload!) Implement the newMetaEntity-Method to return a new ready instance of your MetaTileEntity + * <p/> + * Call the Constructor like the following example inside the Load Phase, to register it. "new + * GT_MetaTileEntity_E_Furnace(54, "GT_E_Furnace", "Automatic E-Furnace");" + */ +@SuppressWarnings("unused") +public abstract class MetaTileEntity implements IMetaTileEntity, ICleanroomReceiver { + + /** + * Only assigned for the MetaTileEntity in the List! Also only used to get the localized Name for the ItemStack and + * for getInvName. + */ + public final String mName; + /** + * The Inventory of the MetaTileEntity. Amount of Slots can be larger than 256. HAYO! + */ + public final ItemStack[] mInventory; + + /** + * Inventory wrapper for ModularUI + */ + public final ItemStackHandler inventoryHandler; + + protected GT_GUIColorOverride colorOverride; + protected GT_TooltipDataCache mTooltipCache = new GT_TooltipDataCache(); + + @Override + public ItemStackHandler getInventoryHandler() { + return inventoryHandler; + } + + public boolean doTickProfilingInThisTick = true; + + private ICleanroom cleanroom; + + /** + * accessibility to this Field is no longer given, see below + */ + private IGregTechTileEntity mBaseMetaTileEntity; + + public long mSoundRequests = 0; + + /** + * This registers your Machine at the List. Use only ID's larger than 2048 - the ones lower are reserved by GT. + * See also the list in the API package - it has a description that contains all the reservations. + * <p> + * The constructor can be overloaded as follows: + * <blockquote> + * + * <pre> + * + * public GT_MetaTileEntity_EBench(int id, String name, String nameRegional) { + * super(id, name, nameRegional); + * } + * </pre> + * + * </blockquote> + * + * @param aID the machine ID + */ + public MetaTileEntity(int aID, String aBasicName, String aRegionalName, int aInvSlotCount) { + if (GregTech_API.sPostloadStarted || !GregTech_API.sPreloadStarted) + throw new IllegalAccessError("This Constructor has to be called in the load Phase"); + if (GregTech_API.METATILEENTITIES[aID] == null) { + GregTech_API.METATILEENTITIES[aID] = this; + } else { + throw new IllegalArgumentException("MetaMachine-Slot Nr. " + aID + " is already occupied!"); + } + mName = aBasicName.replace(" ", "_") + .toLowerCase(Locale.ENGLISH); + setBaseMetaTileEntity(GregTech_API.constructBaseMetaTileEntity()); + getBaseMetaTileEntity().setMetaTileID((short) aID); + GT_LanguageManager.addStringLocalization("gt.blockmachines." + mName + ".name", aRegionalName); + mInventory = new ItemStack[aInvSlotCount]; + inventoryHandler = new ItemStackHandler(mInventory); + } + + /** + * This is the normal Constructor. + */ + public MetaTileEntity(String aName, int aInvSlotCount) { + mInventory = new ItemStack[aInvSlotCount]; + mName = aName; + inventoryHandler = new ItemStackHandler(mInventory); + colorOverride = GT_GUIColorOverride.get(getGUITextureSet().getMainBackground().location); + } + + /** + * This method will only be called on client side + * + * @return whether the secondary description should be display. default is false + */ + @Deprecated + public boolean isDisplaySecondaryDescription() { + return false; + } + + @Override + public IGregTechTileEntity getBaseMetaTileEntity() { + return mBaseMetaTileEntity; + } + + @Override + public void setBaseMetaTileEntity(IGregTechTileEntity aBaseMetaTileEntity) { + if (mBaseMetaTileEntity != null && aBaseMetaTileEntity == null) { + mBaseMetaTileEntity.getMetaTileEntity() + .inValidate(); + mBaseMetaTileEntity.setMetaTileEntity(null); + } + mBaseMetaTileEntity = aBaseMetaTileEntity; + if (mBaseMetaTileEntity != null) { + mBaseMetaTileEntity.setMetaTileEntity(this); + } + } + + public boolean isValid() { + return getBaseMetaTileEntity() != null && getBaseMetaTileEntity().getMetaTileEntity() == this + && !getBaseMetaTileEntity().isDead(); + } + + @Override + public ItemStack getStackForm(long aAmount) { + return new ItemStack(GregTech_API.sBlockMachines, (int) aAmount, getBaseMetaTileEntity().getMetaTileID()); + } + + @Override + public String getLocalName() { + return GT_LanguageManager.getTranslation("gt.blockmachines." + mName + ".name"); + } + + @Override + public void onServerStart() { + /* Do nothing */ + } + + @Override + public void onWorldSave(File aSaveDirectory) { + /* Do nothing */ + } + + @Override + public void onWorldLoad(File aSaveDirectory) { + /* Do nothing */ + } + + @Override + public void onConfigLoad(GT_Config aConfig) { + /* Do nothing */ + } + + @Override + public void setItemNBT(NBTTagCompound aNBT) { + /* Do nothing */ + } + + @Override + @SideOnly(Side.CLIENT) + public void registerIcons(IIconRegister aBlockIconRegister) { + /* Do nothing */ + } + + @Override + public boolean allowCoverOnSide(ForgeDirection side, GT_ItemStack aStack) { + return true; + } + + @Override + public void onScrewdriverRightClick(ForgeDirection side, EntityPlayer aPlayer, float aX, float aY, float aZ, + ItemStack aTool) { + onScrewdriverRightClick(side, aPlayer, aX, aY, aZ); + } + + @Override + public boolean onWrenchRightClick(ForgeDirection side, ForgeDirection wrenchingSide, EntityPlayer entityPlayer, + float aX, float aY, float aZ, ItemStack aTool) { + + // glue + if (onWrenchRightClick(side, wrenchingSide, entityPlayer, aX, aY, aZ)) { + return true; + } + if (getBaseMetaTileEntity().isValidFacing(wrenchingSide)) { + getBaseMetaTileEntity().setFrontFacing(wrenchingSide); + return true; + } + return false; + } + + @Override + public boolean onWireCutterRightClick(ForgeDirection side, ForgeDirection wrenchingSide, EntityPlayer aPlayer, + float aX, float aY, float aZ, ItemStack aTool) { + // glue + if (onWireCutterRightClick(side, wrenchingSide, aPlayer, aX, aY, aZ)) { + return true; + } + if (!aPlayer.isSneaking()) return false; + final ForgeDirection oppositeSide = wrenchingSide.getOpposite(); + final TileEntity tTileEntity = getBaseMetaTileEntity().getTileEntityAtSide(wrenchingSide); + if ((tTileEntity instanceof IGregTechTileEntity gtTE) + && (gtTE.getMetaTileEntity() instanceof GT_MetaPipeEntity_Cable)) { + + // The tile entity we're facing is a cable, let's try to connect to it + return gtTE.getMetaTileEntity() + .onWireCutterRightClick(wrenchingSide, oppositeSide, aPlayer, aX, aY, aZ, aTool); + } + return false; + } + + @Override + public boolean onSolderingToolRightClick(ForgeDirection side, ForgeDirection wrenchingSide, EntityPlayer aPlayer, + float aX, float aY, float aZ, ItemStack aTool) { + + // glue + if (onSolderingToolRightClick(side, wrenchingSide, aPlayer, aX, aY, aZ)) { + return true; + } + + if (!aPlayer.isSneaking()) return false; + final ForgeDirection oppositeSide = wrenchingSide.getOpposite(); + TileEntity tTileEntity = getBaseMetaTileEntity().getTileEntityAtSide(wrenchingSide); + if ((tTileEntity instanceof IGregTechTileEntity gtTE) + && (gtTE.getMetaTileEntity() instanceof GT_MetaPipeEntity_Cable)) { + + // The tile entity we're facing is a cable, let's try to connect to it + return gtTE.getMetaTileEntity() + .onSolderingToolRightClick(wrenchingSide, oppositeSide, aPlayer, aX, aY, aZ, aTool); + } + return false; + } + + @Deprecated + public boolean onSolderingToolRightClick(ForgeDirection side, ForgeDirection wrenchingSide, EntityPlayer aPlayer, + float aX, float aY, float aZ) { + return false; + } + + @Deprecated + public boolean onWireCutterRightClick(ForgeDirection side, ForgeDirection wrenchingSide, EntityPlayer aPlayer, + float aX, float aY, float aZ) { + return false; + } + + @Deprecated + public boolean onWrenchRightClick(ForgeDirection side, ForgeDirection wrenchingSide, EntityPlayer aPlayer, float aX, + float aY, float aZ) { + return false; + } + + @Deprecated + public void onScrewdriverRightClick(ForgeDirection side, EntityPlayer aPlayer, float aX, float aY, float aZ) { + + } + + @Override + public void onExplosion() { + GT_Log.exp.println( + "Machine at " + this.getBaseMetaTileEntity() + .getXCoord() + + " | " + + this.getBaseMetaTileEntity() + .getYCoord() + + " | " + + this.getBaseMetaTileEntity() + .getZCoord() + + " DIMID: " + + this.getBaseMetaTileEntity() + .getWorld().provider.dimensionId + + " exploded."); + } + + @Override + public void onFirstTick(IGregTechTileEntity aBaseMetaTileEntity) { + /* Do nothing */ + } + + @Override + public void onPreTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) { + /* Do nothing */ + } + + @Override + public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) { + if (aBaseMetaTileEntity.isClientSide() && GT_Client.changeDetected == 4) { + /* + * Client tick counter that is set to 5 on hiding pipes and covers. It triggers a texture update next client + * tick when reaching 4, with provision for 3 more update tasks, spreading client change detection related + * work and network traffic on different ticks, until it reaches 0. + */ + aBaseMetaTileEntity.issueTextureUpdate(); + } + } + + public void onTickFail(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {} + + public void onSetActive(boolean active) {} + + public void onDisableWorking() {} + + @Override + public void inValidate() { + /* Do nothing */ + } + + @Override + public void onRemoval() { + /* Do nothing */ + } + + @Override + public void initDefaultModes(NBTTagCompound aNBT) { + /* Do nothing */ + } + + /** + * When a GUI is opened + */ + public void onOpenGUI() { + /* Do nothing */ + } + + /** + * When a GUI is closed + */ + public void onCloseGUI() { + /* Do nothing */ + } + + /** + * a Player rightclicks the Machine Sneaky rightclicks are not getting passed to this! + */ + public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer) { + return false; + } + + @Override + public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer, ForgeDirection side, + float aX, float aY, float aZ) { + return onRightclick(aBaseMetaTileEntity, aPlayer); + } + + @Override + public void onLeftclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer) { + /* Do nothing */ + } + + @Override + public void onValueUpdate(byte aValue) { + /* Do nothing */ + } + + @Override + public byte getUpdateData() { + return 0; + } + + @Override + public void doSound(byte aIndex, double aX, double aY, double aZ) { + /* Do nothing */ + } + + @Override + public void startSoundLoop(byte aIndex, double aX, double aY, double aZ) { + /* Do nothing */ + } + + @Override + public void stopSoundLoop(byte aValue, double aX, double aY, double aZ) { + /* Do nothing */ + } + + @Nullable + @Override + public ICleanroom getCleanroom() { + return cleanroom; + } + + @Override + public void setCleanroom(ICleanroom cleanroom) { + this.cleanroom = cleanroom; + } + + @Override + public final void sendSound(byte aIndex) { + if (!getBaseMetaTileEntity().hasMufflerUpgrade()) + getBaseMetaTileEntity().sendBlockEvent(GregTechTileClientEvents.DO_SOUND, aIndex); + } + + @Override + public final void sendLoopStart(byte aIndex) { + if (!getBaseMetaTileEntity().hasMufflerUpgrade()) + getBaseMetaTileEntity().sendBlockEvent(GregTechTileClientEvents.START_SOUND_LOOP, aIndex); + mSoundRequests++; + } + + @Override + public final void sendLoopEnd(byte aIndex) { + if (!getBaseMetaTileEntity().hasMufflerUpgrade()) + getBaseMetaTileEntity().sendBlockEvent(GregTechTileClientEvents.STOP_SOUND_LOOP, aIndex); + } + + /** + * @return true if this Device emits Energy at all + */ + public boolean isElectric() { + return true; + } + + /** + * @return true if this Device emits Energy at all + */ + public boolean isPneumatic() { + return false; + } + + /** + * @return true if this Device emits Energy at all + */ + public boolean isSteampowered() { + return false; + } + + /** + * @return what type of texture does this machine use for GUI, i.e. Bronze, Steel, or Primitive + */ + public SteamVariant getSteamVariant() { + return SteamVariant.NONE; + } + + /** + * @return true if this Device emits Energy at all + */ + public boolean isEnetOutput() { + return false; + } + + /** + * @return true if this Device consumes Energy at all + */ + public boolean isEnetInput() { + return false; + } + + /** + * @return the amount of EU, which can be stored in this Device. Default is 0 EU. + */ + public long maxEUStore() { + return 0; + } + + /** + * @return the amount of EU/t, which can be accepted by this Device before it explodes. + */ + public long maxEUInput() { + return 0; + } + + /** + * @return the amount of EU/t, which can be outputted by this Device. + */ + public long maxEUOutput() { + return 0; + } + + /** + * @return the amount of E-net Impulses of the maxEUOutput size, which can be outputted by this Device. Default is 1 + * Pulse, this shouldn't be set to smaller Values than 1, as it won't output anything in that Case! + */ + public long maxAmperesOut() { + return 1; + } + + /** + * How many Amperes this Block can suck at max. Surpassing this value won't blow it up. + */ + public long maxAmperesIn() { + return 1; + } + + /** + * @return true if that Side is an Output. + */ + public boolean isOutputFacing(ForgeDirection side) { + return false; + } + + /** + * @return true if that Side is an Input. + */ + public boolean isInputFacing(ForgeDirection side) { + return false; + } + + /** + * @return true if Transformer Upgrades increase Packet Amount. + */ + public boolean isTransformingLowEnergy() { + return true; + } + + @Override + public boolean isFacingValid(ForgeDirection facing) { + return false; + } + + @Override + public boolean isAccessAllowed(EntityPlayer aPlayer) { + return false; + } + + @Override + public boolean isValidSlot(int aIndex) { + return true; + } + + @Override + public boolean shouldDropItemAt(int index) { + return true; + } + + @Override + public boolean setStackToZeroInsteadOfNull(int aIndex) { + return false; + } + + /** + * This is used to get the internal Energy. I use this for the IDSU. + */ + public long getEUVar() { + return ((BaseMetaTileEntity) mBaseMetaTileEntity).mStoredEnergy; + } + + /** + * This is used to set the internal Energy to the given Parameter. I use this for the IDSU. + */ + public void setEUVar(long aEnergy) { + if (aEnergy != ((BaseMetaTileEntity) mBaseMetaTileEntity).mStoredEnergy) { + markDirty(); + ((BaseMetaTileEntity) mBaseMetaTileEntity).mStoredEnergy = aEnergy; + } + } + + /** + * This is used to get the internal Steam Energy. + */ + public long getSteamVar() { + return ((BaseMetaTileEntity) mBaseMetaTileEntity).mStoredSteam; + } + + /** + * This is used to set the internal Steam Energy to the given Parameter. + */ + public void setSteamVar(long aSteam) { + if (((BaseMetaTileEntity) mBaseMetaTileEntity).mStoredSteam != aSteam) { + markDirty(); + ((BaseMetaTileEntity) mBaseMetaTileEntity).mStoredSteam = aSteam; + } + } + + /** + * @return the amount of Steam, which can be stored in this Device. Default is 0 EU. + */ + public long maxSteamStore() { + return 0; + } + + /** + * @return the amount of EU, which this Device stores before starting to emit Energy. useful if you don't want to + * emit stored Energy until a certain Level is reached. + */ + public long getMinimumStoredEU() { + return 512; + } + + /** + * Determines the Tier of the Machine, used for de-charging Tools. + */ + public long getInputTier() { + return GT_Utility.getTier(getBaseMetaTileEntity().getInputVoltage()); + } + + /** + * Determines the Tier of the Machine, used for charging Tools. + */ + public long getOutputTier() { + return GT_Utility.getTier(getBaseMetaTileEntity().getOutputVoltage()); + } + + /** + * gets the first RechargerSlot + */ + public int rechargerSlotStartIndex() { + return 0; + } + + /** + * gets the amount of RechargerSlots + */ + public int rechargerSlotCount() { + return 0; + } + + /** + * gets the first DechargerSlot + */ + public int dechargerSlotStartIndex() { + return 0; + } + + /** + * gets the amount of DechargerSlots + */ + public int dechargerSlotCount() { + return 0; + } + + /** + * gets if this is protected from other Players per default or not + */ + public boolean ownerControl() { + return false; + } + + @Override + public ArrayList<String> getSpecialDebugInfo(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer, + int aLogLevel, ArrayList<String> aList) { + return aList; + } + + @Override + public boolean isLiquidInput(ForgeDirection side) { + return true; + } + + @Override + public boolean isLiquidOutput(ForgeDirection side) { + return true; + } + + /** + * gets the contained Liquid + */ + @Override + public FluidStack getFluid() { + return null; + } + + /** + * tries to fill this Tank + */ + @Override + public int fill(FluidStack resource, boolean doFill) { + return 0; + } + + /** + * tries to empty this Tank + */ + @Override + public FluidStack drain(int maxDrain, boolean doDrain) { + return null; + } + + /** + * Tank pressure + */ + public int getTankPressure() { + return 0; + } + + /** + * Liquid Capacity + */ + @Override + public int getCapacity() { + return 0; + } + + /** + * Actual fluid capacity. If your machine has void-overflow feature, you'll want to override this method to make + * sure correct capacity is shown on GUI. + */ + public int getRealCapacity() { + return getCapacity(); + } + + @Override + public void onMachineBlockUpdate() { + /* Do nothing */ + } + + @Override + public void receiveClientEvent(byte aEventID, byte aValue) { + /* Do nothing */ + } + + @Override + public boolean isSimpleMachine() { + return false; + } + + /** + * If this accepts up to 4 Overclockers + */ + public boolean isOverclockerUpgradable() { + return false; + } + + /** + * If this accepts Transformer Upgrades + */ + public boolean isTransformerUpgradable() { + return false; + } + + /** + * Progress this machine has already made + */ + public int getProgresstime() { + return 0; + } + + /** + * Progress this Machine has to do to produce something + */ + public int maxProgresstime() { + return 0; + } + + /** + * Increases the Progress, returns the overflown Progress. + */ + public int increaseProgress(int aProgress) { + return 0; + } + + /** + * If this TileEntity makes use of Sided Redstone behaviors. Determines only, if the Output Redstone Array is + * getting filled with 0 for true, or 15 for false. + */ + public boolean hasSidedRedstoneOutputBehavior() { + return false; + } + + /** + * When the Facing gets changed. + */ + public void onFacingChange() { + /* Do nothing */ + } + + /** + * if the IC2 Teleporter can drain from this. + */ + public boolean isTeleporterCompatible() { + return isEnetOutput() && getBaseMetaTileEntity().getOutputVoltage() >= 128 + && getBaseMetaTileEntity().getUniversalEnergyCapacity() >= 500000; + } + + /** + * Flag if this MTE will exploding when its raining + * + * @return True if this will explode in rain, else false + */ + public boolean willExplodeInRain() { + return true; + } + + /** + * Gets the Output for the comparator on the given Side + */ + @Override + public byte getComparatorValue(ForgeDirection side) { + return 0; + } + + @Override + public boolean acceptsRotationalEnergy(ForgeDirection side) { + return false; + } + + @Override + public boolean injectRotationalEnergy(ForgeDirection side, long aSpeed, long aEnergy) { + return false; + } + + @Override + public String getSpecialVoltageToolTip() { + return null; + } + + @Override + public boolean isGivingInformation() { + return false; + } + + @Override + public String[] getInfoData() { + return new String[] {}; + } + + public boolean isDigitalChest() { + return false; + } + + public ItemStack[] getStoredItemData() { + return null; + } + + public void setItemCount(int aCount) { + /* Do nothing */ + } + + public int getMaxItemCount() { + return 0; + } + + @Override + public int getSizeInventory() { + return mInventory.length; + } + + @Override + public ItemStack getStackInSlot(int aIndex) { + if (aIndex >= 0 && aIndex < mInventory.length) return mInventory[aIndex]; + return null; + } + + @Override + public void setInventorySlotContents(int aIndex, ItemStack aStack) { + markDirty(); + if (this instanceof IConfigurationCircuitSupport ccs) { + if (ccs.allowSelectCircuit() && aIndex == ccs.getCircuitSlot() && aStack != null) { + mInventory[aIndex] = GT_Utility.copyAmount(0, aStack); + return; + } + } + if (aIndex >= 0 && aIndex < mInventory.length) mInventory[aIndex] = aStack; + } + + @Override + public String getInventoryName() { + if (GregTech_API.METATILEENTITIES[getBaseMetaTileEntity().getMetaTileID()] != null) + return GregTech_API.METATILEENTITIES[getBaseMetaTileEntity().getMetaTileID()].getMetaName(); + return ""; + } + + @Override + public int getInventoryStackLimit() { + return 64; + } + + @Override + public boolean isItemValidForSlot(int aIndex, ItemStack aStack) { + return getBaseMetaTileEntity().isValidSlot(aIndex); + } + + @Override + public ItemStack decrStackSize(int aIndex, int aAmount) { + ItemStack tStack = getStackInSlot(aIndex), rStack = GT_Utility.copyOrNull(tStack); + if (tStack != null) { + if (tStack.stackSize <= aAmount) { + if (setStackToZeroInsteadOfNull(aIndex)) { + tStack.stackSize = 0; + markDirty(); + } else setInventorySlotContents(aIndex, null); + } else { + rStack = tStack.splitStack(aAmount); + markDirty(); + if (tStack.stackSize == 0 && !setStackToZeroInsteadOfNull(aIndex)) + setInventorySlotContents(aIndex, null); + } + } + return rStack; + } + + @Override + public int[] getAccessibleSlotsFromSide(int ordinalSide) { + final TIntList tList = new TIntArrayList(); + final IGregTechTileEntity tTileEntity = getBaseMetaTileEntity(); + final CoverInfo tileCoverInfo = tTileEntity.getCoverInfoAtSide(ForgeDirection.getOrientation(ordinalSide)); + final boolean tSkip = tileCoverInfo.letsItemsIn(-2) || tileCoverInfo.letsItemsOut(-2); + for (int i = 0; i < getSizeInventory(); i++) { + if (isValidSlot(i) && (tSkip || tileCoverInfo.letsItemsOut(i) || tileCoverInfo.letsItemsIn(i))) { + tList.add(i); + } + } + return tList.toArray(); + } + + @Override + public boolean canInsertItem(int aIndex, ItemStack aStack, int ordinalSide) { + return isValidSlot(aIndex) && aStack != null + && aIndex < mInventory.length + && (mInventory[aIndex] == null || GT_Utility.areStacksEqual(aStack, mInventory[aIndex])) + && allowPutStack(getBaseMetaTileEntity(), aIndex, ForgeDirection.getOrientation(ordinalSide), aStack); + } + + @Override + public boolean canExtractItem(int aIndex, ItemStack aStack, int ordinalSide) { + return isValidSlot(aIndex) && aStack != null + && aIndex < mInventory.length + && allowPullStack(getBaseMetaTileEntity(), aIndex, ForgeDirection.getOrientation(ordinalSide), aStack); + } + + @Override + public boolean canFill(ForgeDirection side, Fluid aFluid) { + return fill(side, new FluidStack(aFluid, 1), false) == 1; + } + + @Override + public boolean canDrain(ForgeDirection side, Fluid aFluid) { + return drain(side, new FluidStack(aFluid, 1), false) != null; + } + + @Override + public FluidTankInfo[] getTankInfo(ForgeDirection side) { + if (getCapacity() <= 0 && !getBaseMetaTileEntity().hasSteamEngineUpgrade()) return new FluidTankInfo[] {}; + return new FluidTankInfo[] { getInfo() }; + } + + public int fill_default(ForgeDirection side, FluidStack aFluid, boolean doFill) { + int filled = fill(aFluid, doFill); + if (filled > 0) { + markDirty(); + } + return filled; + } + + @Override + public int fill(ForgeDirection side, FluidStack aFluid, boolean doFill) { + if (getBaseMetaTileEntity().hasSteamEngineUpgrade() && GT_ModHandler.isSteam(aFluid) && aFluid.amount > 1) { + int tSteam = (int) Math.min( + Integer.MAX_VALUE, + Math.min( + aFluid.amount / 2, + getBaseMetaTileEntity().getSteamCapacity() - getBaseMetaTileEntity().getStoredSteam())); + if (tSteam > 0) { + markDirty(); + if (doFill) getBaseMetaTileEntity().increaseStoredSteam(tSteam, true); + return tSteam * 2; + } + } else { + return fill_default(side, aFluid, doFill); + } + return 0; + } + + @Override + public FluidStack drain(ForgeDirection side, FluidStack aFluid, boolean doDrain) { + if (getFluid() != null && aFluid != null && getFluid().isFluidEqual(aFluid)) + return drain(aFluid.amount, doDrain); + return null; + } + + @Override + public FluidStack drain(ForgeDirection side, int maxDrain, boolean doDrain) { + return drain(maxDrain, doDrain); + } + + @Override + public int getFluidAmount() { + return 0; + } + + @Override + public FluidTankInfo getInfo() { + return new FluidTankInfo(this); + } + + @Override + public String getMetaName() { + return mName; + } + + @Override + public ItemStack getStackInSlotOnClosing(int i) { + return null; + } + + @Override + public boolean hasCustomInventoryName() { + return false; + } + + @Override + public boolean doTickProfilingMessageDuringThisTick() { + return doTickProfilingInThisTick; + } + + @Override + public void markDirty() { + if (mBaseMetaTileEntity != null) { + mBaseMetaTileEntity.markDirty(); + } + } + + @Override + public boolean isUseableByPlayer(EntityPlayer entityplayer) { + return false; + } + + @Override + public void openInventory() { + // + } + + @Override + public void closeInventory() { + // + } + + /** + * @deprecated Use ModularUI + */ + @Deprecated + @Override + public Object getServerGUI(int aID, InventoryPlayer aPlayerInventory, IGregTechTileEntity aBaseMetaTileEntity) { + return null; + } + + /** + * @deprecated Use ModularUI + */ + @Deprecated + @Override + public Object getClientGUI(int aID, InventoryPlayer aPlayerInventory, IGregTechTileEntity aBaseMetaTileEntity) { + return null; + } + + @Override + public boolean connectsToItemPipe(ForgeDirection side) { + return false; + } + + @Override + public float getExplosionResistance(ForgeDirection side) { + return 10.0F; + } + + @Override + public ItemStack[] getRealInventory() { + return mInventory; + } + + @Override + public void onColorChangeServer(byte aColor) { + final IGregTechTileEntity meta = getBaseMetaTileEntity(); + final int aX = meta.getXCoord(), aY = meta.getYCoord(), aZ = meta.getZCoord(); + for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) { + // Flag surrounding pipes/cables to revaluate their connection with us if we got painted + final TileEntity tTileEntity = meta.getTileEntityAtSide(side); + if (tTileEntity instanceof BaseMetaPipeEntity pipe) { + pipe.onNeighborBlockChange(aX, aY, aZ); + } + } + } + + @Override + public void onColorChangeClient(byte aColor) { + // Do nothing apparently + } + + @Override + @SideOnly(Side.CLIENT) + public boolean renderInInventory(Block aBlock, int aMeta, RenderBlocks aRenderer) { + return false; + } + + @Override + @SideOnly(Side.CLIENT) + public boolean renderInWorld(IBlockAccess aWorld, int aX, int aY, int aZ, Block aBlock, RenderBlocks aRenderer) { + return false; + } + + @Override + public void doExplosion(long aExplosionPower) { + float tStrength = GT_Values.getExplosionPowerForVoltage(aExplosionPower); + final int tX = getBaseMetaTileEntity().getXCoord(); + final int tY = getBaseMetaTileEntity().getYCoord(); + final int tZ = getBaseMetaTileEntity().getZCoord(); + final World tWorld = getBaseMetaTileEntity().getWorld(); + GT_Utility.sendSoundToPlayers(tWorld, SoundResource.IC2_MACHINES_MACHINE_OVERLOAD, 1.0F, -1, tX, tY, tZ); + tWorld.setBlock(tX, tY, tZ, Blocks.air); + if (GregTech_API.sMachineExplosions) + tWorld.createExplosion(null, tX + 0.5, tY + 0.5, tZ + 0.5, tStrength, true); + } + + @Override + public int getLightOpacity() { + return ((BaseMetaTileEntity) getBaseMetaTileEntity()).getLightValue() > 0 ? 0 : 255; + } + + @Override + public void addCollisionBoxesToList(World aWorld, int aX, int aY, int aZ, AxisAlignedBB inputAABB, + List<AxisAlignedBB> outputAABB, Entity collider) { + AxisAlignedBB axisalignedbb1 = getCollisionBoundingBoxFromPool(aWorld, aX, aY, aZ); + if (axisalignedbb1 != null && inputAABB.intersectsWith(axisalignedbb1)) outputAABB.add(axisalignedbb1); + } + + @Override + public AxisAlignedBB getCollisionBoundingBoxFromPool(World aWorld, int aX, int aY, int aZ) { + return AxisAlignedBB.getBoundingBox(aX, aY, aZ, aX + 1, aY + 1, aZ + 1); + } + + @Override + public void onEntityCollidedWithBlock(World aWorld, int aX, int aY, int aZ, Entity collider) { + // + } + + @Override + public void onCreated(ItemStack aStack, World aWorld, EntityPlayer aPlayer) { + // + } + + @Override + public boolean allowGeneralRedstoneOutput() { + return false; + } + + @Deprecated + public String trans(String aKey, String aEnglish) { + return GT_Utility.trans(aKey, aEnglish); + } + + @Override + public boolean hasAlternativeModeText() { + return false; + } + + @Override + public String getAlternativeModeText() { + return ""; + } + + @Override + public boolean shouldJoinIc2Enet() { + return false; + } + + public boolean shouldTriggerBlockUpdate() { + return false; + } + + // === AE2 compat === + + public AECableType getCableConnectionType(ForgeDirection forgeDirection) { + return AECableType.NONE; + } + + public AENetworkProxy getProxy() { + return null; + } + + public void gridChanged() {} + + @Override + public ItemStack getMachineCraftingIcon() { + final IGregTechTileEntity mte = getBaseMetaTileEntity(); + if (mte == null) { + return null; + } + return mte.getDrops() + .stream() + .findAny() + .orElse(null); + } + + // === Waila compat === + + @Override + public void getWailaBody(ItemStack itemStack, List<String> currenttip, IWailaDataAccessor accessor, + IWailaConfigHandler config) { + currenttip.add( + String.format( + "Facing: %s", + mBaseMetaTileEntity.getFrontFacing() + .name())); + + if (this instanceof IPowerChannelState state) { + final NBTTagCompound tag = accessor.getNBTData(); + final boolean isActive = tag.getBoolean("isActive"); + final boolean isPowered = tag.getBoolean("isPowered"); + final boolean isBooting = tag.getBoolean("isBooting"); + currenttip.add(WailaText.getPowerState(isActive, isPowered, isBooting)); + } + } + + @Override + public void getWailaNBTData(EntityPlayerMP player, TileEntity tile, NBTTagCompound tag, World world, int x, int y, + int z) { + if (this instanceof IPowerChannelState state) { + final boolean isActive = state.isActive(); + final boolean isPowered = state.isPowered(); + final boolean isBooting = state.isBooting(); + tag.setBoolean("isActive", isActive); + tag.setBoolean("isPowered", isPowered); + tag.setBoolean("isBooting", isBooting); + } + } + + protected String getAEDiagnostics() { + try { + if (getProxy() == null) return "(proxy)"; + if (getProxy().getNode() == null) return "(node)"; + if (getProxy().getNode() + .getGrid() == null) return "(grid)"; + if (!getProxy().getNode() + .meetsChannelRequirements()) return "(channels)"; + IPathingGrid pg = getProxy().getNode() + .getGrid() + .getCache(IPathingGrid.class); + if (!pg.isNetworkBooting()) return "(booting)"; + IEnergyGrid eg = getProxy().getNode() + .getGrid() + .getCache(IEnergyGrid.class); + if (!eg.isNetworkPowered()) return "(power)"; + } catch (Throwable ex) { + ex.printStackTrace(); + } + return ""; + } + + @Override + public GUITextureSet getGUITextureSet() { + return GUITextureSet.DEFAULT; + } + + @Override + public int getGUIColorization() { + Dyes dye = Dyes.dyeWhite; + if (this.colorOverride.sLoaded()) { + if (this.colorOverride.sGuiTintingEnabled() && getBaseMetaTileEntity() != null) { + dye = Dyes.getDyeFromIndex(getBaseMetaTileEntity().getColorization()); + return this.colorOverride.getGuiTintOrDefault(dye.mName, GT_Util.getRGBInt(dye.getRGBA())); + } + } else if (GregTech_API.sColoredGUI) { + if (GregTech_API.sMachineMetalGUI) { + dye = Dyes.MACHINE_METAL; + } else if (getBaseMetaTileEntity() != null) { + dye = Dyes.getDyeFromIndex(getBaseMetaTileEntity().getColorization()); + } + } + return GT_Util.getRGBInt(dye.getRGBA()); + } + + @Override + public int getTextColorOrDefault(String textType, int defaultColor) { + return colorOverride.getTextColorOrDefault(textType, defaultColor); + } + + protected Supplier<Integer> COLOR_TITLE = () -> getTextColorOrDefault("title", 0x404040); + protected Supplier<Integer> COLOR_TITLE_WHITE = () -> getTextColorOrDefault("title_white", 0xfafaff); + protected Supplier<Integer> COLOR_TEXT_WHITE = () -> getTextColorOrDefault("text_white", 0xfafaff); + protected Supplier<Integer> COLOR_TEXT_GRAY = () -> getTextColorOrDefault("text_gray", 0x404040); + protected Supplier<Integer> COLOR_TEXT_RED = () -> getTextColorOrDefault("text_red", 0xff0000); +} diff --git a/src/main/java/gregtech/api/metatileentity/TileIC2EnergySink.java b/src/main/java/gregtech/api/metatileentity/TileIC2EnergySink.java new file mode 100644 index 0000000000..6fecb840b4 --- /dev/null +++ b/src/main/java/gregtech/api/metatileentity/TileIC2EnergySink.java @@ -0,0 +1,119 @@ +package gregtech.api.metatileentity; + +import net.minecraft.tileentity.TileEntity; +import net.minecraftforge.common.util.ForgeDirection; + +import com.google.common.collect.Sets; + +import gregtech.api.interfaces.metatileentity.IConnectable; +import gregtech.api.interfaces.metatileentity.IMetaTileEntity; +import gregtech.api.interfaces.metatileentity.IMetaTileEntityCable; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.metatileentity.implementations.GT_MetaPipeEntity_Cable; +import gregtech.api.util.GT_Utility; +import ic2.api.energy.tile.IEnergySink; + +public class TileIC2EnergySink extends TileEntity implements IEnergySink { + + private final IGregTechTileEntity myMeta; + private GT_MetaPipeEntity_Cable cableMeta = null; + + public TileIC2EnergySink(IGregTechTileEntity meta) { + if (meta == null) throw new NullPointerException("no null metas"); + myMeta = meta; + final IMetaTileEntity metaTile = myMeta.getMetaTileEntity(); + if (metaTile instanceof IMetaTileEntityCable) { + cableMeta = (GT_MetaPipeEntity_Cable) metaTile; + } + setWorldObj(meta.getWorld()); + xCoord = meta.getXCoord(); + yCoord = meta.getYCoord(); + zCoord = meta.getZCoord(); + } + /* + * IC2 enet compat - IEnergySink + */ + + /** + * Determine how much energy the sink accepts. Note that you cannot modify the energy net from this method. + * <p> + * Make sure that {@link TileIC2EnergySink#injectEnergy} accepts energy if this function returns anything positive. + * + * @return max accepted input in eu + */ + @Override + public double getDemandedEnergy() { + if (cableMeta != null) { + /* + * We don't want everything to join the enet, treating the cable as a conductor, so we join it as a link. + * We don't want to traverse all cables connected to this, like we would during distribution, to see if it + * actually needs any EU, so we just always say we want it all. If there are more than two things attached + * and one of them is a GT cable that doesn't have anywhere to send its energy, the distribution will be a + * bit weird. In that case, use only one cable or a transformer. + */ + return (cableMeta.mVoltage * cableMeta.mAmperage); + } else return myMeta.getEUCapacity() - myMeta.getStoredEU(); + } + + /** + * Gets the tier of this energy sink. 1 = LV, 2 = MV, 3 = HV, 4 = EV etc. + * + * @return tier of this energy sink or {@link Integer#MAX_VALUE} to allow any voltage + */ + @Override + public int getSinkTier() { + return GT_Utility.getTier(cableMeta != null ? cableMeta.mVoltage : myMeta.getInputVoltage()); + } + + /** + * Transfer energy to the sink. + * <p> + * It's highly recommended to accept all energy by letting the internal buffer overflow to increase the performance + * and accuracy of the distribution simulation. + * + * @param directionFrom direction from which the energy comes from + * @param amount energy to be transferred + * @return Energy not consumed (leftover) + */ + @Override + public double injectEnergy(ForgeDirection directionFrom, double amount, double voltage) { + + final long amps = (long) Math + .max(amount / (cableMeta != null ? cableMeta.mVoltage : myMeta.getInputVoltage() * 1.0), 1.0); + final long euPerAmp = (long) (amount / (amps * 1.0)); + + final IMetaTileEntity metaTile = myMeta.getMetaTileEntity(); + if (metaTile == null) return amount; + + final long usedAmps; + if (cableMeta != null) { + usedAmps = ((IMetaTileEntityCable) metaTile).transferElectricity( + directionFrom, + Math.min(euPerAmp, cableMeta.mVoltage), + amps, + Sets.newHashSet((TileEntity) myMeta)); + + } else usedAmps = myMeta.injectEnergyUnits(directionFrom, Math.min(euPerAmp, myMeta.getInputVoltage()), amps); + return amount - (usedAmps * euPerAmp); + + // transferElectricity for cables + } + + /** + * Determine if this acceptor can accept current from an adjacent emitter in a direction. + * <p> + * The TileEntity in the emitter parameter is what was originally added to the energy net, which may be normal + * in-world TileEntity, a delegate or an IMetaDelegate. + * + * @param emitter energy emitter, may also be null or an IMetaDelegate + * @param direction direction the energy is being received from + */ + @Override + public boolean acceptsEnergyFrom(TileEntity emitter, ForgeDirection direction) { + final IMetaTileEntity metaTile = myMeta.getMetaTileEntity(); + if (metaTile instanceof IMetaTileEntityCable + && (direction == ForgeDirection.UNKNOWN || ((IConnectable) metaTile).isConnectedAtSide(direction))) + return true; + else return myMeta.inputEnergyFrom(direction, false); + } +} diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaPipeEntity_Cable.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaPipeEntity_Cable.java new file mode 100644 index 0000000000..1477f989f8 --- /dev/null +++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaPipeEntity_Cable.java @@ -0,0 +1,679 @@ +package gregtech.api.metatileentity.implementations; + +import static gregtech.api.enums.Mods.GalacticraftCore; +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.UP; +import static net.minecraftforge.common.util.ForgeDirection.WEST; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.AxisAlignedBB; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.StatCollector; +import net.minecraft.world.World; +import net.minecraftforge.common.util.ForgeDirection; + +import cofh.api.energy.IEnergyReceiver; +import gregtech.GT_Mod; +import gregtech.api.GregTech_API; +import gregtech.api.enums.Dyes; +import gregtech.api.enums.Materials; +import gregtech.api.enums.TextureSet; +import gregtech.api.enums.Textures; +import gregtech.api.graphs.Node; +import gregtech.api.graphs.NodeList; +import gregtech.api.graphs.PowerNode; +import gregtech.api.graphs.PowerNodes; +import gregtech.api.graphs.consumers.ConsumerNode; +import gregtech.api.graphs.paths.PowerNodePath; +import gregtech.api.interfaces.ITexture; +import gregtech.api.interfaces.metatileentity.IConnectable; +import gregtech.api.interfaces.metatileentity.IMetaTileEntity; +import gregtech.api.interfaces.metatileentity.IMetaTileEntityCable; +import gregtech.api.interfaces.tileentity.ICoverable; +import gregtech.api.interfaces.tileentity.IEnergyConnected; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.logic.interfaces.PowerLogicHost; +import gregtech.api.metatileentity.BaseMetaPipeEntity; +import gregtech.api.metatileentity.MetaPipeEntity; +import gregtech.api.objects.GT_Cover_None; +import gregtech.api.render.TextureFactory; +import gregtech.api.util.GT_CoverBehavior; +import gregtech.api.util.GT_CoverBehaviorBase; +import gregtech.api.util.GT_GC_Compat; +import gregtech.api.util.GT_ModHandler; +import gregtech.api.util.GT_Utility; +import gregtech.api.util.ISerializableObject; +import gregtech.common.GT_Client; +import gregtech.common.covers.CoverInfo; +import gregtech.common.covers.GT_Cover_SolarPanel; +import ic2.api.energy.EnergyNet; +import ic2.api.energy.tile.IEnergyEmitter; +import ic2.api.energy.tile.IEnergySink; +import ic2.api.energy.tile.IEnergySource; +import ic2.api.energy.tile.IEnergyTile; +import ic2.api.reactor.IReactorChamber; +import mcp.mobius.waila.api.IWailaConfigHandler; +import mcp.mobius.waila.api.IWailaDataAccessor; + +public class GT_MetaPipeEntity_Cable extends MetaPipeEntity implements IMetaTileEntityCable { + + public final float mThickNess; + public final Materials mMaterial; + public final long mCableLossPerMeter, mAmperage, mVoltage; + public final boolean mInsulated, mCanShock; + + public int mTransferredAmperage = 0; + public long mTransferredVoltage = 0; + + @Deprecated + public int mTransferredAmperageLast20 = 0, mTransferredAmperageLast20OK = 0, mTransferredAmperageOK = 0; + @Deprecated + public long mTransferredVoltageLast20 = 0, mTransferredVoltageLast20OK = 0, mTransferredVoltageOK = 0; + + public long mRestRF; + public int mOverheat; + public static short mMaxOverheat = (short) (GT_Mod.gregtechproxy.mWireHeatingTicks * 100); + + private long lastWorldTick; + + public GT_MetaPipeEntity_Cable(int aID, String aName, String aNameRegional, float aThickNess, Materials aMaterial, + long aCableLossPerMeter, long aAmperage, long aVoltage, boolean aInsulated, boolean aCanShock) { + super(aID, aName, aNameRegional, 0); + mThickNess = aThickNess; + mMaterial = aMaterial; + mAmperage = aAmperage; + mVoltage = aVoltage; + mInsulated = aInsulated; + mCanShock = aCanShock; + mCableLossPerMeter = aCableLossPerMeter; + } + + public GT_MetaPipeEntity_Cable(String aName, float aThickNess, Materials aMaterial, long aCableLossPerMeter, + long aAmperage, long aVoltage, boolean aInsulated, boolean aCanShock) { + super(aName, 0); + mThickNess = aThickNess; + mMaterial = aMaterial; + mAmperage = aAmperage; + mVoltage = aVoltage; + mInsulated = aInsulated; + mCanShock = aCanShock; + mCableLossPerMeter = aCableLossPerMeter; + } + + @Override + public byte getTileEntityBaseType() { + return (byte) (mInsulated ? 9 : 8); + } + + @Override + public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) { + return new GT_MetaPipeEntity_Cable( + mName, + mThickNess, + mMaterial, + mCableLossPerMeter, + mAmperage, + mVoltage, + mInsulated, + mCanShock); + } + + @Override + public ITexture[] getTexture(IGregTechTileEntity baseMetaTileEntity, ForgeDirection sideDirection, + int facingDirection, int colorIndex, boolean active, boolean redstoneLevel) { + if (!mInsulated) return new ITexture[] { TextureFactory + .of(mMaterial.mIconSet.mTextures[TextureSet.INDEX_wire], Dyes.getModulation(colorIndex, mMaterial.mRGBa)) }; + if (active) { + float tThickNess = getThickNess(); + if (tThickNess < 0.124F) return new ITexture[] { TextureFactory + .of(Textures.BlockIcons.INSULATION_FULL, Dyes.getModulation(colorIndex, Dyes.CABLE_INSULATION.mRGBa)) }; + if (tThickNess < 0.374F) // 0.375 x1 + return new ITexture[] { + TextureFactory.of(mMaterial.mIconSet.mTextures[TextureSet.INDEX_wire], mMaterial.mRGBa), + TextureFactory.of( + Textures.BlockIcons.INSULATION_TINY, + Dyes.getModulation(colorIndex, Dyes.CABLE_INSULATION.mRGBa)) }; + if (tThickNess < 0.499F) // 0.500 x2 + return new ITexture[] { + TextureFactory.of(mMaterial.mIconSet.mTextures[TextureSet.INDEX_wire], mMaterial.mRGBa), + TextureFactory.of( + Textures.BlockIcons.INSULATION_SMALL, + Dyes.getModulation(colorIndex, Dyes.CABLE_INSULATION.mRGBa)) }; + if (tThickNess < 0.624F) // 0.625 x4 + return new ITexture[] { + TextureFactory.of(mMaterial.mIconSet.mTextures[TextureSet.INDEX_wire], mMaterial.mRGBa), + TextureFactory.of( + Textures.BlockIcons.INSULATION_MEDIUM, + Dyes.getModulation(colorIndex, Dyes.CABLE_INSULATION.mRGBa)) }; + if (tThickNess < 0.749F) // 0.750 x8 + return new ITexture[] { + TextureFactory.of(mMaterial.mIconSet.mTextures[TextureSet.INDEX_wire], mMaterial.mRGBa), + TextureFactory.of( + Textures.BlockIcons.INSULATION_MEDIUM_PLUS, + Dyes.getModulation(colorIndex, Dyes.CABLE_INSULATION.mRGBa)) }; + if (tThickNess < 0.874F) // 0.825 x12 + return new ITexture[] { + TextureFactory.of(mMaterial.mIconSet.mTextures[TextureSet.INDEX_wire], mMaterial.mRGBa), + TextureFactory.of( + Textures.BlockIcons.INSULATION_LARGE, + Dyes.getModulation(colorIndex, Dyes.CABLE_INSULATION.mRGBa)) }; + return new ITexture[] { + TextureFactory.of(mMaterial.mIconSet.mTextures[TextureSet.INDEX_wire], mMaterial.mRGBa), + TextureFactory.of( + Textures.BlockIcons.INSULATION_HUGE, + Dyes.getModulation(colorIndex, Dyes.CABLE_INSULATION.mRGBa)) }; + } + return new ITexture[] { TextureFactory + .of(Textures.BlockIcons.INSULATION_FULL, Dyes.getModulation(colorIndex, Dyes.CABLE_INSULATION.mRGBa)) }; + } + + @Override + public void onEntityCollidedWithBlock(World aWorld, int aX, int aY, int aZ, Entity aEntity) { + + if (!mCanShock) return; + + final BaseMetaPipeEntity baseEntity = (BaseMetaPipeEntity) getBaseMetaTileEntity(); + + if (!(aEntity instanceof EntityLivingBase livingEntity)) return; + if (!(baseEntity.getNodePath() instanceof PowerNodePath powerPath)) return; + + if (isCoverOnSide(baseEntity, livingEntity)) return; + if ((baseEntity.mConnections & IConnectable.HAS_HARDENEDFOAM) == 1) return; + + final long amperage = powerPath.getAmperage(); + final long voltage = powerPath.getVoltage(); + + if (amperage == 0L) return; + + GT_Utility.applyElectricityDamage(livingEntity, voltage, amperage); + } + + @Override + public boolean isSimpleMachine() { + return true; + } + + @Override + public boolean isFacingValid(ForgeDirection facing) { + return false; + } + + @Override + public boolean isValidSlot(int aIndex) { + return true; + } + + @Override + public final boolean renderInside(ForgeDirection side) { + return false; + } + + @Override + public int getProgresstime() { + return (int) mTransferredAmperage * 64; + } + + @Override + public int maxProgresstime() { + return (int) mAmperage * 64; + } + + @Override + public long injectEnergyUnits(ForgeDirection side, long voltage, long amperage) { + if (!isConnectedAtSide(side) && side != ForgeDirection.UNKNOWN) return 0; + if (!getBaseMetaTileEntity().getCoverInfoAtSide(side) + .letsEnergyIn()) return 0; + return transferElectricity(side, voltage, amperage, (HashSet<TileEntity>) null); + } + + @Override + @Deprecated + public long transferElectricity(ForgeDirection side, long aVoltage, long aAmperage, + ArrayList<TileEntity> aAlreadyPassedTileEntityList) { + return transferElectricity(side, aVoltage, aAmperage, new HashSet<>(aAlreadyPassedTileEntityList)); + } + + @Override + public long transferElectricity(ForgeDirection side, long voltage, long amperage, + HashSet<TileEntity> alreadyPassedSet) { + if (!getBaseMetaTileEntity().isServerSide() || !isConnectedAtSide(side) && side != ForgeDirection.UNKNOWN) + return 0; + final BaseMetaPipeEntity tBase = (BaseMetaPipeEntity) getBaseMetaTileEntity(); + if (!(tBase.getNode() instanceof PowerNode tNode)) return 0; + int tPlace = 0; + final Node[] tToPower = new Node[tNode.mConsumers.size()]; + if (tNode.mHadVoltage) { + for (ConsumerNode consumer : tNode.mConsumers) { + if (consumer.needsEnergy()) tToPower[tPlace++] = consumer; + } + } else { + tNode.mHadVoltage = true; + for (ConsumerNode consumer : tNode.mConsumers) { + tToPower[tPlace++] = consumer; + } + } + return PowerNodes.powerNode(tNode, null, new NodeList(tToPower), (int) voltage, (int) amperage); + } + + @Override + public void onFirstTick(IGregTechTileEntity aBaseMetaTileEntity) { + if (aBaseMetaTileEntity.isServerSide()) { + lastWorldTick = aBaseMetaTileEntity.getWorld() + .getTotalWorldTime() - 1; + // sets initial value -1 since it is + // in the same tick as first on post + // tick + } + } + + @Override + public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) { + super.onPostTick(aBaseMetaTileEntity, aTick); + if (aTick % 20 == 0 && aBaseMetaTileEntity.isServerSide() + && (!GT_Mod.gregtechproxy.gt6Cable || mCheckConnections)) { + checkConnections(); + } + } + + @Override + public boolean onWireCutterRightClick(ForgeDirection side, ForgeDirection wrenchingSide, EntityPlayer aPlayer, + float aX, float aY, float aZ) { + if (GT_Mod.gregtechproxy.gt6Cable + && GT_ModHandler.damageOrDechargeItem(aPlayer.inventory.getCurrentItem(), 1, 500, aPlayer)) { + if (isConnectedAtSide(wrenchingSide)) { + disconnect(wrenchingSide); + GT_Utility.sendChatToPlayer(aPlayer, GT_Utility.trans("215", "Disconnected")); + } else if (!GT_Mod.gregtechproxy.costlyCableConnection) { + if (connect(wrenchingSide) > 0) + GT_Utility.sendChatToPlayer(aPlayer, GT_Utility.trans("214", "Connected")); + } + return true; + } + return false; + } + + @Override + public boolean onSolderingToolRightClick(ForgeDirection side, ForgeDirection wrenchingSide, EntityPlayer aPlayer, + float aX, float aY, float aZ) { + if (GT_Mod.gregtechproxy.gt6Cable + && GT_ModHandler.damageOrDechargeItem(aPlayer.inventory.getCurrentItem(), 1, 500, aPlayer)) { + if (isConnectedAtSide(wrenchingSide)) { + disconnect(wrenchingSide); + GT_Utility.sendChatToPlayer(aPlayer, GT_Utility.trans("215", "Disconnected")); + } else if (!GT_Mod.gregtechproxy.costlyCableConnection || GT_ModHandler.consumeSolderingMaterial(aPlayer)) { + if (connect(wrenchingSide) > 0) + GT_Utility.sendChatToPlayer(aPlayer, GT_Utility.trans("214", "Connected")); + } + return true; + } + return false; + } + + @Override + public boolean letsIn(GT_CoverBehavior coverBehavior, ForgeDirection side, int aCoverID, int aCoverVariable, + ICoverable aTileEntity) { + return coverBehavior.letsEnergyIn(side, aCoverID, aCoverVariable, aTileEntity); + } + + @Override + public boolean letsOut(GT_CoverBehavior coverBehavior, ForgeDirection side, int aCoverID, int aCoverVariable, + ICoverable aTileEntity) { + return coverBehavior.letsEnergyOut(side, aCoverID, aCoverVariable, aTileEntity); + } + + @Override + public boolean letsIn(GT_CoverBehaviorBase<?> coverBehavior, ForgeDirection side, int aCoverID, + ISerializableObject aCoverVariable, ICoverable aTileEntity) { + return coverBehavior.letsEnergyIn(side, aCoverID, aCoverVariable, aTileEntity); + } + + @Override + public boolean letsOut(GT_CoverBehaviorBase<?> coverBehavior, ForgeDirection side, int aCoverID, + ISerializableObject aCoverVariable, ICoverable aTileEntity) { + return coverBehavior.letsEnergyOut(side, aCoverID, aCoverVariable, aTileEntity); + } + + @Override + public boolean letsIn(CoverInfo coverInfo) { + return coverInfo.letsEnergyIn(); + } + + @Override + public boolean letsOut(CoverInfo coverInfo) { + return coverInfo.letsEnergyOut(); + } + + @Override + public boolean canConnect(ForgeDirection side, TileEntity tileEntity) { + final IGregTechTileEntity baseMetaTile = getBaseMetaTileEntity(); + final GT_CoverBehaviorBase<?> coverBehavior = baseMetaTile.getCoverBehaviorAtSideNew(side); + final ForgeDirection oppositeSide = side.getOpposite(); + + // GT Machine handling + if ((tileEntity instanceof PowerLogicHost powerLogic && powerLogic.getPowerLogic(oppositeSide) != null) + || ((tileEntity instanceof IEnergyConnected energyConnected) + && (energyConnected.inputEnergyFrom(oppositeSide, false) + || energyConnected.outputsEnergyTo(oppositeSide, false)))) + return true; + + // Solar Panel Compat + if (coverBehavior instanceof GT_Cover_SolarPanel) return true; + + // ((tIsGregTechTileEntity && tIsTileEntityCable) && (tAlwaysLookConnected || tLetEnergyIn || tLetEnergyOut) ) + // --> Not needed + if (GalacticraftCore.isModLoaded() && GT_GC_Compat.canConnect(tileEntity, oppositeSide)) return true; + + // AE2-p2p Compat + if (tileEntity instanceof appeng.tile.powersink.IC2 ic2sink + && ic2sink.acceptsEnergyFrom((TileEntity) baseMetaTile, oppositeSide)) return true; + + // IC2 Compat + { + final TileEntity ic2Energy; + + if (tileEntity instanceof IReactorChamber) + ic2Energy = (TileEntity) ((IReactorChamber) tileEntity).getReactor(); + else ic2Energy = (tileEntity == null || tileEntity instanceof IEnergyTile || EnergyNet.instance == null) + ? tileEntity + : EnergyNet.instance + .getTileEntity(tileEntity.getWorldObj(), tileEntity.xCoord, tileEntity.yCoord, tileEntity.zCoord); + + // IC2 Sink Compat + if ((ic2Energy instanceof IEnergySink) + && ((IEnergySink) ic2Energy).acceptsEnergyFrom((TileEntity) baseMetaTile, oppositeSide)) return true; + + // IC2 Source Compat + if (GT_Mod.gregtechproxy.ic2EnergySourceCompat && (ic2Energy instanceof IEnergySource)) { + if (((IEnergySource) ic2Energy).emitsEnergyTo((TileEntity) baseMetaTile, oppositeSide)) { + return true; + } + } + } + // RF Output Compat + if (GregTech_API.mOutputRF && tileEntity instanceof IEnergyReceiver + && ((IEnergyReceiver) tileEntity).canConnectEnergy(oppositeSide)) return true; + + // RF Input Compat + return GregTech_API.mInputRF && (tileEntity instanceof IEnergyEmitter + && ((IEnergyEmitter) tileEntity).emitsEnergyTo((TileEntity) baseMetaTile, oppositeSide)); + } + + @Override + public boolean getGT6StyleConnection() { + // Yes if GT6 Cables are enabled + return GT_Mod.gregtechproxy.gt6Cable; + } + + @Override + public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side, + ItemStack aStack) { + return false; + } + + @Override + public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side, + ItemStack aStack) { + return false; + } + + @Override + public String[] getDescription() { + return new String[] { + StatCollector.translateToLocal("GT5U.item.cable.max_voltage") + ": %%%" + + EnumChatFormatting.GREEN + + GT_Utility.formatNumbers(mVoltage) + + " (" + + GT_Utility.getColoredTierNameFromVoltage(mVoltage) + + EnumChatFormatting.GREEN + + ")" + + EnumChatFormatting.GRAY, + StatCollector.translateToLocal("GT5U.item.cable.max_amperage") + ": %%%" + + EnumChatFormatting.YELLOW + + GT_Utility.formatNumbers(mAmperage) + + EnumChatFormatting.GRAY, + StatCollector.translateToLocal("GT5U.item.cable.loss") + ": %%%" + + EnumChatFormatting.RED + + GT_Utility.formatNumbers(mCableLossPerMeter) + + EnumChatFormatting.GRAY + + "%%% " + + StatCollector.translateToLocal("GT5U.item.cable.eu_volt") }; + } + + @Override + public float getThickNess() { + if (GT_Mod.instance.isClientSide() && (GT_Client.hideValue & 0x1) != 0) return 0.0625F; + return mThickNess; + } + + @Override + public void saveNBTData(NBTTagCompound aNBT) { + if (GT_Mod.gregtechproxy.gt6Cable) aNBT.setByte("mConnections", mConnections); + } + + @Override + public void loadNBTData(NBTTagCompound aNBT) { + if (GT_Mod.gregtechproxy.gt6Cable) { + mConnections = aNBT.getByte("mConnections"); + } + } + + @Override + public boolean isGivingInformation() { + return true; + } + + @Override + public String[] getInfoData() { + final BaseMetaPipeEntity base = (BaseMetaPipeEntity) getBaseMetaTileEntity(); + final PowerNodePath path = (PowerNodePath) base.getNodePath(); + + if (path == null) + return new String[] { EnumChatFormatting.RED + "Failed to get Power Node info" + EnumChatFormatting.RESET }; + + final long currAmp = path.getAmperage(); + final long currVoltage = path.getVoltage(); + + final double avgAmp = path.getAvgAmperage(); + final double avgVoltage = path.getAvgVoltage(); + + final long maxVoltageOut = (mVoltage - mCableLossPerMeter) * mAmperage; + + return new String[] { + "Heat: " + EnumChatFormatting.RED + + GT_Utility.formatNumbers(mOverheat) + + EnumChatFormatting.RESET + + " / " + + EnumChatFormatting.YELLOW + + GT_Utility.formatNumbers(mMaxOverheat) + + EnumChatFormatting.RESET, + "Amperage: " + EnumChatFormatting.GREEN + + GT_Utility.formatNumbers(currAmp) + + EnumChatFormatting.RESET + + " / " + + EnumChatFormatting.YELLOW + + GT_Utility.formatNumbers(mAmperage) + + EnumChatFormatting.RESET + + " A", + "Voltage Out: " + EnumChatFormatting.GREEN + + GT_Utility.formatNumbers(currVoltage) + + EnumChatFormatting.RESET + + " / " + + EnumChatFormatting.YELLOW + + GT_Utility.formatNumbers(maxVoltageOut) + + EnumChatFormatting.RESET + + " EU/t", + "Avg Amperage (20t): " + EnumChatFormatting.YELLOW + + GT_Utility.formatNumbers(avgAmp) + + EnumChatFormatting.RESET + + " A", + "Avg Output (20t): " + EnumChatFormatting.YELLOW + + GT_Utility.formatNumbers(avgVoltage) + + EnumChatFormatting.RESET + + " EU/t" }; + } + + @Override + public AxisAlignedBB getCollisionBoundingBoxFromPool(World aWorld, int aX, int aY, int aZ) { + if (GT_Mod.instance.isClientSide() && (GT_Client.hideValue & 0x2) != 0) + return AxisAlignedBB.getBoundingBox(aX, aY, aZ, aX + 1, aY + 1, aZ + 1); + else return getActualCollisionBoundingBoxFromPool(aWorld, aX, aY, aZ); + } + + private AxisAlignedBB getActualCollisionBoundingBoxFromPool(World aWorld, int aX, int aY, int aZ) { + float tSpace = (1f - mThickNess) / 2; + float spaceDown = tSpace; + float spaceUp = 1f - tSpace; + float spaceNorth = tSpace; + float spaceSouth = 1f - tSpace; + float spaceWest = tSpace; + float spaceEast = 1f - tSpace; + + if (getBaseMetaTileEntity().getCoverIDAtSide(DOWN) != 0) { + spaceDown = spaceNorth = spaceWest = 0; + spaceSouth = spaceEast = 1; + } + if (getBaseMetaTileEntity().getCoverIDAtSide(UP) != 0) { + spaceNorth = spaceWest = 0; + spaceUp = spaceSouth = spaceEast = 1; + } + if (getBaseMetaTileEntity().getCoverIDAtSide(NORTH) != 0) { + spaceDown = spaceNorth = spaceWest = 0; + spaceUp = spaceEast = 1; + } + if (getBaseMetaTileEntity().getCoverIDAtSide(SOUTH) != 0) { + spaceDown = spaceWest = 0; + spaceUp = spaceSouth = spaceEast = 1; + } + if (getBaseMetaTileEntity().getCoverIDAtSide(WEST) != 0) { + spaceDown = spaceNorth = spaceWest = 0; + spaceUp = spaceSouth = 1; + } + if (getBaseMetaTileEntity().getCoverIDAtSide(EAST) != 0) { + spaceDown = spaceNorth = 0; + spaceUp = spaceSouth = spaceEast = 1; + } + + byte tConn = ((BaseMetaPipeEntity) getBaseMetaTileEntity()).mConnections; + if ((tConn & DOWN.flag) != 0) spaceDown = 0f; + if ((tConn & UP.flag) != 0) spaceUp = 1f; + if ((tConn & NORTH.flag) != 0) spaceNorth = 0f; + if ((tConn & SOUTH.flag) != 0) spaceSouth = 1f; + if ((tConn & WEST.flag) != 0) spaceWest = 0f; + if ((tConn & EAST.flag) != 0) spaceEast = 1f; + + return AxisAlignedBB.getBoundingBox( + aX + spaceWest, + aY + spaceDown, + aZ + spaceNorth, + aX + spaceEast, + aY + spaceUp, + aZ + spaceSouth); + } + + @Override + public void addCollisionBoxesToList(World aWorld, int aX, int aY, int aZ, AxisAlignedBB inputAABB, + List<AxisAlignedBB> outputAABB, Entity collider) { + super.addCollisionBoxesToList(aWorld, aX, aY, aZ, inputAABB, outputAABB, collider); + if (GT_Mod.instance.isClientSide() && (GT_Client.hideValue & 0x2) != 0) { + final AxisAlignedBB aabb = getActualCollisionBoundingBoxFromPool(aWorld, aX, aY, aZ); + if (inputAABB.intersectsWith(aabb)) outputAABB.add(aabb); + } + } + + @Override + public boolean shouldJoinIc2Enet() { + if (!GT_Mod.gregtechproxy.ic2EnergySourceCompat) return false; + + if (mConnections != 0) { + final IGregTechTileEntity baseMeta = getBaseMetaTileEntity(); + for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) { + if (isConnectedAtSide(side)) { + final TileEntity tTileEntity = baseMeta.getTileEntityAtSide(side); + final TileEntity tEmitter = (tTileEntity == null || tTileEntity instanceof IEnergyTile + || EnergyNet.instance == null) + ? tTileEntity + : EnergyNet.instance.getTileEntity( + tTileEntity.getWorldObj(), + tTileEntity.xCoord, + tTileEntity.yCoord, + tTileEntity.zCoord); + + if (tEmitter instanceof IEnergyEmitter) return true; + } + } + } + return false; + } + + @Override + public void reloadLocks() { + final BaseMetaPipeEntity pipe = (BaseMetaPipeEntity) getBaseMetaTileEntity(); + if (pipe.getNode() != null) { + for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) { + if (isConnectedAtSide(side)) { + final CoverInfo coverInfo = pipe.getCoverInfoAtSide(side); + if (coverInfo.getCoverBehavior() instanceof GT_Cover_None) continue; + if (!letsIn(coverInfo) || !letsOut(coverInfo)) { + pipe.addToLock(pipe, side); + } else { + pipe.removeFromLock(pipe, side); + } + } + } + } else { + boolean dontAllow = false; + for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) { + if (isConnectedAtSide(side)) { + final CoverInfo coverInfo = pipe.getCoverInfoAtSide(side); + if (coverInfo.getCoverBehavior() instanceof GT_Cover_None) continue; + + if (!letsIn(coverInfo) || !letsOut(coverInfo)) { + dontAllow = true; + } + } + } + if (dontAllow) { + pipe.addToLock(pipe, DOWN); + } else { + pipe.removeFromLock(pipe, DOWN); + } + } + } + + @Override + public void getWailaBody(ItemStack itemStack, List<String> currenttip, IWailaDataAccessor accessor, + IWailaConfigHandler config) { + + currenttip.add( + StatCollector.translateToLocal("GT5U.item.cable.max_voltage") + ": " + + EnumChatFormatting.GREEN + + GT_Utility.formatNumbers(mVoltage) + + " (" + + GT_Utility.getColoredTierNameFromVoltage(mVoltage) + + EnumChatFormatting.GREEN + + ")"); + currenttip.add( + StatCollector.translateToLocal("GT5U.item.cable.max_amperage") + ": " + + EnumChatFormatting.YELLOW + + GT_Utility.formatNumbers(mAmperage)); + currenttip.add( + StatCollector.translateToLocal("GT5U.item.cable.loss") + ": " + + EnumChatFormatting.RED + + GT_Utility.formatNumbers(mCableLossPerMeter) + + EnumChatFormatting.RESET + + " " + + StatCollector.translateToLocal("GT5U.item.cable.eu_volt")); + } +} diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaPipeEntity_Fluid.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaPipeEntity_Fluid.java new file mode 100644 index 0000000000..f604f1583f --- /dev/null +++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaPipeEntity_Fluid.java @@ -0,0 +1,991 @@ +package gregtech.api.metatileentity.implementations; + +import static gregtech.api.enums.GT_Values.ALL_VALID_SIDES; +import static gregtech.api.enums.GT_Values.D1; +import static gregtech.api.enums.Mods.TinkerConstruct; +import static gregtech.api.enums.Mods.Translocator; +import static gregtech.api.metatileentity.implementations.GT_MetaPipeEntity_Fluid.Border.BOTTOM; +import static gregtech.api.metatileentity.implementations.GT_MetaPipeEntity_Fluid.Border.LEFT; +import static gregtech.api.metatileentity.implementations.GT_MetaPipeEntity_Fluid.Border.RIGHT; +import static gregtech.api.metatileentity.implementations.GT_MetaPipeEntity_Fluid.Border.TOP; +import static gregtech.api.objects.XSTR.XSTR_INSTANCE; +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.UP; +import static net.minecraftforge.common.util.ForgeDirection.WEST; + +import java.util.ArrayList; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.AxisAlignedBB; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.world.World; +import net.minecraftforge.common.util.ForgeDirection; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.FluidTankInfo; +import net.minecraftforge.fluids.IFluidHandler; + +import org.apache.commons.lang3.tuple.MutableTriple; + +import cpw.mods.fml.common.Optional; +import gregtech.GT_Mod; +import gregtech.api.GregTech_API; +import gregtech.api.enums.ConfigCategories; +import gregtech.api.enums.Dyes; +import gregtech.api.enums.Materials; +import gregtech.api.enums.Mods; +import gregtech.api.enums.OrePrefixes; +import gregtech.api.enums.ParticleFX; +import gregtech.api.enums.SoundResource; +import gregtech.api.enums.Textures; +import gregtech.api.enums.ToolModes; +import gregtech.api.interfaces.IIconContainer; +import gregtech.api.interfaces.ITexture; +import gregtech.api.interfaces.metatileentity.IMetaTileEntity; +import gregtech.api.interfaces.tileentity.ICoverable; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.items.GT_MetaGenerated_Tool; +import gregtech.api.metatileentity.BaseMetaPipeEntity; +import gregtech.api.metatileentity.MetaPipeEntity; +import gregtech.api.render.TextureFactory; +import gregtech.api.util.GT_CoverBehavior; +import gregtech.api.util.GT_CoverBehaviorBase; +import gregtech.api.util.GT_Log; +import gregtech.api.util.GT_Utility; +import gregtech.api.util.ISerializableObject; +import gregtech.api.util.WorldSpawnedEventBuilder.ParticleEventBuilder; +import gregtech.common.GT_Client; +import gregtech.common.covers.CoverInfo; +import gregtech.common.covers.GT_Cover_Drain; +import gregtech.common.covers.GT_Cover_FluidRegulator; + +public class GT_MetaPipeEntity_Fluid extends MetaPipeEntity { + + protected static final EnumMap<ForgeDirection, EnumMap<Border, ForgeDirection>> FACE_BORDER_MAP = new EnumMap<>( + ForgeDirection.class); + + static { + FACE_BORDER_MAP.put(DOWN, borderMap(NORTH, SOUTH, EAST, WEST)); + FACE_BORDER_MAP.put(UP, borderMap(NORTH, SOUTH, WEST, EAST)); + FACE_BORDER_MAP.put(NORTH, borderMap(UP, DOWN, EAST, WEST)); + FACE_BORDER_MAP.put(SOUTH, borderMap(UP, DOWN, WEST, EAST)); + FACE_BORDER_MAP.put(WEST, borderMap(UP, DOWN, NORTH, SOUTH)); + FACE_BORDER_MAP.put(EAST, borderMap(UP, DOWN, SOUTH, NORTH)); + } + + protected static final Map<Integer, IIconContainer> RESTR_TEXTURE_MAP = new HashMap<>(); + + static { + RESTR_TEXTURE_MAP.put(TOP.mask, Textures.BlockIcons.PIPE_RESTRICTOR_UP); + RESTR_TEXTURE_MAP.put(BOTTOM.mask, Textures.BlockIcons.PIPE_RESTRICTOR_DOWN); + RESTR_TEXTURE_MAP.put(TOP.mask | BOTTOM.mask, Textures.BlockIcons.PIPE_RESTRICTOR_UD); + RESTR_TEXTURE_MAP.put(LEFT.mask, Textures.BlockIcons.PIPE_RESTRICTOR_LEFT); + RESTR_TEXTURE_MAP.put(TOP.mask | LEFT.mask, Textures.BlockIcons.PIPE_RESTRICTOR_UL); + RESTR_TEXTURE_MAP.put(BOTTOM.mask | LEFT.mask, Textures.BlockIcons.PIPE_RESTRICTOR_DL); + RESTR_TEXTURE_MAP.put(TOP.mask | BOTTOM.mask | LEFT.mask, Textures.BlockIcons.PIPE_RESTRICTOR_NR); + RESTR_TEXTURE_MAP.put(RIGHT.mask, Textures.BlockIcons.PIPE_RESTRICTOR_RIGHT); + RESTR_TEXTURE_MAP.put(TOP.mask | RIGHT.mask, Textures.BlockIcons.PIPE_RESTRICTOR_UR); + RESTR_TEXTURE_MAP.put(BOTTOM.mask | RIGHT.mask, Textures.BlockIcons.PIPE_RESTRICTOR_DR); + RESTR_TEXTURE_MAP.put(TOP.mask | BOTTOM.mask | RIGHT.mask, Textures.BlockIcons.PIPE_RESTRICTOR_NL); + RESTR_TEXTURE_MAP.put(LEFT.mask | RIGHT.mask, Textures.BlockIcons.PIPE_RESTRICTOR_LR); + RESTR_TEXTURE_MAP.put(TOP.mask | LEFT.mask | RIGHT.mask, Textures.BlockIcons.PIPE_RESTRICTOR_ND); + RESTR_TEXTURE_MAP.put(BOTTOM.mask | LEFT.mask | RIGHT.mask, Textures.BlockIcons.PIPE_RESTRICTOR_NU); + RESTR_TEXTURE_MAP.put(TOP.mask | BOTTOM.mask | LEFT.mask | RIGHT.mask, Textures.BlockIcons.PIPE_RESTRICTOR); + } + + public final float mThickNess; + public final Materials mMaterial; + public final int mCapacity, mHeatResistance, mPipeAmount; + public final boolean mGasProof; + public final FluidStack[] mFluids; + public byte mLastReceivedFrom = 0, oLastReceivedFrom = 0; + /** + * Bitmask for whether disable fluid input form each side. + */ + public byte mDisableInput = 0; + + public GT_MetaPipeEntity_Fluid(int aID, String aName, String aNameRegional, float aThickNess, Materials aMaterial, + int aCapacity, int aHeatResistance, boolean aGasProof) { + this(aID, aName, aNameRegional, aThickNess, aMaterial, aCapacity, aHeatResistance, aGasProof, 1); + } + + public GT_MetaPipeEntity_Fluid(int aID, String aName, String aNameRegional, float aThickNess, Materials aMaterial, + int aCapacity, int aHeatResistance, boolean aGasProof, int aFluidTypes) { + super(aID, aName, aNameRegional, 0, false); + mThickNess = aThickNess; + mMaterial = aMaterial; + mCapacity = aCapacity; + mGasProof = aGasProof; + mHeatResistance = aHeatResistance; + mPipeAmount = aFluidTypes; + mFluids = new FluidStack[mPipeAmount]; + addInfo(aID); + } + + @Deprecated + public GT_MetaPipeEntity_Fluid(String aName, float aThickNess, Materials aMaterial, int aCapacity, + int aHeatResistance, boolean aGasProof) { + this(aName, aThickNess, aMaterial, aCapacity, aHeatResistance, aGasProof, 1); + } + + public GT_MetaPipeEntity_Fluid(String aName, float aThickNess, Materials aMaterial, int aCapacity, + int aHeatResistance, boolean aGasProof, int aFluidTypes) { + super(aName, 0); + mThickNess = aThickNess; + mMaterial = aMaterial; + mCapacity = aCapacity; + mGasProof = aGasProof; + mHeatResistance = aHeatResistance; + mPipeAmount = aFluidTypes; + mFluids = new FluidStack[mPipeAmount]; + } + + @Override + public byte getTileEntityBaseType() { + return (byte) (mMaterial == null ? 4 : (byte) (4) + Math.max(0, Math.min(3, mMaterial.mToolQuality))); + } + + @Override + public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) { + return new GT_MetaPipeEntity_Fluid( + mName, + mThickNess, + mMaterial, + mCapacity, + mHeatResistance, + mGasProof, + mPipeAmount); + } + + @Override + public ITexture[] getTexture(IGregTechTileEntity aBaseMetaTileEntity, ForgeDirection side, int aConnections, + int colorIndex, boolean aConnected, boolean redstoneLevel) { + if (side == ForgeDirection.UNKNOWN) return Textures.BlockIcons.ERROR_RENDERING; + final float tThickNess = getThickNess(); + if (mDisableInput == 0) + return new ITexture[] { aConnected ? getBaseTexture(tThickNess, mPipeAmount, mMaterial, colorIndex) + : TextureFactory.of( + mMaterial.mIconSet.mTextures[OrePrefixes.pipe.mTextureIndex], + Dyes.getModulation(colorIndex, mMaterial.mRGBa)) }; + int borderMask = 0; + for (Border border : Border.values()) { + if (isInputDisabledAtSide(getSideAtBorder(side, border))) borderMask |= border.mask; + } + + return new ITexture[] { aConnected ? getBaseTexture(tThickNess, mPipeAmount, mMaterial, colorIndex) + : TextureFactory.of( + mMaterial.mIconSet.mTextures[OrePrefixes.pipe.mTextureIndex], + Dyes.getModulation(colorIndex, mMaterial.mRGBa)), + getRestrictorTexture(borderMask) }; + } + + protected static ITexture getBaseTexture(float aThickNess, int aPipeAmount, Materials aMaterial, int colorIndex) { + if (aPipeAmount >= 9) return TextureFactory.of( + aMaterial.mIconSet.mTextures[OrePrefixes.pipeNonuple.mTextureIndex], + Dyes.getModulation(colorIndex, aMaterial.mRGBa)); + if (aPipeAmount >= 4) return TextureFactory.of( + aMaterial.mIconSet.mTextures[OrePrefixes.pipeQuadruple.mTextureIndex], + Dyes.getModulation(colorIndex, aMaterial.mRGBa)); + if (aThickNess < 0.124F) return TextureFactory.of( + aMaterial.mIconSet.mTextures[OrePrefixes.pipe.mTextureIndex], + Dyes.getModulation(colorIndex, aMaterial.mRGBa)); + if (aThickNess < 0.374F) return TextureFactory.of( + aMaterial.mIconSet.mTextures[OrePrefixes.pipeTiny.mTextureIndex], + Dyes.getModulation(colorIndex, aMaterial.mRGBa)); + if (aThickNess < 0.499F) return TextureFactory.of( + aMaterial.mIconSet.mTextures[OrePrefixes.pipeSmall.mTextureIndex], + Dyes.getModulation(colorIndex, aMaterial.mRGBa)); + if (aThickNess < 0.749F) return TextureFactory.of( + aMaterial.mIconSet.mTextures[OrePrefixes.pipeMedium.mTextureIndex], + Dyes.getModulation(colorIndex, aMaterial.mRGBa)); + if (aThickNess < 0.874F) return TextureFactory.of( + aMaterial.mIconSet.mTextures[OrePrefixes.pipeLarge.mTextureIndex], + Dyes.getModulation(colorIndex, aMaterial.mRGBa)); + return TextureFactory.of( + aMaterial.mIconSet.mTextures[OrePrefixes.pipeHuge.mTextureIndex], + Dyes.getModulation(colorIndex, aMaterial.mRGBa)); + } + + @Deprecated + protected static ITexture getRestrictorTexture(byte borderMask) { + return getRestrictorTexture((int) borderMask); + } + + protected static ITexture getRestrictorTexture(int borderMask) { + final IIconContainer restrictorIcon = RESTR_TEXTURE_MAP.get(borderMask); + return restrictorIcon != null ? TextureFactory.of(restrictorIcon) : null; + } + + @Override + public void onValueUpdate(byte aValue) { + mDisableInput = aValue; + } + + @Override + public byte getUpdateData() { + return mDisableInput; + } + + @Override + public boolean isSimpleMachine() { + return true; + } + + @Override + public boolean isFacingValid(ForgeDirection facing) { + return false; + } + + @Override + public boolean isValidSlot(int aIndex) { + return false; + } + + @Override + public final boolean renderInside(ForgeDirection side) { + return false; + } + + @Override + public int getProgresstime() { + return getFluidAmount(); + } + + @Override + public int maxProgresstime() { + return getCapacity(); + } + + @Override + public void saveNBTData(NBTTagCompound aNBT) { + for (int i = 0; i < mPipeAmount; i++) if (mFluids[i] != null) + aNBT.setTag("mFluid" + (i == 0 ? "" : i), mFluids[i].writeToNBT(new NBTTagCompound())); + aNBT.setByte("mLastReceivedFrom", mLastReceivedFrom); + if (GT_Mod.gregtechproxy.gt6Pipe) { + aNBT.setByte("mConnections", mConnections); + aNBT.setByte("mDisableInput", mDisableInput); + } + } + + @Override + public void loadNBTData(NBTTagCompound aNBT) { + for (int i = 0; i < mPipeAmount; i++) + mFluids[i] = FluidStack.loadFluidStackFromNBT(aNBT.getCompoundTag("mFluid" + (i == 0 ? "" : i))); + mLastReceivedFrom = aNBT.getByte("mLastReceivedFrom"); + if (GT_Mod.gregtechproxy.gt6Pipe) { + mConnections = aNBT.getByte("mConnections"); + mDisableInput = aNBT.getByte("mDisableInput"); + } + } + + @Override + public void onEntityCollidedWithBlock(World aWorld, int aX, int aY, int aZ, Entity aEntity) { + if ((((BaseMetaPipeEntity) getBaseMetaTileEntity()).mConnections & -128) == 0 + && aEntity instanceof EntityLivingBase) { + for (FluidStack tFluid : mFluids) { + if (tFluid != null) { + final int tTemperature = tFluid.getFluid() + .getTemperature(tFluid); + if (tTemperature > 320 + && !isCoverOnSide((BaseMetaPipeEntity) getBaseMetaTileEntity(), (EntityLivingBase) aEntity)) { + GT_Utility.applyHeatDamage((EntityLivingBase) aEntity, (tTemperature - 300) / 50.0F); + break; + } else if (tTemperature < 260 + && !isCoverOnSide((BaseMetaPipeEntity) getBaseMetaTileEntity(), (EntityLivingBase) aEntity)) { + GT_Utility.applyFrostDamage((EntityLivingBase) aEntity, (270 - tTemperature) / 25.0F); + break; + } + } + } + } + } + + @Override + public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) { + super.onPostTick(aBaseMetaTileEntity, aTick); + if (aBaseMetaTileEntity.isServerSide() && aTick % 5 == 0) { + mLastReceivedFrom &= 63; + if (mLastReceivedFrom == 63) { + mLastReceivedFrom = 0; + } + + if (!GT_Mod.gregtechproxy.gt6Pipe || mCheckConnections) checkConnections(); + + final boolean shouldDistribute = (oLastReceivedFrom == mLastReceivedFrom); + for (int i = 0, j = aBaseMetaTileEntity.getRandomNumber(mPipeAmount); i < mPipeAmount; i++) { + final int index = (i + j) % mPipeAmount; + if (mFluids[index] != null && mFluids[index].amount <= 0) mFluids[index] = null; + if (mFluids[index] == null) continue; + + if (checkEnvironment(index, aBaseMetaTileEntity)) return; + + if (shouldDistribute) { + distributeFluid(index, aBaseMetaTileEntity); + mLastReceivedFrom = 0; + } + } + + oLastReceivedFrom = mLastReceivedFrom; + } + } + + private boolean checkEnvironment(int index, IGregTechTileEntity aBaseMetaTileEntity) { + // Check for hot liquids that melt the pipe or gasses that escape and burn/freeze people + final FluidStack tFluid = mFluids[index]; + + if (tFluid != null && tFluid.amount > 0) { + final int tTemperature = tFluid.getFluid() + .getTemperature(tFluid); + if (tTemperature > mHeatResistance) { + if (aBaseMetaTileEntity.getRandomNumber(100) == 0) { + // Poof + GT_Log.exp.println( + "Set Pipe to Fire due to to low heat resistance at " + aBaseMetaTileEntity.getXCoord() + + " | " + + aBaseMetaTileEntity.getYCoord() + + " | " + + aBaseMetaTileEntity.getZCoord() + + " DIMID: " + + aBaseMetaTileEntity.getWorld().provider.dimensionId); + aBaseMetaTileEntity.setToFire(); + return true; + } + // Mmhmm, Fire + aBaseMetaTileEntity.setOnFire(); + GT_Log.exp.println( + "Set Blocks around Pipe to Fire due to to low heat resistance at " + aBaseMetaTileEntity.getXCoord() + + " | " + + aBaseMetaTileEntity.getYCoord() + + " | " + + aBaseMetaTileEntity.getZCoord() + + " DIMID: " + + aBaseMetaTileEntity.getWorld().provider.dimensionId); + } + if (!mGasProof && tFluid.getFluid() + .isGaseous(tFluid)) { + tFluid.amount -= 5; + sendSound((byte) 9); + if (tTemperature > 320) { + try { + for (EntityLivingBase tLiving : getBaseMetaTileEntity().getWorld() + .getEntitiesWithinAABB( + EntityLivingBase.class, + AxisAlignedBB.getBoundingBox( + getBaseMetaTileEntity().getXCoord() - 2, + getBaseMetaTileEntity().getYCoord() - 2, + getBaseMetaTileEntity().getZCoord() - 2, + getBaseMetaTileEntity().getXCoord() + 3, + getBaseMetaTileEntity().getYCoord() + 3, + getBaseMetaTileEntity().getZCoord() + 3))) { + GT_Utility.applyHeatDamage(tLiving, (tTemperature - 300) / 25.0F); + } + } catch (Throwable e) { + if (D1) e.printStackTrace(GT_Log.err); + } + } else if (tTemperature < 260) { + try { + for (EntityLivingBase tLiving : getBaseMetaTileEntity().getWorld() + .getEntitiesWithinAABB( + EntityLivingBase.class, + AxisAlignedBB.getBoundingBox( + getBaseMetaTileEntity().getXCoord() - 2, + getBaseMetaTileEntity().getYCoord() - 2, + getBaseMetaTileEntity().getZCoord() - 2, + getBaseMetaTileEntity().getXCoord() + 3, + getBaseMetaTileEntity().getYCoord() + 3, + getBaseMetaTileEntity().getZCoord() + 3))) { + GT_Utility.applyFrostDamage(tLiving, (270 - tTemperature) / 12.5F); + } + } catch (Throwable e) { + if (D1) e.printStackTrace(GT_Log.err); + } + } + } + if (tFluid.amount <= 0) mFluids[index] = null; + } + return false; + } + + private void distributeFluid(int index, IGregTechTileEntity aBaseMetaTileEntity) { + final FluidStack tFluid = mFluids[index]; + if (tFluid == null) return; + + // Tank, From, Amount to receive + final List<MutableTriple<IFluidHandler, ForgeDirection, Integer>> tTanks = new ArrayList<>(); + final int amount = tFluid.amount; + final byte tOffset = (byte) getBaseMetaTileEntity().getRandomNumber(6); + for (final byte i : ALL_VALID_SIDES) { + // Get a list of tanks accepting fluids, and what side they're on + final ForgeDirection side = ForgeDirection.getOrientation((i + tOffset) % 6); + final ForgeDirection oppositeSide = side.getOpposite(); + final IFluidHandler tTank = aBaseMetaTileEntity.getITankContainerAtSide(side); + final IGregTechTileEntity gTank = tTank instanceof IGregTechTileEntity ? (IGregTechTileEntity) tTank : null; + + if (isConnectedAtSide(side) && tTank != null + && (mLastReceivedFrom & side.flag) == 0 + && getBaseMetaTileEntity().getCoverInfoAtSide(side) + .letsFluidOut(tFluid.getFluid()) + && (gTank == null || gTank.getCoverInfoAtSide(oppositeSide) + .letsFluidIn(tFluid.getFluid()))) { + if (tTank.fill(oppositeSide, tFluid, false) > 0) { + tTanks.add(new MutableTriple<>(tTank, oppositeSide, 0)); + } + tFluid.amount = amount; // Because some mods do actually modify input fluid stack + } + } + + // How much of this fluid is available for distribution? + final double tAmount = Math.max(1, Math.min(mCapacity * 10, tFluid.amount)); + + final FluidStack maxFluid = tFluid.copy(); + maxFluid.amount = Integer.MAX_VALUE; + + double availableCapacity = 0; + // Calculate available capacity for distribution from all tanks + for (final MutableTriple<IFluidHandler, ForgeDirection, Integer> tEntry : tTanks) { + tEntry.right = tEntry.left.fill(tEntry.middle, maxFluid, false); + availableCapacity += tEntry.right; + } + + // Now distribute + for (final MutableTriple<IFluidHandler, ForgeDirection, Integer> tEntry : tTanks) { + // Distribue fluids based on percentage available space at destination + if (availableCapacity > tAmount) + tEntry.right = (int) Math.floor(tEntry.right * tAmount / availableCapacity); + + // If the percent is not enough to give at least 1L, try to give 1L + if (tEntry.right == 0) tEntry.right = (int) Math.min(1, tAmount); + + if (tEntry.right <= 0) continue; + + final int tFilledAmount = tEntry.left + .fill(tEntry.middle, drainFromIndex(tEntry.right, false, index), false); + + if (tFilledAmount > 0) tEntry.left.fill(tEntry.middle, drainFromIndex(tFilledAmount, true, index), true); + + if (mFluids[index] == null || mFluids[index].amount <= 0) return; + } + } + + public void connectPipeOnSide(ForgeDirection side, EntityPlayer entityPlayer) { + if (!isConnectedAtSide(side)) { + if (connect(side) > 0) GT_Utility.sendChatToPlayer(entityPlayer, GT_Utility.trans("214", "Connected")); + } else { + disconnect(side); + GT_Utility.sendChatToPlayer(entityPlayer, GT_Utility.trans("215", "Disconnected")); + } + } + + public void blockPipeOnSide(ForgeDirection side, EntityPlayer entityPlayer, byte mask) { + if (isInputDisabledAtSide(side)) { + mDisableInput &= ~mask; + GT_Utility.sendChatToPlayer(entityPlayer, GT_Utility.trans("212", "Input enabled")); + if (!isConnectedAtSide(side)) connect(side); + } else { + mDisableInput |= mask; + GT_Utility.sendChatToPlayer(entityPlayer, GT_Utility.trans("213", "Input disabled")); + } + } + + @Override + public boolean onWrenchRightClick(ForgeDirection side, ForgeDirection wrenchingSide, EntityPlayer entityPlayer, + float aX, float aY, float aZ, ItemStack aTool) { + + if (GT_Mod.gregtechproxy.gt6Pipe) { + final int mode = GT_MetaGenerated_Tool.getToolMode(aTool); + IGregTechTileEntity currentPipeBase = getBaseMetaTileEntity(); + GT_MetaPipeEntity_Fluid currentPipe = (GT_MetaPipeEntity_Fluid) currentPipeBase.getMetaTileEntity(); + final ForgeDirection tSide = GT_Utility.determineWrenchingSide(side, aX, aY, aZ); + final byte tMask = (byte) (tSide.flag); + + if (mode == ToolModes.REGULAR.get()) { + if (entityPlayer.isSneaking()) { + currentPipe.blockPipeOnSide(tSide, entityPlayer, tMask); + } else currentPipe.connectPipeOnSide(tSide, entityPlayer); + return true; + } + + if (mode == ToolModes.WRENCH_LINE.get()) { + + boolean initialState = entityPlayer.isSneaking() ? currentPipe.isInputDisabledAtSide(tSide) + : currentPipe.isConnectedAtSide(tSide); + + boolean wasActionPerformed = false; + + int limit = GregTech_API.sSpecialFile.get(ConfigCategories.general, "PipeWrenchingChainRange", 64); + for (int connected = 0; connected < limit; connected++) { + + TileEntity nextPipeBaseTile = currentPipeBase.getTileEntityAtSide(tSide); + + // if next tile doesn't exist or if next tile is not GT tile + if (!(nextPipeBaseTile instanceof IGregTechTileEntity nextPipeBase)) { + return wasActionPerformed; + } + + // if next tile is wrong color + if (!currentPipe.connectableColor(nextPipeBaseTile)) { + return wasActionPerformed; + } + + GT_MetaPipeEntity_Fluid nextPipe = nextPipeBase + .getMetaTileEntity() instanceof GT_MetaPipeEntity_Fluid + ? (GT_MetaPipeEntity_Fluid) nextPipeBase.getMetaTileEntity() + : null; + + // if next tile entity is not a pipe + if (nextPipe == null) { + return wasActionPerformed; + } + + // if pipes are same size + if (mPipeAmount != nextPipe.mPipeAmount) { + return wasActionPerformed; + } + + // making sure next pipe has same fluid + for (int i = 0; i < mPipeAmount; i++) { + if (mFluids[i] != null && nextPipe.mFluids[i] != null) { + if (!mFluids[i].isFluidEqual(nextPipe.mFluids[i])) { + return wasActionPerformed; + } + } else if (mFluids[i] != nextPipe.mFluids[i]) { + return wasActionPerformed; + } + } + + boolean currentState = entityPlayer.isSneaking() ? currentPipe.isInputDisabledAtSide(tSide) + : currentPipe.isConnectedAtSide(tSide); + + /* + * Making sure next pipe will have same action applied to it + * e.g. Connecting pipe won`t trigger disconnect if next pipe is already connected + */ + if (currentState != initialState) { + return wasActionPerformed; + } + + if (entityPlayer.isSneaking()) { + currentPipe.blockPipeOnSide(tSide, entityPlayer, tMask); + } else currentPipe.connectPipeOnSide(tSide, entityPlayer); + + wasActionPerformed = true; + + currentPipeBase = (IGregTechTileEntity) nextPipeBase; + currentPipe = nextPipe; + + } + return wasActionPerformed; + } + } + return false; + } + + @Override + public boolean letsIn(GT_CoverBehavior coverBehavior, ForgeDirection side, int aCoverID, int aCoverVariable, + ICoverable aTileEntity) { + return coverBehavior.letsFluidIn(side, aCoverID, aCoverVariable, null, aTileEntity); + } + + @Override + public boolean letsOut(GT_CoverBehavior coverBehavior, ForgeDirection side, int aCoverID, int aCoverVariable, + ICoverable aTileEntity) { + return coverBehavior.letsFluidOut(side, aCoverID, aCoverVariable, null, aTileEntity); + } + + @Override + public boolean letsIn(GT_CoverBehaviorBase<?> coverBehavior, ForgeDirection side, int aCoverID, + ISerializableObject aCoverVariable, ICoverable aTileEntity) { + return coverBehavior.letsFluidIn(side, aCoverID, aCoverVariable, null, aTileEntity); + } + + @Override + public boolean letsOut(GT_CoverBehaviorBase<?> coverBehavior, ForgeDirection side, int aCoverID, + ISerializableObject aCoverVariable, ICoverable aTileEntity) { + return coverBehavior.letsFluidOut(side, aCoverID, aCoverVariable, null, aTileEntity); + } + + @Override + public boolean letsIn(CoverInfo coverInfo) { + return coverInfo.letsFluidIn(null); + } + + @Override + public boolean letsOut(CoverInfo coverInfo) { + return coverInfo.letsFluidOut(null); + } + + @Override + public boolean canConnect(ForgeDirection side, TileEntity tileEntity) { + if (tileEntity == null) return false; + + final ForgeDirection tSide = side.getOpposite(); + final IGregTechTileEntity baseMetaTile = getBaseMetaTileEntity(); + if (baseMetaTile == null) return false; + + final GT_CoverBehaviorBase<?> coverBehavior = baseMetaTile.getCoverBehaviorAtSideNew(side); + final IGregTechTileEntity gTileEntity = (tileEntity instanceof IGregTechTileEntity) + ? (IGregTechTileEntity) tileEntity + : null; + + if (coverBehavior instanceof GT_Cover_Drain + || (TinkerConstruct.isModLoaded() && isTConstructFaucet(tileEntity))) return true; + + final IFluidHandler fTileEntity = (tileEntity instanceof IFluidHandler) ? (IFluidHandler) tileEntity : null; + + if (fTileEntity != null) { + final FluidTankInfo[] tInfo = fTileEntity.getTankInfo(tSide); + if (tInfo != null) { + return tInfo.length > 0 || (Translocator.isModLoaded() && isTranslocator(tileEntity)) + || gTileEntity != null + && gTileEntity.getCoverBehaviorAtSideNew(tSide) instanceof GT_Cover_FluidRegulator; + } + } + return false; + } + + @Optional.Method(modid = Mods.Names.TINKER_CONSTRUCT) + private boolean isTConstructFaucet(TileEntity tTileEntity) { + // Tinker Construct Faucets return a null tank info, so check the class + return tTileEntity instanceof tconstruct.smeltery.logic.FaucetLogic; + } + + @Optional.Method(modid = Mods.Names.TRANSLOCATOR) + private boolean isTranslocator(TileEntity tTileEntity) { + // Translocators return a TankInfo, but it's of 0 length - so check the class if we see this pattern + return tTileEntity instanceof codechicken.translocator.TileLiquidTranslocator; + } + + @Override + public boolean getGT6StyleConnection() { + // Yes if GT6 pipes are enabled + return GT_Mod.gregtechproxy.gt6Pipe; + } + + @Override + public void doSound(byte aIndex, double aX, double aY, double aZ) { + super.doSound(aIndex, aX, aY, aZ); + if (aIndex == 9) { + GT_Utility.doSoundAtClient(SoundResource.RANDOM_FIZZ, 5, 1.0F, aX, aY, aZ); + + new ParticleEventBuilder().setIdentifier(ParticleFX.CLOUD) + .setWorld(getBaseMetaTileEntity().getWorld()) + .<ParticleEventBuilder>times( + 6, + (x, i) -> x + .setMotion( + ForgeDirection.getOrientation(i).offsetX / 5.0, + ForgeDirection.getOrientation(i).offsetY / 5.0, + ForgeDirection.getOrientation(i).offsetZ / 5.0) + .setPosition( + aX - 0.5 + XSTR_INSTANCE.nextFloat(), + aY - 0.5 + XSTR_INSTANCE.nextFloat(), + aZ - 0.5 + XSTR_INSTANCE.nextFloat()) + .run()); + } + } + + @Override + public final int getCapacity() { + return mCapacity * 20 * mPipeAmount; + } + + @Override + public FluidTankInfo getInfo() { + for (FluidStack tFluid : mFluids) { + if (tFluid != null) return new FluidTankInfo(tFluid, mCapacity * 20); + } + return new FluidTankInfo(null, mCapacity * 20); + } + + @Override + public FluidTankInfo[] getTankInfo(ForgeDirection side) { + if (getCapacity() <= 0 && !getBaseMetaTileEntity().hasSteamEngineUpgrade()) return new FluidTankInfo[] {}; + ArrayList<FluidTankInfo> tList = new ArrayList<>(); + for (FluidStack tFluid : mFluids) tList.add(new FluidTankInfo(tFluid, mCapacity * 20)); + return tList.toArray(new FluidTankInfo[mPipeAmount]); + } + + @Override + public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side, + ItemStack aStack) { + return false; + } + + @Override + public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side, + ItemStack aStack) { + return false; + } + + @Override + public final FluidStack getFluid() { + for (FluidStack tFluid : mFluids) { + if (tFluid != null) return tFluid; + } + return null; + } + + @Override + public final int getFluidAmount() { + int rAmount = 0; + for (FluidStack tFluid : mFluids) { + if (tFluid != null) rAmount += tFluid.amount; + } + return rAmount; + } + + @Override + public final int fill_default(ForgeDirection side, FluidStack aFluid, boolean doFill) { + if (aFluid == null || aFluid.getFluid() + .getID() <= 0) return 0; + + int index = -1; + for (int i = 0; i < mPipeAmount; i++) { + if (mFluids[i] != null && mFluids[i].isFluidEqual(aFluid)) { + index = i; + break; + } else if ((mFluids[i] == null || mFluids[i].getFluid() + .getID() <= 0) && index < 0) { + index = i; + } + } + + return fill_default_intoIndex(side, aFluid, doFill, index); + } + + private int fill_default_intoIndex(ForgeDirection side, FluidStack aFluid, boolean doFill, int index) { + if (index < 0 || index >= mPipeAmount) return 0; + if (aFluid == null || aFluid.getFluid() + .getID() <= 0) return 0; + + final int ordinalSide = side.ordinal(); + + if (mFluids[index] == null || mFluids[index].getFluid() + .getID() <= 0) { + if (aFluid.amount * mPipeAmount <= getCapacity()) { + if (doFill) { + mFluids[index] = aFluid.copy(); + mLastReceivedFrom |= (1 << ordinalSide); + } + return aFluid.amount; + } + if (doFill) { + mFluids[index] = aFluid.copy(); + mLastReceivedFrom |= (1 << ordinalSide); + mFluids[index].amount = getCapacity() / mPipeAmount; + } + return getCapacity() / mPipeAmount; + } + + if (!mFluids[index].isFluidEqual(aFluid)) return 0; + + final int space = getCapacity() / mPipeAmount - mFluids[index].amount; + if (aFluid.amount <= space) { + if (doFill) { + mFluids[index].amount += aFluid.amount; + mLastReceivedFrom |= (1 << ordinalSide); + } + return aFluid.amount; + } + if (doFill) { + mFluids[index].amount = getCapacity() / mPipeAmount; + mLastReceivedFrom |= (1 << ordinalSide); + } + return space; + } + + @Override + public final FluidStack drain(int maxDrain, boolean doDrain) { + FluidStack drained; + for (int i = 0; i < mPipeAmount; i++) { + if ((drained = drainFromIndex(maxDrain, doDrain, i)) != null) return drained; + } + return null; + } + + private FluidStack drainFromIndex(int maxDrain, boolean doDrain, int index) { + if (index < 0 || index >= mPipeAmount) return null; + if (mFluids[index] == null) return null; + if (mFluids[index].amount <= 0) { + mFluids[index] = null; + return null; + } + + int used = maxDrain; + if (mFluids[index].amount < used) used = mFluids[index].amount; + + if (doDrain) { + mFluids[index].amount -= used; + } + + final FluidStack drained = mFluids[index].copy(); + drained.amount = used; + + if (mFluids[index].amount <= 0) { + mFluids[index] = null; + } + + return drained; + } + + @Override + public int getTankPressure() { + return getFluidAmount() - (getCapacity() / 2); + } + + @Override + public String[] getDescription() { + List<String> descriptions = new ArrayList<>(); + descriptions.add( + EnumChatFormatting.BLUE + "Fluid Capacity: %%%" + + GT_Utility.formatNumbers(mCapacity * 20L) + + "%%% L/sec" + + EnumChatFormatting.GRAY); + descriptions.add( + EnumChatFormatting.RED + "Heat Limit: %%%" + + GT_Utility.formatNumbers(mHeatResistance) + + "%%% K" + + EnumChatFormatting.GRAY); + if (!mGasProof) { + descriptions.add(EnumChatFormatting.DARK_GREEN + "Cannot handle gas" + EnumChatFormatting.GRAY); + } + if (mPipeAmount != 1) { + descriptions.add(EnumChatFormatting.AQUA + "Pipe Amount: %%%" + mPipeAmount + EnumChatFormatting.GRAY); + } + return descriptions.toArray(new String[0]); + } + + @Override + public float getThickNess() { + if (GT_Mod.instance.isClientSide() && (GT_Client.hideValue & 0x1) != 0) return 0.0625F; + return mThickNess; + } + + @Override + public boolean isLiquidInput(ForgeDirection side) { + return !isInputDisabledAtSide(side); + } + + @Override + public boolean isLiquidOutput(ForgeDirection side) { + return true; + } + + public boolean isInputDisabledAtSide(ForgeDirection side) { + return (mDisableInput & side.flag) != 0; + } + + @Override + public AxisAlignedBB getCollisionBoundingBoxFromPool(World aWorld, int aX, int aY, int aZ) { + if (GT_Mod.instance.isClientSide() && (GT_Client.hideValue & 0x2) != 0) + return AxisAlignedBB.getBoundingBox(aX, aY, aZ, aX + 1, aY + 1, aZ + 1); + else return getActualCollisionBoundingBoxFromPool(aWorld, aX, aY, aZ); + } + + private AxisAlignedBB getActualCollisionBoundingBoxFromPool(World aWorld, int aX, int aY, int aZ) { + final float tSpace = (1f - mThickNess) / 2; + float tSide0 = tSpace; + float tSide1 = 1f - tSpace; + float tSide2 = tSpace; + float tSide3 = 1f - tSpace; + float tSide4 = tSpace; + float tSide5 = 1f - tSpace; + + if (getBaseMetaTileEntity().getCoverIDAtSide(ForgeDirection.DOWN) != 0) { + tSide0 = tSide2 = tSide4 = 0; + tSide3 = tSide5 = 1; + } + if (getBaseMetaTileEntity().getCoverIDAtSide(ForgeDirection.UP) != 0) { + tSide2 = tSide4 = 0; + tSide1 = tSide3 = tSide5 = 1; + } + if (getBaseMetaTileEntity().getCoverIDAtSide(ForgeDirection.NORTH) != 0) { + tSide0 = tSide2 = tSide4 = 0; + tSide1 = tSide5 = 1; + } + if (getBaseMetaTileEntity().getCoverIDAtSide(ForgeDirection.SOUTH) != 0) { + tSide0 = tSide4 = 0; + tSide1 = tSide3 = tSide5 = 1; + } + if (getBaseMetaTileEntity().getCoverIDAtSide(ForgeDirection.WEST) != 0) { + tSide0 = tSide2 = tSide4 = 0; + tSide1 = tSide3 = 1; + } + if (getBaseMetaTileEntity().getCoverIDAtSide(ForgeDirection.EAST) != 0) { + tSide0 = tSide2 = 0; + tSide1 = tSide3 = tSide5 = 1; + } + + final byte tConn = ((BaseMetaPipeEntity) getBaseMetaTileEntity()).mConnections; + if ((tConn & ForgeDirection.DOWN.flag) != 0) tSide0 = 0f; + if ((tConn & ForgeDirection.UP.flag) != 0) tSide1 = 1f; + if ((tConn & ForgeDirection.NORTH.flag) != 0) tSide2 = 0f; + if ((tConn & ForgeDirection.SOUTH.flag) != 0) tSide3 = 1f; + if ((tConn & ForgeDirection.WEST.flag) != 0) tSide4 = 0f; + if ((tConn & ForgeDirection.EAST.flag) != 0) tSide5 = 1f; + + return AxisAlignedBB + .getBoundingBox(aX + tSide4, aY + tSide0, aZ + tSide2, aX + tSide5, aY + tSide1, aZ + tSide3); + } + + @Override + public void addCollisionBoxesToList(World aWorld, int aX, int aY, int aZ, AxisAlignedBB inputAABB, + List<AxisAlignedBB> outputAABB, Entity collider) { + super.addCollisionBoxesToList(aWorld, aX, aY, aZ, inputAABB, outputAABB, collider); + if (GT_Mod.instance.isClientSide() && (GT_Client.hideValue & 0x2) != 0) { + final AxisAlignedBB aabb = getActualCollisionBoundingBoxFromPool(aWorld, aX, aY, aZ); + if (inputAABB.intersectsWith(aabb)) outputAABB.add(aabb); + } + } + + @Override + public FluidStack drain(ForgeDirection side, FluidStack aFluid, boolean doDrain) { + if (aFluid == null) return null; + for (int i = 0; i < mFluids.length; ++i) { + final FluidStack f = mFluids[i]; + if (f == null || !f.isFluidEqual(aFluid)) continue; + return drainFromIndex(aFluid.amount, doDrain, i); + } + return null; + } + + private static EnumMap<Border, ForgeDirection> borderMap(ForgeDirection topSide, ForgeDirection bottomSide, + ForgeDirection leftSide, ForgeDirection rightSide) { + final EnumMap<Border, ForgeDirection> sideMap = new EnumMap<>(Border.class); + sideMap.put(TOP, topSide); + sideMap.put(BOTTOM, bottomSide); + sideMap.put(LEFT, leftSide); + sideMap.put(RIGHT, rightSide); + return sideMap; + } + + protected static ForgeDirection getSideAtBorder(ForgeDirection side, Border border) { + return FACE_BORDER_MAP.get(side) + .get(border); + } + + protected enum Border { + + TOP(), + BOTTOM(), + LEFT(), + RIGHT(); + + public final int mask; + + Border() { + mask = 1 << this.ordinal(); + } + } +} diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaPipeEntity_Frame.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaPipeEntity_Frame.java new file mode 100644 index 0000000000..399c536b9f --- /dev/null +++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaPipeEntity_Frame.java @@ -0,0 +1,150 @@ +package gregtech.api.metatileentity.implementations; + +import static gregtech.api.recipe.RecipeMaps.assemblerRecipes; +import static gregtech.api.util.GT_RecipeBuilder.SECONDS; +import static gregtech.api.util.GT_RecipeBuilder.TICKS; +import static gregtech.api.util.GT_Utility.calculateRecipeEU; + +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraftforge.common.util.ForgeDirection; + +import gregtech.api.enums.Dyes; +import gregtech.api.enums.GT_Values; +import gregtech.api.enums.Materials; +import gregtech.api.enums.OrePrefixes; +import gregtech.api.enums.SubTag; +import gregtech.api.enums.TierEU; +import gregtech.api.interfaces.ITexture; +import gregtech.api.interfaces.metatileentity.IMetaTileEntity; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.metatileentity.MetaPipeEntity; +import gregtech.api.render.TextureFactory; +import gregtech.api.util.GT_LanguageManager; +import gregtech.api.util.GT_ModHandler; +import gregtech.api.util.GT_ModHandler.RecipeBits; +import gregtech.api.util.GT_OreDictUnificator; +import gregtech.api.util.GT_Utility; + +public class GT_MetaPipeEntity_Frame extends MetaPipeEntity { + + private static final String localizedDescFormat = GT_LanguageManager + .addStringLocalization("gt.blockmachines.gt_frame.desc.format", "Just something you can put covers on."); + public final Materials mMaterial; + + public GT_MetaPipeEntity_Frame(int aID, String aName, String aNameRegional, Materials aMaterial) { + super(aID, aName, aNameRegional, 0); + mMaterial = aMaterial; + + GT_OreDictUnificator.registerOre(OrePrefixes.frameGt, aMaterial, getStackForm(1)); + if (aMaterial.getProcessingMaterialTierEU() < TierEU.IV) { + GT_ModHandler.addCraftingRecipe( + getStackForm(2), + RecipeBits.NOT_REMOVABLE | GT_ModHandler.RecipeBits.BUFFERED, + new Object[] { "SSS", "SwS", "SSS", 'S', OrePrefixes.stick.get(mMaterial) }); + } + + if (!aMaterial.contains(SubTag.NO_RECIPES) + && GT_OreDictUnificator.get(OrePrefixes.stick, aMaterial, 1) != null) { + // Auto generate frame box recipe in an assembler. + GT_Values.RA.stdBuilder() + .itemInputs( + GT_OreDictUnificator.get(OrePrefixes.stick, aMaterial, 4), + GT_Utility.getIntegratedCircuit(4)) + .itemOutputs(getStackForm(1)) + .duration(3 * SECONDS + 4 * TICKS) + .eut(calculateRecipeEU(aMaterial, 7)) + .addTo(assemblerRecipes); + } + } + + public GT_MetaPipeEntity_Frame(String aName, Materials aMaterial) { + super(aName, 0); + mMaterial = aMaterial; + } + + @Override + public byte getTileEntityBaseType() { + return (byte) (mMaterial == null ? 4 : (byte) (4) + Math.max(0, Math.min(3, mMaterial.mToolQuality))); + } + + @Override + public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) { + return new GT_MetaPipeEntity_Frame(mName, mMaterial); + } + + @Override + public ITexture[] getTexture(IGregTechTileEntity baseMetaTileEntity, ForgeDirection sideDirection, int connections, + int colorIndex, boolean active, boolean redstoneLevel) { + return new ITexture[] { TextureFactory.of( + mMaterial.mIconSet.mTextures[OrePrefixes.frameGt.mTextureIndex], + Dyes.getModulation(colorIndex, mMaterial.mRGBa)) }; + } + + @Override + public String[] getDescription() { + return localizedDescFormat.split("\\R"); + } + + @Override + public final boolean isSimpleMachine() { + return true; + } + + @Override + public final boolean isFacingValid(ForgeDirection facing) { + return false; + } + + @Override + public final boolean isValidSlot(int aIndex) { + return false; + } + + @Override + public final boolean renderInside(ForgeDirection side) { + return true; + } + + @Override + public final float getThickNess() { + return 1.0F; + } + + @Override + public final void saveNBTData(NBTTagCompound aNBT) { + /* Do nothing */ + } + + @Override + public final void loadNBTData(NBTTagCompound aNBT) { + /* Do nothing */ + } + + @Override + public final boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side, + ItemStack aStack) { + return false; + } + + @Override + public final boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side, + ItemStack aStack) { + return false; + } + + @Override + public int connect(ForgeDirection side) { + return 0; + } + + @Override + public void disconnect(ForgeDirection side) { + /* Do nothing */ + } + + @Override + public boolean isMachineBlockUpdateRecursive() { + return true; + } +} diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaPipeEntity_Item.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaPipeEntity_Item.java new file mode 100644 index 0000000000..660230660e --- /dev/null +++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaPipeEntity_Item.java @@ -0,0 +1,530 @@ +package gregtech.api.metatileentity.implementations; + +import static gregtech.api.enums.GT_Values.ALL_VALID_SIDES; +import static gregtech.api.enums.Textures.BlockIcons.PIPE_RESTRICTOR; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.IInventory; +import net.minecraft.inventory.ISidedInventory; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.tileentity.TileEntityDispenser; +import net.minecraft.tileentity.TileEntityHopper; +import net.minecraft.util.AxisAlignedBB; +import net.minecraft.world.World; +import net.minecraftforge.common.util.ForgeDirection; + +import gregtech.GT_Mod; +import gregtech.api.enums.Dyes; +import gregtech.api.enums.GT_Values; +import gregtech.api.enums.Materials; +import gregtech.api.enums.OrePrefixes; +import gregtech.api.interfaces.ITexture; +import gregtech.api.interfaces.metatileentity.IMetaTileEntity; +import gregtech.api.interfaces.metatileentity.IMetaTileEntityItemPipe; +import gregtech.api.interfaces.tileentity.ICoverable; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.metatileentity.BaseMetaPipeEntity; +import gregtech.api.metatileentity.MetaPipeEntity; +import gregtech.api.render.TextureFactory; +import gregtech.api.util.GT_CoverBehavior; +import gregtech.api.util.GT_CoverBehaviorBase; +import gregtech.api.util.GT_Utility; +import gregtech.api.util.ISerializableObject; +import gregtech.common.GT_Client; +import gregtech.common.covers.CoverInfo; + +public class GT_MetaPipeEntity_Item extends MetaPipeEntity implements IMetaTileEntityItemPipe { + + public final float mThickNess; + public final Materials mMaterial; + public final int mStepSize; + public final int mTickTime; + public int mTransferredItems = 0; + public long mCurrentTransferStartTick = 0; + public ForgeDirection mLastReceivedFrom = ForgeDirection.UNKNOWN, oLastReceivedFrom = ForgeDirection.UNKNOWN; + public boolean mIsRestrictive = false; + private int[] cacheSides; + + public GT_MetaPipeEntity_Item(int aID, String aName, String aNameRegional, float aThickNess, Materials aMaterial, + int aInvSlotCount, int aStepSize, boolean aIsRestrictive, int aTickTime) { + super(aID, aName, aNameRegional, aInvSlotCount, false); + mIsRestrictive = aIsRestrictive; + mThickNess = aThickNess; + mMaterial = aMaterial; + mStepSize = aStepSize; + mTickTime = aTickTime; + addInfo(aID); + } + + public GT_MetaPipeEntity_Item(int aID, String aName, String aNameRegional, float aThickNess, Materials aMaterial, + int aInvSlotCount, int aStepSize, boolean aIsRestrictive) { + this(aID, aName, aNameRegional, aThickNess, aMaterial, aInvSlotCount, aStepSize, aIsRestrictive, 20); + } + + public GT_MetaPipeEntity_Item(String aName, float aThickNess, Materials aMaterial, int aInvSlotCount, int aStepSize, + boolean aIsRestrictive, int aTickTime) { + super(aName, aInvSlotCount); + mIsRestrictive = aIsRestrictive; + mThickNess = aThickNess; + mMaterial = aMaterial; + mStepSize = aStepSize; + mTickTime = aTickTime; + } + + @Override + public byte getTileEntityBaseType() { + return (byte) (mMaterial == null ? 4 : (byte) (4) + Math.max(0, Math.min(3, mMaterial.mToolQuality))); + } + + @Override + public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) { + return new GT_MetaPipeEntity_Item( + mName, + mThickNess, + mMaterial, + mInventory.length, + mStepSize, + mIsRestrictive, + mTickTime); + } + + @Override + public ITexture[] getTexture(IGregTechTileEntity aBaseMetaTileEntity, ForgeDirection side, int aConnections, + int aColorIndex, boolean aConnected, boolean redstoneLevel) { + if (mIsRestrictive) { + if (aConnected) { + float tThickNess = getThickNess(); + if (tThickNess < 0.124F) return new ITexture[] { TextureFactory.of( + mMaterial.mIconSet.mTextures[OrePrefixes.pipe.mTextureIndex], + Dyes.getModulation(aColorIndex, mMaterial.mRGBa)), TextureFactory.of(PIPE_RESTRICTOR) }; + if (tThickNess < 0.374F) // 0.375 + return new ITexture[] { TextureFactory.of( + mMaterial.mIconSet.mTextures[OrePrefixes.pipeTiny.mTextureIndex], + Dyes.getModulation(aColorIndex, mMaterial.mRGBa)), TextureFactory.of(PIPE_RESTRICTOR) }; + if (tThickNess < 0.499F) // 0.500 + return new ITexture[] { TextureFactory.of( + mMaterial.mIconSet.mTextures[OrePrefixes.pipeSmall.mTextureIndex], + Dyes.getModulation(aColorIndex, mMaterial.mRGBa)), TextureFactory.of(PIPE_RESTRICTOR) }; + if (tThickNess < 0.749F) // 0.750 + return new ITexture[] { TextureFactory.of( + mMaterial.mIconSet.mTextures[OrePrefixes.pipeMedium.mTextureIndex], + Dyes.getModulation(aColorIndex, mMaterial.mRGBa)), TextureFactory.of(PIPE_RESTRICTOR) }; + if (tThickNess < 0.874F) // 0.825 + return new ITexture[] { TextureFactory.of( + mMaterial.mIconSet.mTextures[OrePrefixes.pipeLarge.mTextureIndex], + Dyes.getModulation(aColorIndex, mMaterial.mRGBa)), TextureFactory.of(PIPE_RESTRICTOR) }; + return new ITexture[] { TextureFactory.of( + mMaterial.mIconSet.mTextures[OrePrefixes.pipeHuge.mTextureIndex], + Dyes.getModulation(aColorIndex, mMaterial.mRGBa)), TextureFactory.of(PIPE_RESTRICTOR) }; + } + return new ITexture[] { TextureFactory.of( + mMaterial.mIconSet.mTextures[OrePrefixes.pipe.mTextureIndex], + Dyes.getModulation(aColorIndex, mMaterial.mRGBa)), TextureFactory.of(PIPE_RESTRICTOR) }; + } + if (aConnected) { + float tThickNess = getThickNess(); + if (tThickNess < 0.124F) return new ITexture[] { TextureFactory.of( + mMaterial.mIconSet.mTextures[OrePrefixes.pipe.mTextureIndex], + Dyes.getModulation(aColorIndex, mMaterial.mRGBa)) }; + if (tThickNess < 0.374F) // 0.375 + return new ITexture[] { TextureFactory.of( + mMaterial.mIconSet.mTextures[OrePrefixes.pipeTiny.mTextureIndex], + Dyes.getModulation(aColorIndex, mMaterial.mRGBa)) }; + if (tThickNess < 0.499F) // 0.500 + return new ITexture[] { TextureFactory.of( + mMaterial.mIconSet.mTextures[OrePrefixes.pipeSmall.mTextureIndex], + Dyes.getModulation(aColorIndex, mMaterial.mRGBa)) }; + if (tThickNess < 0.749F) // 0.750 + return new ITexture[] { TextureFactory.of( + mMaterial.mIconSet.mTextures[OrePrefixes.pipeMedium.mTextureIndex], + Dyes.getModulation(aColorIndex, mMaterial.mRGBa)) }; + if (tThickNess < 0.874F) // 0.825 + return new ITexture[] { TextureFactory.of( + mMaterial.mIconSet.mTextures[OrePrefixes.pipeLarge.mTextureIndex], + Dyes.getModulation(aColorIndex, mMaterial.mRGBa)) }; + return new ITexture[] { TextureFactory.of( + mMaterial.mIconSet.mTextures[OrePrefixes.pipeHuge.mTextureIndex], + Dyes.getModulation(aColorIndex, mMaterial.mRGBa)) }; + } + return new ITexture[] { TextureFactory.of( + mMaterial.mIconSet.mTextures[OrePrefixes.pipe.mTextureIndex], + Dyes.getModulation(aColorIndex, mMaterial.mRGBa)) }; + } + + @Override + public boolean isSimpleMachine() { + return true; + } + + @Override + public boolean isFacingValid(ForgeDirection facing) { + return false; + } + + @Override + public boolean isValidSlot(int ignoredSlotIndex) { + return true; + } + + @Override + public final boolean renderInside(ForgeDirection side) { + return false; + } + + @Override + public int getProgresstime() { + return getPipeContent() * 64; + } + + @Override + public int maxProgresstime() { + return getMaxPipeCapacity() * 64; + } + + @Override + public void saveNBTData(NBTTagCompound aNBT) { + aNBT.setByte("mLastReceivedFrom", (byte) mLastReceivedFrom.ordinal()); + if (GT_Mod.gregtechproxy.gt6Pipe) aNBT.setByte("mConnections", mConnections); + } + + @Override + public void loadNBTData(NBTTagCompound aNBT) { + mLastReceivedFrom = ForgeDirection.getOrientation(aNBT.getByte("mLastReceivedFrom")); + if (GT_Mod.gregtechproxy.gt6Pipe) { + mConnections = aNBT.getByte("mConnections"); + } + } + + @Override + public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) { + super.onPostTick(aBaseMetaTileEntity, aTick); + if (aBaseMetaTileEntity.isServerSide() && (aTick - mCurrentTransferStartTick) % 10 == 0) { + if ((aTick - mCurrentTransferStartTick) % mTickTime == 0) { + mTransferredItems = 0; + mCurrentTransferStartTick = 0; + } + + if (!GT_Mod.gregtechproxy.gt6Pipe || mCheckConnections) checkConnections(); + + if (oLastReceivedFrom == mLastReceivedFrom) { + doTickProfilingInThisTick = false; + + final ArrayList<IMetaTileEntityItemPipe> tPipeList = new ArrayList<>(); + + for (boolean temp = true; temp && !isInventoryEmpty() && pipeCapacityCheck();) { + temp = false; + tPipeList.clear(); + for (IMetaTileEntityItemPipe tTileEntity : GT_Utility + .sortMapByValuesAcending( + IMetaTileEntityItemPipe.Util.scanPipes(this, new HashMap<>(), 0, false, false)) + .keySet()) { + if (temp) break; + tPipeList.add(tTileEntity); + while (!temp && !isInventoryEmpty() && tTileEntity.sendItemStack(aBaseMetaTileEntity)) + for (IMetaTileEntityItemPipe tPipe : tPipeList) + if (!tPipe.incrementTransferCounter(1)) temp = true; + } + } + } + + if (isInventoryEmpty()) mLastReceivedFrom = ForgeDirection.UNKNOWN; + oLastReceivedFrom = mLastReceivedFrom; + } + } + + @Override + public boolean onWrenchRightClick(ForgeDirection side, ForgeDirection wrenchingSide, EntityPlayer entityPlayer, + float aX, float aY, float aZ) { + if (GT_Mod.gregtechproxy.gt6Pipe) { + final ForgeDirection tSide = GT_Utility.determineWrenchingSide(side, aX, aY, aZ); + if (isConnectedAtSide(tSide)) { + disconnect(tSide); + GT_Utility.sendChatToPlayer(entityPlayer, GT_Utility.trans("215", "Disconnected")); + } else { + if (connect(tSide) > 0) GT_Utility.sendChatToPlayer(entityPlayer, GT_Utility.trans("214", "Connected")); + } + return true; + } + return false; + } + + @Override + public boolean letsIn(GT_CoverBehavior coverBehavior, ForgeDirection side, int aCoverID, int aCoverVariable, + ICoverable aTileEntity) { + return coverBehavior.letsItemsIn(side, aCoverID, aCoverVariable, -1, aTileEntity); + } + + @Override + public boolean letsOut(GT_CoverBehavior coverBehavior, ForgeDirection side, int aCoverID, int aCoverVariable, + ICoverable aTileEntity) { + return coverBehavior.letsItemsOut(side, aCoverID, aCoverVariable, -1, aTileEntity); + } + + @Override + public boolean letsIn(GT_CoverBehaviorBase<?> coverBehavior, ForgeDirection side, int aCoverID, + ISerializableObject aCoverVariable, ICoverable aTileEntity) { + return coverBehavior.letsItemsIn(side, aCoverID, aCoverVariable, -1, aTileEntity); + } + + @Override + public boolean letsOut(GT_CoverBehaviorBase<?> coverBehavior, ForgeDirection side, int aCoverID, + ISerializableObject aCoverVariable, ICoverable aTileEntity) { + return coverBehavior.letsItemsOut(side, aCoverID, aCoverVariable, -1, aTileEntity); + } + + @Override + public boolean letsIn(CoverInfo coverInfo) { + return coverInfo.letsItemsOut(-1); + } + + @Override + public boolean letsOut(CoverInfo coverInfo) { + return coverInfo.letsItemsOut(-1); + } + + @Override + public boolean canConnect(ForgeDirection side, TileEntity tileEntity) { + if (tileEntity == null) return false; + + final ForgeDirection oppositeSide = side.getOpposite(); + boolean connectable = GT_Utility.isConnectableNonInventoryPipe(tileEntity, oppositeSide); + + final IGregTechTileEntity gTileEntity = (tileEntity instanceof IGregTechTileEntity) + ? (IGregTechTileEntity) tileEntity + : null; + if (gTileEntity != null) { + if (gTileEntity.getMetaTileEntity() == null) return false; + if (gTileEntity.getMetaTileEntity() + .connectsToItemPipe(oppositeSide)) return true; + connectable = true; + } + + if (tileEntity instanceof IInventory) { + if (((IInventory) tileEntity).getSizeInventory() <= 0) return false; + connectable = true; + } + if (tileEntity instanceof ISidedInventory) { + final int[] tSlots = ((ISidedInventory) tileEntity).getAccessibleSlotsFromSide(oppositeSide.ordinal()); + if (tSlots == null || tSlots.length == 0) return false; + connectable = true; + } + + return connectable; + } + + @Override + public boolean getGT6StyleConnection() { + // Yes if GT6 pipes are enabled + return GT_Mod.gregtechproxy.gt6Pipe; + } + + @Override + public boolean incrementTransferCounter(int aIncrement) { + if (mTransferredItems == 0) mCurrentTransferStartTick = getBaseMetaTileEntity().getTimer(); + mTransferredItems += aIncrement; + return pipeCapacityCheck(); + } + + @Override + public boolean sendItemStack(Object aSender) { + if (pipeCapacityCheck()) { + final byte tOffset = (byte) getBaseMetaTileEntity().getRandomNumber(6); + for (final byte i : ALL_VALID_SIDES) { + final ForgeDirection tSide = ForgeDirection.getOrientation((i + tOffset) % 6); + if (isConnectedAtSide(tSide) + && (isInventoryEmpty() || (tSide != mLastReceivedFrom || aSender != getBaseMetaTileEntity()))) { + if (insertItemStackIntoTileEntity(aSender, tSide)) return true; + } + } + } + return false; + } + + @Override + public boolean insertItemStackIntoTileEntity(Object aSender, ForgeDirection side) { + if (getBaseMetaTileEntity().getCoverInfoAtSide(side) + .letsItemsOut(-1)) { + final TileEntity tInventory = getBaseMetaTileEntity().getTileEntityAtSide(side); + if (tInventory != null && !(tInventory instanceof BaseMetaPipeEntity)) { + if ((!(tInventory instanceof TileEntityHopper) && !(tInventory instanceof TileEntityDispenser)) + || getBaseMetaTileEntity().getMetaIDAtSide(side) != side.getOpposite() + .ordinal()) { + return GT_Utility.moveMultipleItemStacks( + aSender, + tInventory, + ForgeDirection.UNKNOWN, + side.getOpposite(), + null, + false, + (byte) 64, + (byte) 1, + (byte) 64, + (byte) 1, + 1) > 0; + } + } + } + return false; + } + + @Override + public boolean pipeCapacityCheck() { + return mTransferredItems <= 0 || getPipeContent() < getMaxPipeCapacity(); + } + + private int getPipeContent() { + return mTransferredItems; + } + + private int getMaxPipeCapacity() { + return Math.max(1, getPipeCapacity()); + } + + /** + * Amount of ItemStacks this Pipe can conduct per Second. + */ + public int getPipeCapacity() { + return mInventory.length; + } + + @Override + public int getStepSize() { + return mStepSize; + } + + @Override + public boolean canInsertItem(int aIndex, ItemStack aStack, int ordinalSide) { + return isConnectedAtSide(ForgeDirection.getOrientation(ordinalSide)) + && super.canInsertItem(aIndex, aStack, ordinalSide); + } + + @Override + public boolean canExtractItem(int aIndex, ItemStack aStack, int ordinalSide) { + return isConnectedAtSide(ForgeDirection.getOrientation(ordinalSide)); + } + + @Override + public int[] getAccessibleSlotsFromSide(int ordinalSide) { + final IGregTechTileEntity tTileEntity = getBaseMetaTileEntity(); + final CoverInfo coverInfo = tTileEntity.getCoverInfoAtSide(ForgeDirection.getOrientation(ordinalSide)); + final boolean tAllow = coverInfo.letsItemsIn(-2) || coverInfo.letsItemsOut(-2); + if (tAllow) { + if (cacheSides == null) cacheSides = super.getAccessibleSlotsFromSide(ordinalSide); + return cacheSides; + } else { + return GT_Values.emptyIntArray; + } + } + + @Override + public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side, + ItemStack aStack) { + return isConnectedAtSide(side); + } + + @Override + public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side, + ItemStack aStack) { + if (!isConnectedAtSide(side)) return false; + if (isInventoryEmpty()) mLastReceivedFrom = side; + return mLastReceivedFrom == side && mInventory[aIndex] == null; + } + + @Override + public String[] getDescription() { + if (mTickTime == 20) return new String[] { "Item Capacity: %%%" + getMaxPipeCapacity() + "%%% Stacks/sec", + "Routing Value: %%%" + GT_Utility.formatNumbers(mStepSize) }; + else if (mTickTime % 20 == 0) return new String[] { + "Item Capacity: %%%" + getMaxPipeCapacity() + "%%% Stacks/%%%" + (mTickTime / 20) + "%%% sec", + "Routing Value: %%%" + GT_Utility.formatNumbers(mStepSize) }; + else return new String[] { + "Item Capacity: %%%" + getMaxPipeCapacity() + "%%% Stacks/%%%" + mTickTime + "%%% ticks", + "Routing Value: %%%" + GT_Utility.formatNumbers(mStepSize) }; + } + + private boolean isInventoryEmpty() { + for (ItemStack tStack : mInventory) if (tStack != null) return false; + return true; + } + + @Override + public float getThickNess() { + if (GT_Mod.instance.isClientSide() && (GT_Client.hideValue & 0x1) != 0) return 0.0625F; + return mThickNess; + } + + @Override + public AxisAlignedBB getCollisionBoundingBoxFromPool(World aWorld, int aX, int aY, int aZ) { + if (GT_Mod.instance.isClientSide() && (GT_Client.hideValue & 0x2) != 0) + return AxisAlignedBB.getBoundingBox(aX, aY, aZ, aX + 1, aY + 1, aZ + 1); + else return getActualCollisionBoundingBoxFromPool(aWorld, aX, aY, aZ); + } + + private AxisAlignedBB getActualCollisionBoundingBoxFromPool(World ignoredAWorld, int aX, int aY, int aZ) { + final float tSpace = (1f - mThickNess) / 2; + float spaceDown = tSpace; + float spaceUp = 1f - tSpace; + float spaceNorth = tSpace; + float spaceSouth = 1f - tSpace; + float spaceWest = tSpace; + float spaceEast = 1f - tSpace; + + if (getBaseMetaTileEntity().getCoverIDAtSide(ForgeDirection.DOWN) != 0) { + spaceDown = spaceNorth = spaceWest = 0; + spaceSouth = spaceEast = 1; + } + if (getBaseMetaTileEntity().getCoverIDAtSide(ForgeDirection.UP) != 0) { + spaceNorth = spaceWest = 0; + spaceUp = spaceSouth = spaceEast = 1; + } + if (getBaseMetaTileEntity().getCoverIDAtSide(ForgeDirection.NORTH) != 0) { + spaceDown = spaceNorth = spaceWest = 0; + spaceUp = spaceEast = 1; + } + if (getBaseMetaTileEntity().getCoverIDAtSide(ForgeDirection.SOUTH) != 0) { + spaceDown = spaceWest = 0; + spaceUp = spaceSouth = spaceEast = 1; + } + if (getBaseMetaTileEntity().getCoverIDAtSide(ForgeDirection.WEST) != 0) { + spaceDown = spaceNorth = spaceWest = 0; + spaceUp = spaceSouth = 1; + } + if (getBaseMetaTileEntity().getCoverIDAtSide(ForgeDirection.EAST) != 0) { + spaceDown = spaceNorth = 0; + spaceUp = spaceSouth = spaceEast = 1; + } + + final byte tConn = ((BaseMetaPipeEntity) getBaseMetaTileEntity()).mConnections; + if ((tConn & ForgeDirection.DOWN.flag) != 0) spaceDown = 0f; + if ((tConn & ForgeDirection.UP.flag) != 0) spaceUp = 1f; + if ((tConn & ForgeDirection.NORTH.flag) != 0) spaceNorth = 0f; + if ((tConn & ForgeDirection.SOUTH.flag) != 0) spaceSouth = 1f; + if ((tConn & ForgeDirection.WEST.flag) != 0) spaceWest = 0f; + if ((tConn & ForgeDirection.EAST.flag) != 0) spaceEast = 1f; + + return AxisAlignedBB.getBoundingBox( + aX + spaceWest, + aY + spaceDown, + aZ + spaceNorth, + aX + spaceEast, + aY + spaceUp, + aZ + spaceSouth); + } + + @Override + public void addCollisionBoxesToList(World aWorld, int aX, int aY, int aZ, AxisAlignedBB inputAABB, + List<AxisAlignedBB> outputAABB, Entity collider) { + super.addCollisionBoxesToList(aWorld, aX, aY, aZ, inputAABB, outputAABB, collider); + if (GT_Mod.instance.isClientSide() && (GT_Client.hideValue & 0x2) != 0) { + final AxisAlignedBB aabb = getActualCollisionBoundingBoxFromPool(aWorld, aX, aY, aZ); + if (inputAABB.intersectsWith(aabb)) outputAABB.add(aabb); + } + } +} diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicBatteryBuffer.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicBatteryBuffer.java new file mode 100644 index 0000000000..ece64e2c1d --- /dev/null +++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicBatteryBuffer.java @@ -0,0 +1,441 @@ +package gregtech.api.metatileentity.implementations; + +import static gregtech.api.enums.GT_Values.V; + +import java.util.List; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.StatCollector; +import net.minecraft.world.World; +import net.minecraftforge.common.util.ForgeDirection; + +import com.gtnewhorizons.modularui.api.screen.ModularWindow; +import com.gtnewhorizons.modularui.api.screen.UIBuildContext; +import com.gtnewhorizons.modularui.common.internal.wrapper.BaseSlot; +import com.gtnewhorizons.modularui.common.widget.SlotGroup; + +import gregtech.api.enums.Textures; +import gregtech.api.gui.modularui.GT_UIInfos; +import gregtech.api.interfaces.ITexture; +import gregtech.api.interfaces.metatileentity.IMetaTileEntity; +import gregtech.api.interfaces.modularui.IAddUIWidgets; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.items.GT_MetaBase_Item; +import gregtech.api.util.GT_ModHandler; +import gregtech.api.util.GT_Utility; +import ic2.api.item.IElectricItem; +import mcp.mobius.waila.api.IWailaConfigHandler; +import mcp.mobius.waila.api.IWailaDataAccessor; + +/** + * NEVER INCLUDE THIS FILE IN YOUR MOD!!! + * <p/> + * This is the main construct for my Basic Machines such as the Automatic Extractor Extend this class to make a simple + * Machine + */ +public class GT_MetaTileEntity_BasicBatteryBuffer extends GT_MetaTileEntity_TieredMachineBlock + implements IAddUIWidgets { + + public boolean mCharge = false, mDecharge = false; + public int mBatteryCount = 0, mChargeableCount = 0; + private long count = 0; + private long mStored = 0; + private long mMax = 0; + + public GT_MetaTileEntity_BasicBatteryBuffer(int aID, String aName, String aNameRegional, int aTier, + String aDescription, int aSlotCount) { + super(aID, aName, aNameRegional, aTier, aSlotCount, aDescription); + } + + public GT_MetaTileEntity_BasicBatteryBuffer(String aName, int aTier, String aDescription, ITexture[][][] aTextures, + int aSlotCount) { + super(aName, aTier, aSlotCount, aDescription, aTextures); + } + + public GT_MetaTileEntity_BasicBatteryBuffer(String aName, int aTier, String[] aDescription, + ITexture[][][] aTextures, int aSlotCount) { + super(aName, aTier, aSlotCount, aDescription, aTextures); + } + + @Override + public String[] getDescription() { + String[] desc = new String[mDescriptionArray.length + 1]; + System.arraycopy(mDescriptionArray, 0, desc, 0, mDescriptionArray.length); + desc[mDescriptionArray.length] = mInventory.length + " Slots"; + return desc; + } + + @Override + public ITexture[][][] getTextureSet(ITexture[] aTextures) { + ITexture[][][] rTextures = new ITexture[2][17][]; + for (byte i = -1; i < 16; i++) { + rTextures[0][i + 1] = new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][i + 1] }; + rTextures[1][i + 1] = new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][i + 1], + mInventory.length == 16 ? Textures.BlockIcons.OVERLAYS_ENERGY_OUT_POWER[mTier] + : mInventory.length > 4 ? Textures.BlockIcons.OVERLAYS_ENERGY_OUT_MULTI[mTier] + : Textures.BlockIcons.OVERLAYS_ENERGY_OUT[mTier] }; + } + return rTextures; + } + + @Override + public ITexture[] getTexture(IGregTechTileEntity aBaseMetaTileEntity, ForgeDirection side, ForgeDirection aFacing, + int colorIndex, boolean aActive, boolean redstoneLevel) { + return mTextures[side == aFacing ? 1 : 0][colorIndex + 1]; + } + + @Override + public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) { + return new GT_MetaTileEntity_BasicBatteryBuffer(mName, mTier, mDescriptionArray, mTextures, mInventory.length); + } + + @Override + public boolean isSimpleMachine() { + return false; + } + + @Override + public boolean isElectric() { + return true; + } + + @Override + public boolean isValidSlot(int aIndex) { + return true; + } + + @Override + public boolean isFacingValid(ForgeDirection facing) { + return true; + } + + @Override + public boolean isEnetInput() { + return true; + } + + @Override + public boolean isEnetOutput() { + return true; + } + + @Override + public boolean isInputFacing(ForgeDirection side) { + return side != getBaseMetaTileEntity().getFrontFacing(); + } + + @Override + public boolean isOutputFacing(ForgeDirection side) { + return side == getBaseMetaTileEntity().getFrontFacing(); + } + + @Override + public boolean isTeleporterCompatible() { + return false; + } + + @Override + public long getMinimumStoredEU() { + return V[mTier] * 16L * mInventory.length; + } + + @Override + public long maxEUStore() { + return V[mTier] * 64L * mInventory.length; + } + + @Override + public long maxEUInput() { + return V[mTier]; + } + + @Override + public long maxEUOutput() { + return V[mTier]; + } + + @Override + public long maxAmperesIn() { + return mChargeableCount * 2L; + } + + @Override + public long maxAmperesOut() { + return mBatteryCount; + } + + @Override + public int rechargerSlotStartIndex() { + return 0; + } + + @Override + public int dechargerSlotStartIndex() { + return 0; + } + + @Override + public int rechargerSlotCount() { + return mCharge ? mInventory.length : 0; + } + + @Override + public int dechargerSlotCount() { + return mDecharge ? mInventory.length : 0; + } + + @Override + public int getProgresstime() { + return (int) getBaseMetaTileEntity().getUniversalEnergyStored(); + } + + @Override + public int maxProgresstime() { + return (int) getBaseMetaTileEntity().getUniversalEnergyCapacity(); + } + + @Override + public boolean isAccessAllowed(EntityPlayer aPlayer) { + return true; + } + + @Override + public void saveNBTData(NBTTagCompound aNBT) { + // + } + + @Override + public void loadNBTData(NBTTagCompound aNBT) { + // + } + + @Override + public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer) { + GT_UIInfos.openGTTileEntityUI(aBaseMetaTileEntity, aPlayer); + return true; + } + + @Override + public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) { + if (aBaseMetaTileEntity.isServerSide()) { + mCharge = aBaseMetaTileEntity.getStoredEU() / 2 > aBaseMetaTileEntity.getEUCapacity() / 3; + mDecharge = aBaseMetaTileEntity.getStoredEU() < aBaseMetaTileEntity.getEUCapacity() / 3; + mBatteryCount = 0; + mChargeableCount = 0; + for (ItemStack tStack : mInventory) if (GT_ModHandler.isElectricItem(tStack, mTier)) { + if (GT_ModHandler.isChargerItem(tStack)) mBatteryCount++; + mChargeableCount++; + } + } + count++; + } + + @Override + public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side, + ItemStack aStack) { + if (GT_ModHandler.isElectricItem(aStack) && aStack.getUnlocalizedName() + .startsWith("gt.metaitem.01.")) { + String name = aStack.getUnlocalizedName(); + if (name.equals("gt.metaitem.01.32510") || name.equals("gt.metaitem.01.32511") + || name.equals("gt.metaitem.01.32520") + || name.equals("gt.metaitem.01.32521") + || name.equals("gt.metaitem.01.32530") + || name.equals("gt.metaitem.01.32531")) { + return ic2.api.item.ElectricItem.manager.getCharge(aStack) == 0; + } + } + return false; + } + + @Override + public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side, + ItemStack aStack) { + if (!GT_Utility.isStackValid(aStack)) { + return false; + } + return mInventory[aIndex] == null && GT_ModHandler.isElectricItem(aStack, this.mTier); + } + + @Override + public int getInventoryStackLimit() { + return 1; + } + + public long[] getStoredEnergy() { + boolean scaleOverflow = false; + boolean storedOverflow = false; + long tScale = getBaseMetaTileEntity().getEUCapacity(); + long tStored = getBaseMetaTileEntity().getStoredEU(); + long tStep = 0; + if (mInventory != null) { + for (ItemStack aStack : mInventory) { + if (GT_ModHandler.isElectricItem(aStack)) { + + if (aStack.getItem() instanceof GT_MetaBase_Item) { + Long[] stats = ((GT_MetaBase_Item) aStack.getItem()).getElectricStats(aStack); + if (stats != null) { + if (stats[0] > Long.MAX_VALUE / 2) { + scaleOverflow = true; + } + tScale = tScale + stats[0]; + tStep = ((GT_MetaBase_Item) aStack.getItem()).getRealCharge(aStack); + if (tStep > Long.MAX_VALUE / 2) { + storedOverflow = true; + } + tStored = tStored + tStep; + } + } else if (aStack.getItem() instanceof IElectricItem) { + tStored = tStored + (long) ic2.api.item.ElectricItem.manager.getCharge(aStack); + tScale = tScale + (long) ((IElectricItem) aStack.getItem()).getMaxCharge(aStack); + } + } + } + } + if (scaleOverflow) { + tScale = Long.MAX_VALUE; + } + if (storedOverflow) { + tStored = Long.MAX_VALUE; + } + return new long[] { tStored, tScale }; + } + + @Override + public String[] getInfoData() { + updateStorageInfo(); + + return new String[] { EnumChatFormatting.BLUE + getLocalName() + EnumChatFormatting.RESET, "Stored Items:", + EnumChatFormatting.GREEN + GT_Utility.formatNumbers(mStored) + + EnumChatFormatting.RESET + + " EU / " + + EnumChatFormatting.YELLOW + + GT_Utility.formatNumbers(mMax) + + EnumChatFormatting.RESET + + " EU", + "Average input:", GT_Utility.formatNumbers(getBaseMetaTileEntity().getAverageElectricInput()) + " EU/t", + "Average output:", GT_Utility.formatNumbers(getBaseMetaTileEntity().getAverageElectricOutput()) + " EU/t" }; + } + + private void updateStorageInfo() { + if (mMax == 0 || (count > 20)) { + long[] tmp = getStoredEnergy(); + mStored = tmp[0]; + mMax = tmp[1]; + count = 0; + } + } + + @Override + public void getWailaBody(ItemStack itemStack, List<String> currenttip, IWailaDataAccessor accessor, + IWailaConfigHandler config) { + NBTTagCompound tag = accessor.getNBTData(); + currenttip.add( + StatCollector.translateToLocalFormatted( + "GT5U.waila.energy.stored", + GT_Utility.formatNumbers(tag.getLong("mStored")), + GT_Utility.formatNumbers(tag.getLong("mMax")))); + long avgIn = tag.getLong("AvgIn"); + long avgOut = tag.getLong("AvgOut"); + currenttip.add( + StatCollector.translateToLocalFormatted( + "GT5U.waila.energy.avg_in_with_amperage", + GT_Utility.formatNumbers(avgIn), + GT_Utility.getAmperageForTier(avgIn, (byte) getInputTier()), + GT_Utility.getColoredTierNameFromTier((byte) getInputTier()))); + currenttip.add( + StatCollector.translateToLocalFormatted( + "GT5U.waila.energy.avg_out_with_amperage", + GT_Utility.formatNumbers(avgOut), + GT_Utility.getAmperageForTier(avgOut, (byte) getOutputTier()), + GT_Utility.getColoredTierNameFromTier((byte) getOutputTier()))); + super.getWailaBody(itemStack, currenttip, accessor, config); + } + + @Override + public void getWailaNBTData(EntityPlayerMP player, TileEntity tile, NBTTagCompound tag, World world, int x, int y, + int z) { + updateStorageInfo(); + super.getWailaNBTData(player, tile, tag, world, x, y, z); + tag.setLong("mStored", mStored); + tag.setLong("mMax", mMax); + tag.setLong("AvgIn", getBaseMetaTileEntity().getAverageElectricInput()); + tag.setLong("AvgOut", getBaseMetaTileEntity().getAverageElectricOutput()); + } + + @Override + public boolean isGivingInformation() { + return true; + } + + @Override + public boolean useModularUI() { + return true; + } + + @Override + public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) { + switch (mInventory.length) { + case 4 -> builder.widget( + SlotGroup.ofItemHandler(inventoryHandler, 2) + .startFromSlot(0) + .endAtSlot(3) + .slotCreator(index -> new BaseSlot(inventoryHandler, index) { + + @Override + public int getSlotStackLimit() { + return 1; + } + }) + .background(getGUITextureSet().getItemSlot()) + .build() + .setPos(70, 25)); + case 9 -> builder.widget( + SlotGroup.ofItemHandler(inventoryHandler, 3) + .startFromSlot(0) + .endAtSlot(8) + .slotCreator(index -> new BaseSlot(inventoryHandler, index) { + + @Override + public int getSlotStackLimit() { + return 1; + } + }) + .background(getGUITextureSet().getItemSlot()) + .build() + .setPos(61, 16)); + case 16 -> builder.widget( + SlotGroup.ofItemHandler(inventoryHandler, 4) + .startFromSlot(0) + .endAtSlot(15) + .slotCreator(index -> new BaseSlot(inventoryHandler, index) { + + @Override + public int getSlotStackLimit() { + return 1; + } + }) + .background(getGUITextureSet().getItemSlot()) + .build() + .setPos(52, 7)); + default -> builder.widget( + SlotGroup.ofItemHandler(inventoryHandler, 1) + .startFromSlot(0) + .endAtSlot(0) + .slotCreator(index -> new BaseSlot(inventoryHandler, index) { + + @Override + public int getSlotStackLimit() { + return 1; + } + }) + .background(getGUITextureSet().getItemSlot()) + .build() + .setPos(79, 34)); + } + } +} diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicGenerator.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicGenerator.java new file mode 100644 index 0000000000..897f9dad6f --- /dev/null +++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicGenerator.java @@ -0,0 +1,344 @@ +package gregtech.api.metatileentity.implementations; + +import static gregtech.api.enums.GT_Values.V; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraftforge.common.util.ForgeDirection; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.IFluidContainerItem; +import net.minecraftforge.fluids.IFluidHandler; + +import gregtech.api.enums.ItemList; +import gregtech.api.enums.OrePrefixes; +import gregtech.api.enums.Textures; +import gregtech.api.gui.modularui.GT_UIInfos; +import gregtech.api.interfaces.ITexture; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.interfaces.tileentity.RecipeMapWorkable; +import gregtech.api.objects.ItemData; +import gregtech.api.recipe.RecipeMap; +import gregtech.api.recipe.maps.FuelBackend; +import gregtech.api.util.GT_OreDictUnificator; +import gregtech.api.util.GT_Recipe; +import gregtech.api.util.GT_Utility; +import gregtech.common.GT_Pollution; + +public abstract class GT_MetaTileEntity_BasicGenerator extends GT_MetaTileEntity_BasicTank + implements RecipeMapWorkable { + + public GT_MetaTileEntity_BasicGenerator(int aID, String aName, String aNameRegional, int aTier, String aDescription, + ITexture... aTextures) { + super(aID, aName, aNameRegional, aTier, 3, aDescription, aTextures); + } + + public GT_MetaTileEntity_BasicGenerator(int aID, String aName, String aNameRegional, int aTier, + String[] aDescription, ITexture... aTextures) { + super(aID, aName, aNameRegional, aTier, 3, aDescription, aTextures); + } + + public GT_MetaTileEntity_BasicGenerator(String aName, int aTier, String aDescription, ITexture[][][] aTextures) { + super(aName, aTier, 3, aDescription, aTextures); + } + + public GT_MetaTileEntity_BasicGenerator(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures) { + super(aName, aTier, 3, aDescription, aTextures); + } + + @Override + public ITexture[][][] getTextureSet(ITexture[] aTextures) { + ITexture[][][] rTextures = new ITexture[10][17][]; + for (byte i = -1; i < 16; i++) { + rTextures[0][i + 1] = getFront(i); + rTextures[1][i + 1] = getBack(i); + rTextures[2][i + 1] = getBottom(i); + rTextures[3][i + 1] = getTop(i); + rTextures[4][i + 1] = getSides(i); + rTextures[5][i + 1] = getFrontActive(i); + rTextures[6][i + 1] = getBackActive(i); + rTextures[7][i + 1] = getBottomActive(i); + rTextures[8][i + 1] = getTopActive(i); + rTextures[9][i + 1] = getSidesActive(i); + } + return rTextures; + } + + @Override + public ITexture[] getTexture(IGregTechTileEntity baseMetaTileEntity, ForgeDirection side, + ForgeDirection facingDirection, int colorIndex, boolean active, boolean redstoneLevel) { + return mTextures[(active ? 5 : 0) + (side == facingDirection ? 0 + : side == facingDirection.getOpposite() ? 1 + : side == ForgeDirection.DOWN ? 2 : side == ForgeDirection.UP ? 3 : 4)][colorIndex + 1]; + } + + @Override + public String[] getDescription() { + String[] desc = new String[mDescriptionArray.length + 1]; + System.arraycopy(mDescriptionArray, 0, desc, 0, mDescriptionArray.length); + desc[mDescriptionArray.length] = "Fuel Efficiency: " + getEfficiency() + "%"; + return desc; + } + + @Override + public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer) { + GT_UIInfos.openGTTileEntityUI(aBaseMetaTileEntity, aPlayer); + return true; + } + + public ITexture[] getFront(byte aColor) { + return new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][aColor + 1] }; + } + + public ITexture[] getBack(byte aColor) { + return new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][aColor + 1] }; + } + + public ITexture[] getBottom(byte aColor) { + return new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][aColor + 1] }; + } + + public ITexture[] getTop(byte aColor) { + return new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][aColor + 1] }; + } + + public ITexture[] getSides(byte aColor) { + return new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][aColor + 1] }; + } + + public ITexture[] getFrontActive(byte aColor) { + return getFront(aColor); + } + + public ITexture[] getBackActive(byte aColor) { + return getBack(aColor); + } + + public ITexture[] getBottomActive(byte aColor) { + return getBottom(aColor); + } + + public ITexture[] getTopActive(byte aColor) { + return getTop(aColor); + } + + public ITexture[] getSidesActive(byte aColor) { + return getSides(aColor); + } + + @Override + public boolean isFacingValid(ForgeDirection side) { + return true; + } + + @Override + public boolean isSimpleMachine() { + return false; + } + + @Override + public boolean isValidSlot(int aIndex) { + return aIndex < 2; + } + + @Override + public boolean isEnetOutput() { + return true; + } + + @Override + public boolean isOutputFacing(ForgeDirection side) { + return true; + } + + @Override + public boolean isAccessAllowed(EntityPlayer aPlayer) { + return true; + } + + @Override + public long maxEUOutput() { + return getBaseMetaTileEntity().isAllowedToWork() ? V[mTier] : 0L; + } + + @Override + public long maxEUStore() { + return Math.max(getEUVar(), V[mTier] * 80L + getMinimumStoredEU()); + } + + @Override + public boolean doesFillContainers() { + return getBaseMetaTileEntity().isAllowedToWork(); + // return false; + } + + @Override + public boolean doesEmptyContainers() { + return getBaseMetaTileEntity().isAllowedToWork(); + } + + @Override + public boolean canTankBeFilled() { + return getBaseMetaTileEntity().isAllowedToWork(); + } + + @Override + public boolean canTankBeEmptied() { + return getBaseMetaTileEntity().isAllowedToWork(); + } + + @Override + public boolean displaysItemStack() { + return true; + } + + @Override + public boolean displaysStackSize() { + return false; + } + + @Override + public boolean isFluidInputAllowed(FluidStack aFluid) { + return getFuelValue(aFluid) > 0; + } + + @Override + public boolean isLiquidOutput(ForgeDirection side) { + // return super.isLiquidOutput(aSide); + return false; + } + + @Override + public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) { + if (aBaseMetaTileEntity.isServerSide() && aBaseMetaTileEntity.isAllowedToWork() && aTick % 10 == 0) { + if (mFluid != null) { + long tFuelValue = getFuelValue(mFluid), tConsumed = consumedFluidPerOperation(mFluid); + if (tFuelValue > 0 && tConsumed > 0 && mFluid.amount >= tConsumed) { + long tFluidAmountToUse = Math.min( + mFluid.amount / tConsumed, + (maxEUStore() - aBaseMetaTileEntity.getUniversalEnergyStored()) / tFuelValue); + if (tFluidAmountToUse > 0 + && aBaseMetaTileEntity.increaseStoredEnergyUnits(tFluidAmountToUse * tFuelValue, true)) { + // divided by two because this is called every 10 ticks, not 20 + GT_Pollution.addPollution(getBaseMetaTileEntity(), getPollution() / 2); + mFluid.amount -= tFluidAmountToUse * tConsumed; + } + } + } + + if (mInventory[getInputSlot()] != null + && aBaseMetaTileEntity.getUniversalEnergyStored() < (maxEUOutput() * 20 + getMinimumStoredEU()) + && ((GT_Utility.getFluidForFilledItem(mInventory[getInputSlot()], true) != null) + || solidFuelOverride(mInventory[getInputSlot()]))) { + long tFuelValue = getFuelValue(mInventory[getInputSlot()]); + if (tFuelValue <= 0) tFuelValue = getFuelValue(mInventory[getInputSlot()], true); + // System.out.println(" tFuelValue : " + tFuelValue ); + if (tFuelValue > 0) { + ItemStack tEmptyContainer = getEmptyContainer(mInventory[getInputSlot()]); + if (aBaseMetaTileEntity.addStackToSlot(getOutputSlot(), tEmptyContainer)) { + aBaseMetaTileEntity.increaseStoredEnergyUnits(tFuelValue, true); + aBaseMetaTileEntity.decrStackSize(getInputSlot(), 1); + // divided by two because this is called every 10 ticks, not 20 + GT_Pollution.addPollution(getBaseMetaTileEntity(), getPollution() / 2); + } + } + } + } + + if (aBaseMetaTileEntity.isServerSide()) aBaseMetaTileEntity.setActive( + aBaseMetaTileEntity.isAllowedToWork() + && aBaseMetaTileEntity.getUniversalEnergyStored() >= maxEUOutput() + getMinimumStoredEU()); + } + + /** + * @param stack the fuel stack + * @return if the stack is a solid fuel + */ + public boolean solidFuelOverride(ItemStack stack) { + // this could be used for a coal generator for example aswell... + ItemData association = GT_OreDictUnificator.getAssociation(stack); + // if it is a gregtech Item, make sure its not a VOLUMETRIC_FLASK or any type of cell, else do vanilla checks + if (association != null) { + return !OrePrefixes.CELL_TYPES.contains(association.mPrefix) + && !GT_Utility.areStacksEqual(ItemList.VOLUMETRIC_FLASK.get(1L), stack, true); + } else { + return stack != null && // when the stack is null its not a solid + stack.getItem() != null && // when the item in the stack is null its not a solid + !(stack.getItem() instanceof IFluidContainerItem) && // when the item is a fluid container its not a + // solid... + !(stack.getItem() instanceof IFluidHandler) && // when the item is a fluid handler its not a + // solid... + !stack.getItem() + .getUnlocalizedName() + .contains("bucket"); // since we cant really check for + // buckets... + } + } + + public abstract int getPollution(); + + @Override + public abstract RecipeMap<?> getRecipeMap(); + + public abstract int getEfficiency(); + + public int consumedFluidPerOperation(FluidStack aLiquid) { + return 1; + } + + public int getFuelValue(FluidStack aLiquid) { + long value = getFuelValue(aLiquid, true); + return (value > Integer.MAX_VALUE) ? 0 : (int) value; + } + + public long getFuelValue(FluidStack aLiquid, boolean aLong) { + RecipeMap<?> tRecipes = getRecipeMap(); + if (aLiquid == null || !(tRecipes.getBackend() instanceof FuelBackend tFuels)) return 0; + GT_Recipe tFuel = tFuels.findFuel(aLiquid); + if (tFuel == null) return 0; + + return (long) tFuel.mSpecialValue * getEfficiency() * consumedFluidPerOperation(aLiquid) / 100; + } + + public int getFuelValue(ItemStack aStack) { + long value = getFuelValue(aStack, true); + return (value > Integer.MAX_VALUE) ? 0 : (int) value; + } + + public long getFuelValue(ItemStack aStack, boolean aLong) { + if (GT_Utility.isStackInvalid(aStack) || getRecipeMap() == null) return 0; + GT_Recipe tFuel = getRecipeMap().findRecipe(getBaseMetaTileEntity(), false, Long.MAX_VALUE, null, aStack); + if (tFuel == null) return 0; + + long liters = 10L; // 1000mb/100 + return (long) tFuel.mSpecialValue * liters * getEfficiency(); + } + + public ItemStack getEmptyContainer(ItemStack aStack) { + if (GT_Utility.isStackInvalid(aStack) || getRecipeMap() == null) return null; + GT_Recipe tFuel = getRecipeMap().findRecipe(getBaseMetaTileEntity(), false, Long.MAX_VALUE, null, aStack); + if (tFuel != null) return GT_Utility.copyOrNull(tFuel.getOutput(0)); + return GT_Utility.getContainerItem(aStack, true); + } + + @Override + public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side, + ItemStack aStack) { + return super.allowPutStack(aBaseMetaTileEntity, aIndex, side, aStack) && (getFuelValue(aStack, true) > 0 + || getFuelValue(GT_Utility.getFluidForFilledItem(aStack, true), true) > 0); + } + + @Override + public int getCapacity() { + return 16000; + } + + @Override + public int getTankPressure() { + return -100; + } + + @Override + public boolean useModularUI() { + return true; + } +} diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicHull.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicHull.java new file mode 100644 index 0000000000..e5766eee39 --- /dev/null +++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicHull.java @@ -0,0 +1,175 @@ +package gregtech.api.metatileentity.implementations; + +import static gregtech.api.enums.GT_Values.V; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraftforge.common.util.ForgeDirection; + +import gregtech.api.enums.Textures; +import gregtech.api.interfaces.ITexture; +import gregtech.api.interfaces.metatileentity.IMetaTileEntity; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; + +public class GT_MetaTileEntity_BasicHull extends GT_MetaTileEntity_BasicTank { + + public GT_MetaTileEntity_BasicHull(int aID, String aName, String aNameRegional, int aTier, String aDescription, + ITexture... aTextures) { + super(aID, aName, aNameRegional, aTier, 1, aDescription, aTextures); + } + + public GT_MetaTileEntity_BasicHull(int aID, String aName, String aNameRegional, int aTier, int aInvSlotCount, + String aDescription, ITexture... aTextures) { + super(aID, aName, aNameRegional, aTier, aInvSlotCount, aDescription, aTextures); + } + + public GT_MetaTileEntity_BasicHull(String aName, int aTier, int aInvSlotCount, String aDescription, + ITexture[][][] aTextures) { + super(aName, aTier, aInvSlotCount, aDescription, aTextures); + } + + public GT_MetaTileEntity_BasicHull(String aName, int aTier, int aInvSlotCount, String[] aDescription, + ITexture[][][] aTextures) { + super(aName, aTier, aInvSlotCount, aDescription, aTextures); + } + + @Override + public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) { + return new GT_MetaTileEntity_BasicHull(mName, mTier, mInventory.length, mDescriptionArray, mTextures); + } + + @Override + public boolean isElectric() { + return true; + } + + @Override + public boolean isEnetInput() { + return true; + } + + @Override + public boolean isEnetOutput() { + return true; + } + + @Override + public boolean isAccessAllowed(EntityPlayer aPlayer) { + return true; + } + + @Override + public boolean isInputFacing(ForgeDirection side) { + return !isOutputFacing(side); + } + + @Override + public boolean isOutputFacing(ForgeDirection side) { + return side == getBaseMetaTileEntity().getFrontFacing(); + } + + @Override + public long getMinimumStoredEU() { + return 512; + } + + @Override + public long maxEUStore() { + return 512 + V[mTier] * 50; + } + + @Override + public long maxEUInput() { + return V[mTier]; + } + + @Override + public long maxEUOutput() { + return V[mTier]; + } + + @Override + public boolean isSimpleMachine() { + return true; + } + + @Override + public boolean isFacingValid(ForgeDirection facing) { + return true; + } + + @Override + public boolean isValidSlot(int aIndex) { + return true; + } + + @Override + public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side, + ItemStack aStack) { + return true; + } + + @Override + public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side, + ItemStack aStack) { + return true; + } + + @Override + public ITexture[] getTexture(IGregTechTileEntity aBaseMetaTileEntity, ForgeDirection side, ForgeDirection aFacing, + int colorIndex, boolean aConnected, boolean redstoneLevel) { + return mTextures[Math.min(2, side.ordinal()) + (side == aFacing ? 3 : 0)][colorIndex + 1]; + } + + @Override + public ITexture[][][] getTextureSet(ITexture[] aTextures) { + ITexture[][][] rTextures = new ITexture[6][17][]; + for (byte i = -1; i < 16; i++) { + rTextures[0][i + 1] = new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][i + 1] }; + rTextures[1][i + 1] = new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][i + 1] }; + rTextures[2][i + 1] = new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][i + 1] }; + rTextures[3][i + 1] = new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][i + 1], + Textures.BlockIcons.OVERLAYS_ENERGY_OUT[mTier] }; + rTextures[4][i + 1] = new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][i + 1], + Textures.BlockIcons.OVERLAYS_ENERGY_OUT[mTier] }; + rTextures[5][i + 1] = new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][i + 1], + Textures.BlockIcons.OVERLAYS_ENERGY_OUT[mTier] }; + } + return rTextures; + } + + @Override + public boolean doesFillContainers() { + return false; + } + + @Override + public boolean doesEmptyContainers() { + return false; + } + + @Override + public boolean canTankBeFilled() { + return true; + } + + @Override + public boolean canTankBeEmptied() { + return true; + } + + @Override + public boolean displaysItemStack() { + return false; + } + + @Override + public boolean displaysStackSize() { + return false; + } + + @Override + public int getCapacity() { + return (mTier + 1) * 1000; + } +} diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicHull_NonElectric.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicHull_NonElectric.java new file mode 100644 index 0000000000..b6584b50ab --- /dev/null +++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicHull_NonElectric.java @@ -0,0 +1,78 @@ +package gregtech.api.metatileentity.implementations; + +import net.minecraftforge.common.util.ForgeDirection; + +import gregtech.api.interfaces.ITexture; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; + +public abstract class GT_MetaTileEntity_BasicHull_NonElectric extends GT_MetaTileEntity_BasicHull { + + public GT_MetaTileEntity_BasicHull_NonElectric(int aID, String aName, String aNameRegional, int aTier, + String aDescription) { + super(aID, aName, aNameRegional, aTier, aDescription); + } + + public GT_MetaTileEntity_BasicHull_NonElectric(String aName, int aTier, String aDescription, + ITexture[][][] aTextures) { + super(aName, aTier, 1, aDescription, aTextures); + } + + public GT_MetaTileEntity_BasicHull_NonElectric(String aName, int aTier, String[] aDescription, + ITexture[][][] aTextures) { + super(aName, aTier, 1, aDescription, aTextures); + } + + @Override + public ITexture[] getTexture(IGregTechTileEntity baseMetaTileEntity, ForgeDirection sideDirection, + ForgeDirection facingDirection, int colorIndex, boolean active, boolean redstoneLevel) { + return mTextures[Math.min(2, sideDirection.ordinal())][colorIndex + 1]; + } + + @Override + public boolean isElectric() { + return false; + } + + @Override + public boolean isEnetInput() { + return false; + } + + @Override + public boolean isEnetOutput() { + return false; + } + + @Override + public boolean isInputFacing(ForgeDirection side) { + return false; + } + + @Override + public boolean isOutputFacing(ForgeDirection side) { + return false; + } + + @Override + public long getMinimumStoredEU() { + return 0; + } + + @Override + public long maxEUStore() { + return 0; + } + + @Override + public long maxEUInput() { + return 0; + } + + @Override + public long maxEUOutput() { + return 0; + } + + @Override + public abstract ITexture[][][] getTextureSet(ITexture[] aTextures); +} diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicMachine.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicMachine.java new file mode 100644 index 0000000000..4709c82776 --- /dev/null +++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicMachine.java @@ -0,0 +1,1568 @@ +package gregtech.api.metatileentity.implementations; + +import static gregtech.api.enums.GT_Values.V; +import static gregtech.api.enums.GT_Values.debugCleanroom; +import static gregtech.api.enums.Textures.BlockIcons.MACHINE_CASINGS; +import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_PIPE_OUT; +import static gregtech.api.metatileentity.BaseTileEntity.FLUID_TRANSFER_TOOLTIP; +import static gregtech.api.metatileentity.BaseTileEntity.ITEM_TRANSFER_TOOLTIP; +import static gregtech.api.metatileentity.BaseTileEntity.NEI_TRANSFER_STEAM_TOOLTIP; +import static gregtech.api.metatileentity.BaseTileEntity.NEI_TRANSFER_VOLTAGE_TOOLTIP; +import static gregtech.api.metatileentity.BaseTileEntity.POWER_SOURCE_KEY; +import static gregtech.api.metatileentity.BaseTileEntity.SPECIAL_SLOT_TOOLTIP; +import static gregtech.api.metatileentity.BaseTileEntity.STALLED_STUTTERING_TOOLTIP; +import static gregtech.api.metatileentity.BaseTileEntity.STALLED_VENT_TOOLTIP; +import static gregtech.api.metatileentity.BaseTileEntity.TOOLTIP_DELAY; +import static gregtech.api.metatileentity.BaseTileEntity.UNUSED_SLOT_TOOLTIP; +import static gregtech.api.util.GT_RecipeConstants.EXPLODE; +import static gregtech.api.util.GT_RecipeConstants.ON_FIRE; +import static gregtech.api.util.GT_Utility.moveMultipleItemStacks; +import static net.minecraftforge.common.util.ForgeDirection.DOWN; +import static net.minecraftforge.common.util.ForgeDirection.UNKNOWN; +import static net.minecraftforge.common.util.ForgeDirection.UP; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import javax.annotation.Nonnull; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.StatCollector; +import net.minecraft.world.World; +import net.minecraftforge.common.DimensionManager; +import net.minecraftforge.common.util.ForgeDirection; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.IFluidHandler; + +import org.apache.commons.lang3.tuple.Pair; + +import com.gtnewhorizons.modularui.api.drawable.IDrawable; +import com.gtnewhorizons.modularui.api.math.Pos2d; +import com.gtnewhorizons.modularui.api.math.Size; +import com.gtnewhorizons.modularui.api.screen.ModularWindow; +import com.gtnewhorizons.modularui.api.screen.UIBuildContext; +import com.gtnewhorizons.modularui.api.widget.Widget; +import com.gtnewhorizons.modularui.common.fluid.FluidStackTank; +import com.gtnewhorizons.modularui.common.widget.CycleButtonWidget; +import com.gtnewhorizons.modularui.common.widget.DrawableWidget; +import com.gtnewhorizons.modularui.common.widget.FakeSyncWidget; +import com.gtnewhorizons.modularui.common.widget.FluidSlotWidget; +import com.gtnewhorizons.modularui.common.widget.ProgressBar; +import com.gtnewhorizons.modularui.common.widget.SlotWidget; + +import gregtech.GT_Mod; +import gregtech.api.GregTech_API; +import gregtech.api.enums.SoundResource; +import gregtech.api.gui.modularui.GT_UIInfos; +import gregtech.api.gui.modularui.GT_UITextures; +import gregtech.api.gui.modularui.SteamTexture; +import gregtech.api.interfaces.ICleanroom; +import gregtech.api.interfaces.IConfigurationCircuitSupport; +import gregtech.api.interfaces.ITexture; +import gregtech.api.interfaces.modularui.IAddGregtechLogo; +import gregtech.api.interfaces.modularui.IAddUIWidgets; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.interfaces.tileentity.IOverclockDescriptionProvider; +import gregtech.api.interfaces.tileentity.RecipeMapWorkable; +import gregtech.api.objects.GT_ItemStack; +import gregtech.api.objects.overclockdescriber.EUOverclockDescriber; +import gregtech.api.objects.overclockdescriber.OverclockDescriber; +import gregtech.api.recipe.BasicUIProperties; +import gregtech.api.recipe.RecipeMap; +import gregtech.api.render.TextureFactory; +import gregtech.api.util.GT_ClientPreference; +import gregtech.api.util.GT_CoverBehaviorBase; +import gregtech.api.util.GT_Log; +import gregtech.api.util.GT_OreDictUnificator; +import gregtech.api.util.GT_OverclockCalculator; +import gregtech.api.util.GT_Recipe; +import gregtech.api.util.GT_TooltipDataCache; +import gregtech.api.util.GT_Utility; +import gregtech.api.util.GT_Waila; +import gregtech.common.gui.modularui.UIHelper; +import mcp.mobius.waila.api.IWailaConfigHandler; +import mcp.mobius.waila.api.IWailaDataAccessor; + +/** + * NEVER INCLUDE THIS FILE IN YOUR MOD!!! + * <p/> + * This is the main construct for my Basic Machines such as the Automatic Extractor Extend this class to make a simple + * Machine + */ +public abstract class GT_MetaTileEntity_BasicMachine extends GT_MetaTileEntity_BasicTank implements RecipeMapWorkable, + IConfigurationCircuitSupport, IOverclockDescriptionProvider, IAddGregtechLogo, IAddUIWidgets { + + /** + * return values for checkRecipe() + */ + protected static final int DID_NOT_FIND_RECIPE = 0, FOUND_RECIPE_BUT_DID_NOT_MEET_REQUIREMENTS = 1, + FOUND_AND_SUCCESSFULLY_USED_RECIPE = 2; + + public static final int OTHER_SLOT_COUNT = 5; + public final ItemStack[] mOutputItems; + public final int mInputSlotCount, mAmperage; + public boolean mAllowInputFromOutputSide = false, mFluidTransfer = false, mItemTransfer = false, + mHasBeenUpdated = false, mStuttering = false, mCharge = false, mDecharge = false; + public boolean mDisableFilter = true; + public boolean mDisableMultiStack = true; + public int mProgresstime = 0, mMaxProgresstime = 0, mEUt = 0, mOutputBlocked = 0; + public ForgeDirection mMainFacing = ForgeDirection.WEST; + public FluidStack mOutputFluid; + protected final OverclockDescriber overclockDescriber; + + /** + * Contains the Recipe which has been previously used, or null if there was no previous Recipe, which could have + * been buffered + */ + protected GT_Recipe mLastRecipe = null; + + private FluidStack mFluidOut; + protected final FluidStackTank fluidOutputTank = new FluidStackTank( + () -> mFluidOut, + fluidStack -> mFluidOut = fluidStack, + this::getCapacity); + + /** + * Registers machine with single-line description. + * + * @param aOverlays 0 = SideFacingActive 1 = SideFacingInactive 2 = FrontFacingActive 3 = FrontFacingInactive 4 = + * TopFacingActive 5 = TopFacingInactive 6 = BottomFacingActive 7 = BottomFacingInactive ----- Not + * all Array Elements have to be initialised, you can also just use 8 Parameters for the Default + * Pipe Texture Overlays ----- 8 = BottomFacingPipeActive 9 = BottomFacingPipeInactive 10 = + * TopFacingPipeActive 11 = TopFacingPipeInactive 12 = SideFacingPipeActive 13 = + * SideFacingPipeInactive + */ + public GT_MetaTileEntity_BasicMachine(int aID, String aName, String aNameRegional, int aTier, int aAmperage, + String aDescription, int aInputSlotCount, int aOutputSlotCount, ITexture... aOverlays) { + super( + aID, + aName, + aNameRegional, + aTier, + OTHER_SLOT_COUNT + aInputSlotCount + aOutputSlotCount + 1, + aDescription, + aOverlays); + mInputSlotCount = Math.max(0, aInputSlotCount); + mOutputItems = new ItemStack[Math.max(0, aOutputSlotCount)]; + mAmperage = aAmperage; + overclockDescriber = createOverclockDescriber(); + } + + /** + * Registers machine with multi-line descriptions. + */ + public GT_MetaTileEntity_BasicMachine(int aID, String aName, String aNameRegional, int aTier, int aAmperage, + String[] aDescription, int aInputSlotCount, int aOutputSlotCount, ITexture... aOverlays) { + super( + aID, + aName, + aNameRegional, + aTier, + OTHER_SLOT_COUNT + aInputSlotCount + aOutputSlotCount + 1, + aDescription, + aOverlays); + mInputSlotCount = Math.max(0, aInputSlotCount); + mOutputItems = new ItemStack[Math.max(0, aOutputSlotCount)]; + mAmperage = aAmperage; + overclockDescriber = createOverclockDescriber(); + } + + /** + * For {@link #newMetaEntity}. + */ + public GT_MetaTileEntity_BasicMachine(String aName, int aTier, int aAmperage, String[] aDescription, + ITexture[][][] aTextures, int aInputSlotCount, int aOutputSlotCount) { + super(aName, aTier, OTHER_SLOT_COUNT + aInputSlotCount + aOutputSlotCount + 1, aDescription, aTextures); + mInputSlotCount = Math.max(0, aInputSlotCount); + mOutputItems = new ItemStack[Math.max(0, aOutputSlotCount)]; + mAmperage = aAmperage; + overclockDescriber = createOverclockDescriber(); + } + + /** + * To be called by the constructor to initialize this instance's overclock behavior + */ + protected OverclockDescriber createOverclockDescriber() { + return new EUOverclockDescriber(mTier, mAmperage); + } + + protected boolean isValidMainFacing(ForgeDirection side) { + return (side.flag & (UP.flag | DOWN.flag | UNKNOWN.flag)) == 0; // Horizontal + } + + public boolean setMainFacing(ForgeDirection side) { + if (!isValidMainFacing(side)) return false; + mMainFacing = side; + if (getBaseMetaTileEntity().getFrontFacing() == mMainFacing) { + getBaseMetaTileEntity().setFrontFacing(side.getOpposite()); + } + onFacingChange(); + onMachineBlockUpdate(); + return true; + } + + @Override + public void onFacingChange() { + super.onFacingChange(); + // Set up the correct facing (front towards player, output opposite) client-side before the server packet + // arrives + if (mMainFacing == UNKNOWN) { + IGregTechTileEntity te = getBaseMetaTileEntity(); + if (te != null && te.getWorld().isRemote) { + mMainFacing = te.getFrontFacing(); + te.setFrontFacing(te.getBackFacing()); + } + } + } + + @Override + public ITexture[][][] getTextureSet(ITexture[] aTextures) { + ITexture[][][] rTextures = new ITexture[14][17][]; + aTextures = Arrays.copyOf(aTextures, 14); + + for (int i = 0; i < aTextures.length; i++) if (aTextures[i] != null) for (byte c = -1; c < 16; c++) { + if (rTextures[i][c + 1] == null) + rTextures[i][c + 1] = new ITexture[] { MACHINE_CASINGS[mTier][c + 1], aTextures[i] }; + } + + for (byte c = -1; c < 16; c++) { + if (rTextures[0][c + 1] == null) rTextures[0][c + 1] = getSideFacingActive(c); + if (rTextures[1][c + 1] == null) rTextures[1][c + 1] = getSideFacingInactive(c); + if (rTextures[2][c + 1] == null) rTextures[2][c + 1] = getFrontFacingActive(c); + if (rTextures[3][c + 1] == null) rTextures[3][c + 1] = getFrontFacingInactive(c); + if (rTextures[4][c + 1] == null) rTextures[4][c + 1] = getTopFacingActive(c); + if (rTextures[5][c + 1] == null) rTextures[5][c + 1] = getTopFacingInactive(c); + if (rTextures[6][c + 1] == null) rTextures[6][c + 1] = getBottomFacingActive(c); + if (rTextures[7][c + 1] == null) rTextures[7][c + 1] = getBottomFacingInactive(c); + if (rTextures[8][c + 1] == null) rTextures[8][c + 1] = getBottomFacingPipeActive(c); + if (rTextures[9][c + 1] == null) rTextures[9][c + 1] = getBottomFacingPipeInactive(c); + if (rTextures[10][c + 1] == null) rTextures[10][c + 1] = getTopFacingPipeActive(c); + if (rTextures[11][c + 1] == null) rTextures[11][c + 1] = getTopFacingPipeInactive(c); + if (rTextures[12][c + 1] == null) rTextures[12][c + 1] = getSideFacingPipeActive(c); + if (rTextures[13][c + 1] == null) rTextures[13][c + 1] = getSideFacingPipeInactive(c); + } + return rTextures; + } + + @Override + public ITexture[] getTexture(IGregTechTileEntity baseMetaTileEntity, ForgeDirection sideDirection, + ForgeDirection facingDirection, int colorIndex, boolean active, boolean redstoneLevel) { + final int textureIndex; + if ((mMainFacing.flag & (UP.flag | DOWN.flag)) != 0) { // UP or DOWN + if (sideDirection == facingDirection) { + textureIndex = active ? 2 : 3; + } else { + textureIndex = switch (sideDirection) { + case DOWN -> active ? 6 : 7; + case UP -> active ? 4 : 5; + default -> active ? 0 : 1; + }; + } + } else { + if (sideDirection == mMainFacing) { + textureIndex = active ? 2 : 3; + } else { + if (showPipeFacing() && sideDirection == facingDirection) { + textureIndex = switch (sideDirection) { + case DOWN -> active ? 8 : 9; + case UP -> active ? 10 : 11; + default -> active ? 12 : 13; + }; + } else { + textureIndex = switch (sideDirection) { + case DOWN -> active ? 6 : 7; + case UP -> active ? 4 : 5; + default -> active ? 0 : 1; + }; + } + } + } + return mTextures[textureIndex][colorIndex + 1]; + } + + @Override + public boolean isSimpleMachine() { + return false; + } + + @Override + public boolean isOverclockerUpgradable() { + return false; + } + + @Override + public boolean isTransformerUpgradable() { + return false; + } + + @Override + public boolean isElectric() { + return true; + } + + @Override + public boolean isValidSlot(int aIndex) { + return aIndex > 0 && super.isValidSlot(aIndex) + && aIndex != getCircuitSlot() + && aIndex != OTHER_SLOT_COUNT + mInputSlotCount + mOutputItems.length; + } + + @Override + public boolean isFacingValid(ForgeDirection facing) { + // Either mMainFacing or mMainFacing is horizontal + return ((facing.flag | mMainFacing.flag) & ~(UP.flag | DOWN.flag | UNKNOWN.flag)) != 0; + } + + @Override + public boolean isEnetInput() { + return true; + } + + @Override + public boolean isInputFacing(ForgeDirection side) { + return side != mMainFacing; + } + + @Override + public boolean isOutputFacing(ForgeDirection side) { + return false; + } + + @Override + public boolean isTeleporterCompatible() { + return false; + } + + @Override + public boolean isLiquidInput(ForgeDirection side) { + return side != mMainFacing && (mAllowInputFromOutputSide || side != getBaseMetaTileEntity().getFrontFacing()); + } + + @Override + public boolean isLiquidOutput(ForgeDirection side) { + return side != mMainFacing; + } + + @Override + public long getMinimumStoredEU() { + return V[mTier] * 16L; + } + + @Override + public long maxEUStore() { + return V[mTier] * 64L; + } + + @Override + public long maxEUInput() { + return V[mTier]; + } + + @Override + public long maxSteamStore() { + return maxEUStore(); + } + + @Override + public long maxAmperesIn() { + return ((long) mEUt * 2L) / V[mTier] + 1L; + } + + @Override + public int getInputSlot() { + return OTHER_SLOT_COUNT; + } + + @Override + public int getOutputSlot() { + return OTHER_SLOT_COUNT + mInputSlotCount; + } + + public int getSpecialSlotIndex() { + return 3; + } + + @Override + public int getStackDisplaySlot() { + return 2; + } + + @Override + public int rechargerSlotStartIndex() { + return 1; + } + + @Override + public int dechargerSlotStartIndex() { + return 1; + } + + @Override + public int rechargerSlotCount() { + return mCharge ? 1 : 0; + } + + @Override + public int dechargerSlotCount() { + return mDecharge ? 1 : 0; + } + + @Override + public boolean isAccessAllowed(EntityPlayer aPlayer) { + return true; + } + + @Override + public int getProgresstime() { + return mProgresstime; + } + + @Override + public int maxProgresstime() { + return mMaxProgresstime; + } + + @Override + public int increaseProgress(int aProgress) { + mProgresstime += aProgress; + return mMaxProgresstime - mProgresstime; + } + + @Override + public boolean isFluidInputAllowed(FluidStack aFluid) { + return getFillableStack() != null || (getRecipeMap() != null && getRecipeMap().containsInput(aFluid)); + } + + @Override + public boolean isFluidChangingAllowed() { + return true; + } + + @Override + public boolean doesFillContainers() { + return false; + } + + @Override + public boolean doesEmptyContainers() { + return false; + } + + @Override + public boolean canTankBeFilled() { + return true; + } + + @Override + public boolean canTankBeEmptied() { + return true; + } + + @Override + public boolean displaysItemStack() { + return true; + } + + @Override + public boolean displaysStackSize() { + return true; + } + + @Override + public FluidStack getDrainableStack() { + return mFluidOut; + } + + @Override + public FluidStack setDrainableStack(FluidStack aFluid) { + markDirty(); + mFluidOut = aFluid; + return mFluidOut; + } + + @Override + public boolean isDrainableStackSeparate() { + return true; + } + + @Override + public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer) { + if (aBaseMetaTileEntity.isClientSide()) return true; + if (!GT_Mod.gregtechproxy.mForceFreeFace) { + GT_UIInfos.openGTTileEntityUI(aBaseMetaTileEntity, aPlayer); + return true; + } + for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) { + if (aBaseMetaTileEntity.getAirAtSide(side)) { + GT_UIInfos.openGTTileEntityUI(aBaseMetaTileEntity, aPlayer); + return true; + } + } + GT_Utility.sendChatToPlayer(aPlayer, "No free Side!"); + return true; + } + + @Override + public void initDefaultModes(NBTTagCompound aNBT) { + mMainFacing = ForgeDirection.UNKNOWN; + if (!getBaseMetaTileEntity().getWorld().isRemote) { + final GT_ClientPreference tPreference = GT_Mod.gregtechproxy + .getClientPreference(getBaseMetaTileEntity().getOwnerUuid()); + if (tPreference != null) { + mDisableFilter = !tPreference.isSingleBlockInitialFilterEnabled(); + mDisableMultiStack = !tPreference.isSingleBlockInitialMultiStackEnabled(); + } + } + } + + @Override + public void saveNBTData(NBTTagCompound aNBT) { + super.saveNBTData(aNBT); + aNBT.setBoolean("mFluidTransfer", mFluidTransfer); + aNBT.setBoolean("mItemTransfer", mItemTransfer); + aNBT.setBoolean("mHasBeenUpdated", mHasBeenUpdated); + aNBT.setBoolean("mAllowInputFromOutputSide", mAllowInputFromOutputSide); + aNBT.setBoolean("mDisableFilter", mDisableFilter); + aNBT.setBoolean("mDisableMultiStack", mDisableMultiStack); + aNBT.setInteger("mEUt", mEUt); + aNBT.setInteger("mMainFacing", mMainFacing.ordinal()); + aNBT.setInteger("mProgresstime", mProgresstime); + aNBT.setInteger("mMaxProgresstime", mMaxProgresstime); + if (mOutputFluid != null) aNBT.setTag("mOutputFluid", mOutputFluid.writeToNBT(new NBTTagCompound())); + if (mFluidOut != null) aNBT.setTag("mFluidOut", mFluidOut.writeToNBT(new NBTTagCompound())); + + for (int i = 0; i < mOutputItems.length; i++) + if (mOutputItems[i] != null) GT_Utility.saveItem(aNBT, "mOutputItem" + i, mOutputItems[i]); + } + + @Override + public void loadNBTData(NBTTagCompound aNBT) { + super.loadNBTData(aNBT); + mFluidTransfer = aNBT.getBoolean("mFluidTransfer"); + mItemTransfer = aNBT.getBoolean("mItemTransfer"); + mHasBeenUpdated = aNBT.getBoolean("mHasBeenUpdated"); + mAllowInputFromOutputSide = aNBT.getBoolean("mAllowInputFromOutputSide"); + mDisableFilter = aNBT.getBoolean("mDisableFilter"); + mDisableMultiStack = aNBT.getBoolean("mDisableMultiStack"); + mEUt = aNBT.getInteger("mEUt"); + mMainFacing = ForgeDirection.getOrientation(aNBT.getInteger("mMainFacing")); + mProgresstime = aNBT.getInteger("mProgresstime"); + mMaxProgresstime = aNBT.getInteger("mMaxProgresstime"); + mOutputFluid = FluidStack.loadFluidStackFromNBT(aNBT.getCompoundTag("mOutputFluid")); + mFluidOut = FluidStack.loadFluidStackFromNBT(aNBT.getCompoundTag("mFluidOut")); + + for (int i = 0; i < mOutputItems.length; i++) mOutputItems[i] = GT_Utility.loadItem(aNBT, "mOutputItem" + i); + } + + @Override + public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) { + super.onPostTick(aBaseMetaTileEntity, aTick); + + if (aBaseMetaTileEntity.isServerSide()) { + mCharge = aBaseMetaTileEntity.getStoredEU() / 2 > aBaseMetaTileEntity.getEUCapacity() / 3; + mDecharge = aBaseMetaTileEntity.getStoredEU() < aBaseMetaTileEntity.getEUCapacity() / 3; + + doDisplayThings(); + + boolean tSucceeded = false; + + if (mMaxProgresstime > 0 && (mProgresstime >= 0 || aBaseMetaTileEntity.isAllowedToWork())) { + markDirty(); + aBaseMetaTileEntity.setActive(true); + if (mProgresstime < 0 || drainEnergyForProcess(mEUt)) { + if (++mProgresstime >= mMaxProgresstime) { + for (int i = 0; i < mOutputItems.length; i++) + for (int j = 0; j < mOutputItems.length; j++) if (aBaseMetaTileEntity + .addStackToSlot(getOutputSlot() + ((j + i) % mOutputItems.length), mOutputItems[i])) + break; + if (mOutputFluid != null) + if (getDrainableStack() == null) setDrainableStack(mOutputFluid.copy()); + else if (mOutputFluid.isFluidEqual(getDrainableStack())) + getDrainableStack().amount += mOutputFluid.amount; + Arrays.fill(mOutputItems, null); + mOutputFluid = null; + mEUt = 0; + mProgresstime = 0; + mMaxProgresstime = 0; + mStuttering = false; + tSucceeded = true; + endProcess(); + } + if (mProgresstime > 5) mStuttering = false; + } else { + if (!mStuttering) { + stutterProcess(); + if (canHaveInsufficientEnergy()) mProgresstime = -100; + mStuttering = true; + } + } + } else { + aBaseMetaTileEntity.setActive(false); + } + + boolean tRemovedOutputFluid = false; + + if (doesAutoOutputFluids() && getDrainableStack() != null + && aBaseMetaTileEntity.getFrontFacing() != mMainFacing + && (tSucceeded || aTick % 20 == 0)) { + IFluidHandler tTank = aBaseMetaTileEntity.getITankContainerAtSide(aBaseMetaTileEntity.getFrontFacing()); + if (tTank != null) { + FluidStack tDrained = drain(1000, false); + if (tDrained != null) { + final int tFilledAmount = tTank.fill(aBaseMetaTileEntity.getBackFacing(), tDrained, false); + if (tFilledAmount > 0) + tTank.fill(aBaseMetaTileEntity.getBackFacing(), drain(tFilledAmount, true), true); + } + } + if (getDrainableStack() == null) tRemovedOutputFluid = true; + } + + if (doesAutoOutput() && !isOutputEmpty() + && aBaseMetaTileEntity.getFrontFacing() != mMainFacing + && (tSucceeded || mOutputBlocked % 300 == 1 + || aBaseMetaTileEntity.hasInventoryBeenModified() + || aTick % 600 == 0)) { + TileEntity tTileEntity2 = aBaseMetaTileEntity.getTileEntityAtSide(aBaseMetaTileEntity.getFrontFacing()); + long tStoredEnergy = aBaseMetaTileEntity.getUniversalEnergyStored(); + int tMaxStacks = (int) (tStoredEnergy / 64L); + if (tMaxStacks > mOutputItems.length) tMaxStacks = mOutputItems.length; + + moveMultipleItemStacks( + aBaseMetaTileEntity, + tTileEntity2, + aBaseMetaTileEntity.getFrontFacing(), + aBaseMetaTileEntity.getBackFacing(), + null, + false, + (byte) 64, + (byte) 1, + (byte) 64, + (byte) 1, + tMaxStacks); + } + + if (mOutputBlocked != 0) if (isOutputEmpty()) mOutputBlocked = 0; + else mOutputBlocked++; + + if (allowToCheckRecipe()) { + if (mMaxProgresstime <= 0 && aBaseMetaTileEntity.isAllowedToWork() + && (tRemovedOutputFluid || tSucceeded + || aBaseMetaTileEntity.hasInventoryBeenModified() + || aTick % 600 == 0 + || aBaseMetaTileEntity.hasWorkJustBeenEnabled()) + && hasEnoughEnergyToCheckRecipe()) { + if (checkRecipe() == FOUND_AND_SUCCESSFULLY_USED_RECIPE) { + if (getSpecialSlot() != null && getSpecialSlot().stackSize <= 0) + mInventory[getSpecialSlotIndex()] = null; + for (int i = getInputSlot(), j = i + mInputSlotCount; i < j; i++) + if (mInventory[i] != null && mInventory[i].stackSize <= 0) mInventory[i] = null; + for (int i = 0; i < mOutputItems.length; i++) { + mOutputItems[i] = GT_Utility.copyOrNull(mOutputItems[i]); + if (mOutputItems[i] != null && mOutputItems[i].stackSize > 64) + mOutputItems[i].stackSize = 64; + mOutputItems[i] = GT_OreDictUnificator.get(true, mOutputItems[i]); + } + if (mFluid != null && mFluid.amount <= 0) mFluid = null; + mMaxProgresstime = Math.max(1, mMaxProgresstime); + if (GT_Utility.isDebugItem(mInventory[dechargerSlotStartIndex()])) { + mEUt = mMaxProgresstime = 1; + } + startProcess(); + } else { + mMaxProgresstime = 0; + Arrays.fill(mOutputItems, null); + mOutputFluid = null; + } + } + } else { + if (!mStuttering) { + stutterProcess(); + mStuttering = true; + } + } + } + // Only using mNeedsSteamVenting right now and assigning it to 64 to space in the range for more single block + // machine problems. + // Value | Class | Field + // 1 | GT_MetaTileEntity_BasicMachine | mStuttering + // 64 | GT_MetaTileEntity_BasicMachine_Bronze | mNeedsSteamVenting + aBaseMetaTileEntity.setErrorDisplayID((aBaseMetaTileEntity.getErrorDisplayID() & ~127)); // | (mStuttering ? 1 : + // 0)); + } + + protected void doDisplayThings() { + if (!isValidMainFacing(mMainFacing) && isValidMainFacing(getBaseMetaTileEntity().getFrontFacing())) { + mMainFacing = getBaseMetaTileEntity().getFrontFacing(); + } + if (isValidMainFacing(mMainFacing) && !mHasBeenUpdated) { + mHasBeenUpdated = true; + getBaseMetaTileEntity().setFrontFacing(getBaseMetaTileEntity().getBackFacing()); + } + } + + protected boolean hasEnoughEnergyToCheckRecipe() { + return getBaseMetaTileEntity().isUniversalEnergyStored(getMinimumStoredEU() / 2); + } + + protected boolean drainEnergyForProcess(long aEUt) { + return getBaseMetaTileEntity().decreaseStoredEnergyUnits(aEUt, false); + } + + /** + * Calculates overclock based on {@link #overclockDescriber}. + */ + protected void calculateCustomOverclock(GT_Recipe recipe) { + GT_OverclockCalculator calculator = overclockDescriber.createCalculator( + new GT_OverclockCalculator().setRecipeEUt(recipe.mEUt) + .setDuration(recipe.mDuration) + .setOneTickDiscount(true), + recipe); + calculator.calculate(); + mEUt = (int) calculator.getConsumption(); + mMaxProgresstime = calculator.getDuration(); + } + + /** + * Helper method for calculating simple overclock. + */ + protected void calculateOverclockedNess(int eut, int duration) { + GT_OverclockCalculator calculator = new GT_OverclockCalculator().setRecipeEUt(eut) + .setEUt(V[mTier] * mAmperage) + .setDuration(duration) + .setOneTickDiscount(true) + .calculate(); + mEUt = (int) calculator.getConsumption(); + mMaxProgresstime = calculator.getDuration(); + } + + protected ItemStack getSpecialSlot() { + return mInventory[getSpecialSlotIndex()]; + } + + protected ItemStack getOutputAt(int aIndex) { + return mInventory[getOutputSlot() + aIndex]; + } + + protected ItemStack[] getAllOutputs() { + ItemStack[] rOutputs = new ItemStack[mOutputItems.length]; + for (int i = 0; i < mOutputItems.length; i++) rOutputs[i] = getOutputAt(i); + return rOutputs; + } + + protected boolean canOutput(GT_Recipe aRecipe) { + return aRecipe != null && (aRecipe.mNeedsEmptyOutput ? isOutputEmpty() && getDrainableStack() == null + : canOutput(aRecipe.getFluidOutput(0)) && canOutput(aRecipe.mOutputs)); + } + + protected boolean canOutput(ItemStack... aOutputs) { + if (aOutputs == null) return true; + ItemStack[] tOutputSlots = getAllOutputs(); + for (int i = 0; i < tOutputSlots.length && i < aOutputs.length; i++) + if (tOutputSlots[i] != null && aOutputs[i] != null + && (!GT_Utility.areStacksEqual(tOutputSlots[i], aOutputs[i], false) + || tOutputSlots[i].stackSize + aOutputs[i].stackSize > tOutputSlots[i].getMaxStackSize())) { + mOutputBlocked++; + return false; + } + return true; + } + + protected boolean canOutput(FluidStack aOutput) { + if (aOutput == null) return true; + FluidStack drainableStack = getDrainableStack(); + if (drainableStack != null && !drainableStack.isFluidEqual(aOutput)) return false; + return (drainableStack != null ? drainableStack.amount : 0) + aOutput.amount <= getCapacity(); + } + + protected ItemStack getInputAt(int aIndex) { + return mInventory[getInputSlot() + aIndex]; + } + + protected ItemStack[] getAllInputs() { + int tRealInputSlotCount = this.mInputSlotCount + (allowSelectCircuit() ? 1 : 0); + ItemStack[] rInputs = new ItemStack[tRealInputSlotCount]; + for (int i = 0; i < mInputSlotCount; i++) rInputs[i] = getInputAt(i); + if (allowSelectCircuit()) rInputs[mInputSlotCount] = getStackInSlot(getCircuitSlot()); + return rInputs; + } + + protected boolean isOutputEmpty() { + boolean rIsEmpty = true; + for (ItemStack tOutputSlotContent : getAllOutputs()) if (tOutputSlotContent != null) { + rIsEmpty = false; + break; + } + return rIsEmpty; + } + + @Override + public void onValueUpdate(byte aValue) { + mMainFacing = ForgeDirection.getOrientation(aValue); + } + + @Override + public byte getUpdateData() { + return (byte) mMainFacing.ordinal(); + } + + @Override + public void doSound(byte aIndex, double aX, double aY, double aZ) { + super.doSound(aIndex, aX, aY, aZ); + if (aIndex == 8) GT_Utility.doSoundAtClient(SoundResource.IC2_MACHINES_INTERRUPT_ONE, 100, 1.0F, aX, aY, aZ); + } + + public boolean doesAutoOutput() { + return mItemTransfer; + } + + public boolean doesAutoOutputFluids() { + return mFluidTransfer; + } + + public boolean allowToCheckRecipe() { + return true; + } + + public boolean showPipeFacing() { + return true; + } + + /** + * Called whenever the Machine successfully started a Process, useful for Sound Effects + */ + public void startProcess() { + // + } + + /** + * Called whenever the Machine successfully finished a Process, useful for Sound Effects + */ + public void endProcess() { + // + } + + /** + * Called whenever the Machine aborted a Process, useful for Sound Effects + */ + public void abortProcess() { + // + } + + /** + * Called whenever the Machine aborted a Process but still works on it, useful for Sound Effects + */ + public void stutterProcess() { + if (useStandardStutterSound()) sendSound((byte) 8); + } + + /** + * If this Machine can have the Insufficient Energy Line Problem + */ + public boolean canHaveInsufficientEnergy() { + return true; + } + + public boolean useStandardStutterSound() { + return true; + } + + @Override + public String[] getInfoData() { + return new String[] { "Progress:", + EnumChatFormatting.GREEN + GT_Utility.formatNumbers((mProgresstime / 20)) + + EnumChatFormatting.RESET + + " s / " + + EnumChatFormatting.YELLOW + + GT_Utility.formatNumbers(mMaxProgresstime / 20) + + EnumChatFormatting.RESET + + " s", + "Stored Energy:", + EnumChatFormatting.GREEN + GT_Utility.formatNumbers(getBaseMetaTileEntity().getStoredEU()) + + EnumChatFormatting.RESET + + " EU / " + + EnumChatFormatting.YELLOW + + GT_Utility.formatNumbers(getBaseMetaTileEntity().getEUCapacity()) + + EnumChatFormatting.RESET + + " EU", + "Probably uses: " + EnumChatFormatting.RED + + GT_Utility.formatNumbers(mEUt) + + EnumChatFormatting.RESET + + " EU/t at " + + EnumChatFormatting.RED + + GT_Utility.formatNumbers(mEUt == 0 ? 0 : mAmperage) + + EnumChatFormatting.RESET + + " A" }; + } + + @Override + public boolean isGivingInformation() { + return true; + } + + @Override + public void onScrewdriverRightClick(ForgeDirection side, EntityPlayer aPlayer, float aX, float aY, float aZ) { + if (side == getBaseMetaTileEntity().getFrontFacing() || side == mMainFacing) { + if (aPlayer.isSneaking()) { + mDisableFilter = !mDisableFilter; + GT_Utility.sendChatToPlayer( + aPlayer, + StatCollector.translateToLocal("GT5U.hatch.disableFilter." + mDisableFilter)); + } else { + mAllowInputFromOutputSide = !mAllowInputFromOutputSide; + GT_Utility.sendChatToPlayer( + aPlayer, + mAllowInputFromOutputSide ? GT_Utility.trans("095", "Input from Output Side allowed") + : GT_Utility.trans("096", "Input from Output Side forbidden")); + } + } + } + + @Override + public boolean onSolderingToolRightClick(ForgeDirection side, ForgeDirection wrenchingSide, + EntityPlayer entityPlayer, float aX, float aY, float aZ) { + if (!entityPlayer.isSneaking()) return false; + final boolean click = super.onSolderingToolRightClick(side, wrenchingSide, entityPlayer, aX, aY, aZ); + if (click) return true; + if (wrenchingSide != mMainFacing) return false; + mDisableMultiStack = !mDisableMultiStack; + GT_Utility.sendChatToPlayer( + entityPlayer, + StatCollector.translateToLocal("GT5U.hatch.disableMultiStack." + mDisableMultiStack)); + return true; + } + + @Override + public boolean allowCoverOnSide(ForgeDirection side, GT_ItemStack aCoverID) { + if (side != mMainFacing) return true; + GT_CoverBehaviorBase<?> tBehavior = GregTech_API.getCoverBehaviorNew(aCoverID.toStack()); + return tBehavior.isGUIClickable( + side, + GT_Utility.stackToInt(aCoverID.toStack()), + tBehavior.createDataObject(), + getBaseMetaTileEntity()); + } + + @Override + public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side, + ItemStack aStack) { + return side != mMainFacing && aIndex >= getOutputSlot() && aIndex < getOutputSlot() + mOutputItems.length; + } + + @Override + public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side, + ItemStack aStack) { + if (side == mMainFacing || aIndex < getInputSlot() + || aIndex >= getInputSlot() + mInputSlotCount + || (!mAllowInputFromOutputSide && side == aBaseMetaTileEntity.getFrontFacing())) return false; + for (int i = getInputSlot(), j = i + mInputSlotCount; i < j; i++) + if (GT_Utility.areStacksEqual(GT_OreDictUnificator.get(aStack), mInventory[i]) && mDisableMultiStack) + return i == aIndex; + return mDisableFilter || allowPutStackValidated(aBaseMetaTileEntity, aIndex, side, aStack); + } + + /** + * Test if given stack can be inserted into specified slot. If mDisableMultiStack is false, before execution of this + * method it is ensured there is no such kind of item inside any input slots already. Otherwise, you don't need to + * check for it anyway. + */ + protected boolean allowPutStackValidated(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side, + ItemStack aStack) { + return !mDisableMultiStack || mInventory[aIndex] == null; + } + + @Override + public boolean allowSelectCircuit() { + return false; + } + + protected final ItemStack[] appendSelectedCircuit(ItemStack... inputs) { + if (allowSelectCircuit()) { + ItemStack circuit = getStackInSlot(getCircuitSlot()); + if (circuit != null) { + ItemStack[] result = Arrays.copyOf(inputs, inputs.length + 1); + result[inputs.length] = circuit; + return result; + } + } + return inputs; + } + + @Override + public int getCircuitSlot() { + return 4; + } + + @Override + public int getCircuitGUISlot() { + return 3; + } + + @Override + public List<ItemStack> getConfigurationCircuits() { + return GregTech_API.getConfigurationCircuitList(mTier); + } + + @Override + public RecipeMap<?> getRecipeMap() { + return null; + } + + /** + * Override this to check the Recipes yourself, super calls to this could be useful if you just want to add a + * special case + * <p/> + * I thought about Enum too, but Enum doesn't add support for people adding other return Systems. + * <p/> + * Funny how Eclipse marks the word Enum as not correctly spelled. + * + * @return see constants above + */ + public int checkRecipe() { + return checkRecipe(false); + } + + public static boolean isValidForLowGravity(GT_Recipe tRecipe, int dimId) { + return // TODO check or get a better solution + DimensionManager.getProvider(dimId) + .getClass() + .getName() + .contains("Orbit") + || DimensionManager.getProvider(dimId) + .getClass() + .getName() + .endsWith("Space") + || DimensionManager.getProvider(dimId) + .getClass() + .getName() + .endsWith("Asteroids") + || DimensionManager.getProvider(dimId) + .getClass() + .getName() + .endsWith("SS") + || DimensionManager.getProvider(dimId) + .getClass() + .getName() + .contains("SpaceStation"); + } + + /** + * + * @param skipOC disables OverclockedNess calculation and check - if you do you must implement your own method... + * @return DID_NOT_FIND_RECIPE = 0, FOUND_RECIPE_BUT_DID_NOT_MEET_REQUIREMENTS = 1, + * FOUND_AND_SUCCESSFULLY_USED_RECIPE = 2; + */ + public int checkRecipe(boolean skipOC) { + RecipeMap<?> tMap = getRecipeMap(); + if (tMap == null) return DID_NOT_FIND_RECIPE; + GT_Recipe tRecipe = tMap.findRecipeQuery() + .items(getAllInputs()) + .fluids(getFillableStack()) + .specialSlot(getSpecialSlot()) + .voltage(V[mTier]) + .cachedRecipe(mLastRecipe) + .find(); + if (tRecipe == null) { + return DID_NOT_FIND_RECIPE; + } + if (tRecipe.getMetadataOrDefault(EXPLODE, false) && getBaseMetaTileEntity() != null) { + getBaseMetaTileEntity().doExplosion(V[mTier] * 4); + return DID_NOT_FIND_RECIPE; + } + if (tRecipe.getMetadataOrDefault(ON_FIRE, false) && getBaseMetaTileEntity() != null) { + getBaseMetaTileEntity().setOnFire(); + return DID_NOT_FIND_RECIPE; + } + + if (GT_Mod.gregtechproxy.mLowGravProcessing && (tRecipe.mSpecialValue == -100 || tRecipe.mSpecialValue == -300) + && !isValidForLowGravity(tRecipe, getBaseMetaTileEntity().getWorld().provider.dimensionId)) + return FOUND_RECIPE_BUT_DID_NOT_MEET_REQUIREMENTS; + if (tRecipe.mCanBeBuffered) mLastRecipe = tRecipe; + if (!canOutput(tRecipe)) { + mOutputBlocked++; + return FOUND_RECIPE_BUT_DID_NOT_MEET_REQUIREMENTS; + } + ICleanroom cleanroom = getCleanroom(); + if (tRecipe.mSpecialValue == -200 || tRecipe.mSpecialValue == -300) { + if (cleanroom == null || !cleanroom.isValidCleanroom() || cleanroom.getCleanness() == 0) { + return FOUND_RECIPE_BUT_DID_NOT_MEET_REQUIREMENTS; + } + } + if (!tRecipe.isRecipeInputEqual(true, new FluidStack[] { getFillableStack() }, getAllInputs())) + return FOUND_RECIPE_BUT_DID_NOT_MEET_REQUIREMENTS; + for (int i = 0; i < mOutputItems.length; i++) + if (getBaseMetaTileEntity().getRandomNumber(10000) < tRecipe.getOutputChance(i)) + mOutputItems[i] = tRecipe.getOutput(i); + if (tRecipe.mSpecialValue == -200 || tRecipe.mSpecialValue == -300) { + assert cleanroom != null; + for (int i = 0; i < mOutputItems.length; i++) if (mOutputItems[i] != null + && getBaseMetaTileEntity().getRandomNumber(10000) > cleanroom.getCleanness()) { + if (debugCleanroom) { + GT_Log.out.println( + "BasicMachine: Voiding output due to cleanness failure. Cleanness = " + + cleanroom.getCleanness()); + } + mOutputItems[i] = null; + } + } + mOutputFluid = tRecipe.getFluidOutput(0); + if (!skipOC) { + calculateCustomOverclock(tRecipe); + // In case recipe is too OP for that machine + if (mMaxProgresstime == Integer.MAX_VALUE - 1 && mEUt == Integer.MAX_VALUE - 1) + return FOUND_RECIPE_BUT_DID_NOT_MEET_REQUIREMENTS; + } + return FOUND_AND_SUCCESSFULLY_USED_RECIPE; + } + + public ITexture[] getSideFacingActive(byte aColor) { + return new ITexture[] { MACHINE_CASINGS[mTier][aColor + 1] }; + } + + public ITexture[] getSideFacingInactive(byte aColor) { + return new ITexture[] { MACHINE_CASINGS[mTier][aColor + 1] }; + } + + public ITexture[] getFrontFacingActive(byte aColor) { + return new ITexture[] { MACHINE_CASINGS[mTier][aColor + 1] }; + } + + public ITexture[] getFrontFacingInactive(byte aColor) { + return new ITexture[] { MACHINE_CASINGS[mTier][aColor + 1] }; + } + + public ITexture[] getTopFacingActive(byte aColor) { + return new ITexture[] { MACHINE_CASINGS[mTier][aColor + 1] }; + } + + public ITexture[] getTopFacingInactive(byte aColor) { + return new ITexture[] { MACHINE_CASINGS[mTier][aColor + 1] }; + } + + public ITexture[] getBottomFacingActive(byte aColor) { + return new ITexture[] { MACHINE_CASINGS[mTier][aColor + 1] }; + } + + public ITexture[] getBottomFacingInactive(byte aColor) { + return new ITexture[] { MACHINE_CASINGS[mTier][aColor + 1] }; + } + + public ITexture[] getBottomFacingPipeActive(byte aColor) { + return new ITexture[] { MACHINE_CASINGS[mTier][aColor + 1], TextureFactory.of(OVERLAY_PIPE_OUT) }; + } + + public ITexture[] getBottomFacingPipeInactive(byte aColor) { + return new ITexture[] { MACHINE_CASINGS[mTier][aColor + 1], TextureFactory.of(OVERLAY_PIPE_OUT) }; + } + + public ITexture[] getTopFacingPipeActive(byte aColor) { + return new ITexture[] { MACHINE_CASINGS[mTier][aColor + 1], TextureFactory.of(OVERLAY_PIPE_OUT) }; + } + + public ITexture[] getTopFacingPipeInactive(byte aColor) { + return new ITexture[] { MACHINE_CASINGS[mTier][aColor + 1], TextureFactory.of(OVERLAY_PIPE_OUT) }; + } + + public ITexture[] getSideFacingPipeActive(byte aColor) { + return new ITexture[] { MACHINE_CASINGS[mTier][aColor + 1], TextureFactory.of(OVERLAY_PIPE_OUT) }; + } + + public ITexture[] getSideFacingPipeInactive(byte aColor) { + return new ITexture[] { MACHINE_CASINGS[mTier][aColor + 1], TextureFactory.of(OVERLAY_PIPE_OUT) }; + } + + @Override + public void getWailaBody(ItemStack itemStack, List<String> currenttip, IWailaDataAccessor accessor, + IWailaConfigHandler config) { + final NBTTagCompound tag = accessor.getNBTData(); + + if (tag.getBoolean("stutteringSingleBlock")) { + currenttip.add("Status: insufficient energy"); + } else { + boolean isActive = tag.getBoolean("isActiveSingleBlock"); + if (isActive) { + int mEUt = tag.getInteger("eut"); + if (!isSteampowered()) { + if (mEUt > 0) { + currenttip.add( + StatCollector.translateToLocalFormatted( + "GT5U.waila.energy.use_with_amperage", + GT_Utility.formatNumbers(mEUt), + GT_Utility.getAmperageForTier(mEUt, (byte) getInputTier()), + GT_Utility.getColoredTierNameFromTier((byte) getInputTier()))); + } else if (mEUt < 0) { + currenttip.add( + StatCollector.translateToLocalFormatted( + "GT5U.waila.energy.produce_with_amperage", + GT_Utility.formatNumbers(-mEUt), + GT_Utility.getAmperageForTier(-mEUt, (byte) getOutputTier()), + GT_Utility.getColoredTierNameFromTier((byte) getOutputTier()))); + } + } else { + if (mEUt > 0) { + currenttip.add( + StatCollector.translateToLocalFormatted( + "GT5U.waila.energy.use", + GT_Utility.formatNumbers(mEUt), + GT_Utility.getColoredTierNameFromVoltage(mEUt))); + } else if (mEUt < 0) { + currenttip.add( + StatCollector.translateToLocalFormatted( + "GT5U.waila.energy.produce", + GT_Utility.formatNumbers(-mEUt), + GT_Utility.getColoredTierNameFromVoltage(-mEUt))); + } + } + } + currenttip.add( + GT_Waila.getMachineProgressString( + isActive, + tag.getInteger("maxProgressSingleBlock"), + tag.getInteger("progressSingleBlock"))); + } + + currenttip.add( + String.format( + "Machine Facing: %s", + ForgeDirection.getOrientation(tag.getInteger("mainFacingSingleBlock")) + .name())); + + currenttip.add( + String.format( + "Output Facing: %s", + ForgeDirection.getOrientation(tag.getInteger("outputFacingSingleBlock")) + .name())); + } + + @Override + public void getWailaNBTData(EntityPlayerMP player, TileEntity tile, NBTTagCompound tag, World world, int x, int y, + int z) { + super.getWailaNBTData(player, tile, tag, world, x, y, z); + + tag.setInteger("progressSingleBlock", mProgresstime); + tag.setInteger("maxProgressSingleBlock", mMaxProgresstime); + tag.setInteger("mainFacingSingleBlock", mMainFacing.ordinal()); + tag.setBoolean("stutteringSingleBlock", mStuttering); + + final IGregTechTileEntity tileEntity = getBaseMetaTileEntity(); + if (tileEntity != null) { + tag.setBoolean("isActiveSingleBlock", tileEntity.isActive()); + tag.setInteger( + "outputFacingSingleBlock", + tileEntity.getFrontFacing() + .ordinal()); + if (tileEntity.isActive()) tag.setInteger("eut", mEUt); + } + } + + @Nonnull + @Override + public OverclockDescriber getOverclockDescriber() { + return overclockDescriber; + } + + // GUI stuff + + @Override + public boolean useModularUI() { + return getRecipeMap() != null; + } + + @Override + public int getCircuitSlotX() { + return 153; + } + + @Override + public int getCircuitSlotY() { + return 63; + } + + @Override + public void addGregTechLogo(ModularWindow.Builder builder) { + if (getRecipeMap() != null) { + getRecipeMap().getFrontend() + .addGregTechLogo(builder, new Pos2d(0, 0)); + } else { + builder.widget( + new DrawableWidget().setDrawable(getGUITextureSet().getGregTechLogo()) + .setSize(17, 17) + .setPos(152, 63)); + } + } + + @Override + public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) { + if (!isSteampowered()) { + builder.widget(createFluidAutoOutputButton()); + builder.widget(createItemAutoOutputButton()); + } + + BasicUIProperties uiProperties = getUIProperties(); + addIOSlots(builder, uiProperties); + + builder.widget(createChargerSlot(79, 62)); + + addProgressBar(builder, uiProperties); + + builder.widget( + createErrorStatusArea( + builder, + isSteampowered() ? GT_UITextures.PICTURE_STALLED_STEAM : GT_UITextures.PICTURE_STALLED_ELECTRICITY)); + } + + /** + * Override to specify UI properties if this machine doesn't work with recipemap. + */ + protected BasicUIProperties getUIProperties() { + if (getRecipeMap() != null) { + BasicUIProperties originalProperties = getRecipeMap().getFrontend() + .getUIProperties(); + return originalProperties.toBuilder() + .maxItemInputs(mInputSlotCount) + .maxItemOutputs(mOutputItems.length) + .maxFluidInputs(Math.min(originalProperties.maxFluidInputs, 1)) + .maxFluidOutputs(Math.min(originalProperties.maxFluidOutputs, 1)) + .build(); + } + return BasicUIProperties.builder() + .maxItemInputs(mInputSlotCount) + .maxItemOutputs(mOutputItems.length) + .maxFluidInputs(getCapacity() != 0 ? 1 : 0) + .maxFluidOutputs(0) + .build(); + } + + /** + * Adds item I/O, special item, and fluid I/O slots. + */ + protected void addIOSlots(ModularWindow.Builder builder, BasicUIProperties uiProperties) { + UIHelper.forEachSlots( + (i, backgrounds, pos) -> builder.widget(createItemInputSlot(i, backgrounds, pos)), + (i, backgrounds, pos) -> builder.widget(createItemOutputSlot(i, backgrounds, pos)), + (i, backgrounds, pos) -> builder.widget(createSpecialSlot(backgrounds, pos, uiProperties)), + (i, backgrounds, pos) -> builder.widget(createFluidInputSlot(backgrounds, pos)), + (i, backgrounds, pos) -> builder.widget(createFluidOutputSlot(backgrounds, pos)), + getGUITextureSet().getItemSlot(), + getGUITextureSet().getFluidSlot(), + uiProperties, + uiProperties.maxItemInputs, + uiProperties.maxItemOutputs, + uiProperties.maxFluidInputs, + uiProperties.maxFluidOutputs, + getSteamVariant(), + Pos2d.ZERO); + } + + protected void addProgressBar(ModularWindow.Builder builder, BasicUIProperties uiProperties) { + boolean isSteamPowered = isSteampowered(); + RecipeMap<?> recipeMap = getRecipeMap(); + if (!isSteamPowered && uiProperties.progressBarTexture == null) { + if (recipeMap != null) { + // Require progress bar texture for machines working with recipemap, otherwise permit + throw new RuntimeException("Missing progressbar texture for " + recipeMap.unlocalizedName); + } else { + return; + } + } + if (isSteamPowered && uiProperties.progressBarTextureSteam == null) { + if (recipeMap != null) { + throw new RuntimeException("Missing steam progressbar texture for " + recipeMap.unlocalizedName); + } else { + return; + } + } + + builder.widget( + setNEITransferRect( + new ProgressBar() + .setProgress(() -> maxProgresstime() != 0 ? (float) getProgresstime() / maxProgresstime() : 0) + .setTexture( + isSteamPowered ? uiProperties.progressBarTextureSteam.get(getSteamVariant()) + : uiProperties.progressBarTexture.get(), + uiProperties.progressBarImageSize) + .setDirection(uiProperties.progressBarDirection) + .setPos(uiProperties.progressBarPos) + .setSize(uiProperties.progressBarSize), + uiProperties.neiTransferRectId)); + addProgressBarSpecialTextures(builder, uiProperties); + } + + /** + * Override this as needed instead of calling. + */ + protected SlotWidget createItemInputSlot(int index, IDrawable[] backgrounds, Pos2d pos) { + return (SlotWidget) new SlotWidget(inventoryHandler, getInputSlot() + index).setAccess(true, true) + .setBackground(backgrounds) + .setPos(pos); + } + + /** + * Override this as needed instead of calling. + */ + protected SlotWidget createItemOutputSlot(int index, IDrawable[] backgrounds, Pos2d pos) { + return (SlotWidget) new SlotWidget(inventoryHandler, getOutputSlot() + index).setAccess(true, false) + .setBackground(backgrounds) + .setPos(pos); + } + + /** + * Override this as needed instead of calling. + */ + protected SlotWidget createSpecialSlot(IDrawable[] backgrounds, Pos2d pos, BasicUIProperties uiProperties) { + return (SlotWidget) new SlotWidget(inventoryHandler, getSpecialSlotIndex()).setAccess(true, true) + .disableShiftInsert() + .setGTTooltip( + () -> mTooltipCache.getData(uiProperties.useSpecialSlot ? SPECIAL_SLOT_TOOLTIP : UNUSED_SLOT_TOOLTIP)) + .setTooltipShowUpDelay(TOOLTIP_DELAY) + .setBackground(backgrounds) + .setPos(pos); + } + + protected FluidSlotWidget createFluidInputSlot(IDrawable[] backgrounds, Pos2d pos) { + return (FluidSlotWidget) new FluidSlotWidget(fluidTank).setBackground(backgrounds) + .setPos(pos); + } + + protected FluidSlotWidget createFluidOutputSlot(IDrawable[] backgrounds, Pos2d pos) { + return (FluidSlotWidget) new FluidSlotWidget(fluidOutputTank).setInteraction(true, false) + .setBackground(backgrounds) + .setPos(pos); + } + + @Override + protected SlotWidget createChargerSlot(int x, int y) { + if (isSteampowered()) { + return (SlotWidget) createChargerSlot(x, y, UNUSED_SLOT_TOOLTIP, new String[0]) + .setBackground(getGUITextureSet().getItemSlot()); + } else { + return super.createChargerSlot(x, y); + } + } + + protected CycleButtonWidget createItemAutoOutputButton() { + return (CycleButtonWidget) new CycleButtonWidget().setToggle(() -> mItemTransfer, val -> mItemTransfer = val) + .setStaticTexture(GT_UITextures.OVERLAY_BUTTON_AUTOOUTPUT_ITEM) + .setVariableBackground(GT_UITextures.BUTTON_STANDARD_TOGGLE) + .setGTTooltip(() -> mTooltipCache.getData(ITEM_TRANSFER_TOOLTIP)) + .setTooltipShowUpDelay(TOOLTIP_DELAY) + .setPos(25, 62) + .setSize(18, 18); + } + + protected CycleButtonWidget createFluidAutoOutputButton() { + return (CycleButtonWidget) new CycleButtonWidget().setToggle(() -> mFluidTransfer, val -> mFluidTransfer = val) + .setStaticTexture(GT_UITextures.OVERLAY_BUTTON_AUTOOUTPUT_FLUID) + .setVariableBackground(GT_UITextures.BUTTON_STANDARD_TOGGLE) + .setGTTooltip(() -> mTooltipCache.getData(FLUID_TRANSFER_TOOLTIP)) + .setTooltipShowUpDelay(TOOLTIP_DELAY) + .setPos(7, 62) + .setSize(18, 18); + } + + protected Widget setNEITransferRect(Widget widget, String transferRectID) { + if (GT_Utility.isStringInvalid(transferRectID)) { + return widget; + } + final String transferRectTooltip; + if (isSteampowered()) { + transferRectTooltip = StatCollector + .translateToLocalFormatted(NEI_TRANSFER_STEAM_TOOLTIP, overclockDescriber.getTierString()); + } else { + transferRectTooltip = StatCollector + .translateToLocalFormatted(NEI_TRANSFER_VOLTAGE_TOOLTIP, overclockDescriber.getTierString()); + } + widget.setNEITransferRect(transferRectID, new Object[] { overclockDescriber }, transferRectTooltip); + return widget; + } + + protected void addProgressBarSpecialTextures(ModularWindow.Builder builder, BasicUIProperties uiProperties) { + if (isSteampowered()) { + for (Pair<SteamTexture, Pair<Size, Pos2d>> specialTexture : uiProperties.specialTexturesSteam) { + builder.widget( + new DrawableWidget().setDrawable( + specialTexture.getLeft() + .get(getSteamVariant())) + .setSize( + specialTexture.getRight() + .getLeft()) + .setPos( + specialTexture.getRight() + .getRight())); + } + } else { + for (Pair<IDrawable, Pair<Size, Pos2d>> specialTexture : uiProperties.specialTextures) { + builder.widget( + new DrawableWidget().setDrawable(specialTexture.getLeft()) + .setSize( + specialTexture.getRight() + .getLeft()) + .setPos( + specialTexture.getRight() + .getRight())); + } + } + } + + protected DrawableWidget createErrorStatusArea(ModularWindow.Builder builder, IDrawable picture) { + return (DrawableWidget) new DrawableWidget().setDrawable(picture) + .setTooltipShowUpDelay(TOOLTIP_DELAY) + .setEnabled( + widget -> !widget.getTooltip() + .isEmpty()) + .dynamicTooltip(this::getErrorDescriptions) + .dynamicTooltipShift(this::getErrorDescriptionsShift) + .setPos(79, 44) + .setSize(18, 18) + .attachSyncer( + new FakeSyncWidget.BooleanSyncer(() -> mStuttering, val -> mStuttering = val), + builder, + (widget, val) -> widget.notifyTooltipChange()) + .attachSyncer( + new FakeSyncWidget.IntegerSyncer( + () -> getBaseMetaTileEntity().getErrorDisplayID(), + val -> getBaseMetaTileEntity().setErrorDisplayID(val)), + builder, + (widget, val) -> widget.notifyTooltipChange()); + } + + protected List<String> getErrorDescriptions() { + final GT_TooltipDataCache.TooltipData tooltip = getErrorTooltip(); + return tooltip != null ? tooltip.text : Collections.emptyList(); + } + + protected List<String> getErrorDescriptionsShift() { + final GT_TooltipDataCache.TooltipData tooltip = getErrorTooltip(); + return tooltip != null ? tooltip.shiftText : Collections.emptyList(); + } + + protected GT_TooltipDataCache.TooltipData getErrorTooltip() { + if (isSteampowered()) { + if ((getBaseMetaTileEntity().getErrorDisplayID() & 64) != 0) { + return mTooltipCache.getData(STALLED_VENT_TOOLTIP); + } + } + if (mStuttering) { + return mTooltipCache.getData( + STALLED_STUTTERING_TOOLTIP, + StatCollector.translateToLocal(POWER_SOURCE_KEY + (isSteampowered() ? "steam" : "power"))); + } + return null; + } + + protected static int getCapacityForTier(int tier) { + return switch (tier) { + case 0 -> 8000; + case 1 -> 16000; + case 2 -> 32000; + case 3 -> 64000; + case 4 -> 128000; + default -> 256000; + }; + } +} diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicMachine_Bronze.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicMachine_Bronze.java new file mode 100644 index 0000000000..5eb648d560 --- /dev/null +++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicMachine_Bronze.java @@ -0,0 +1,387 @@ +package gregtech.api.metatileentity.implementations; + +import static gregtech.api.enums.GT_Values.D1; +import static gregtech.api.enums.Textures.BlockIcons.MACHINE_BRONZEBRICKS_BOTTOM; +import static gregtech.api.enums.Textures.BlockIcons.MACHINE_BRONZEBRICKS_SIDE; +import static gregtech.api.enums.Textures.BlockIcons.MACHINE_BRONZEBRICKS_TOP; +import static gregtech.api.enums.Textures.BlockIcons.MACHINE_BRONZE_BOTTOM; +import static gregtech.api.enums.Textures.BlockIcons.MACHINE_BRONZE_SIDE; +import static gregtech.api.enums.Textures.BlockIcons.MACHINE_BRONZE_TOP; +import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_PIPE_OUT; +import static gregtech.api.objects.XSTR.XSTR_INSTANCE; + +import java.util.Arrays; + +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.AxisAlignedBB; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.StatCollector; +import net.minecraftforge.common.util.ForgeDirection; + +import com.gtnewhorizons.modularui.api.drawable.IDrawable; +import com.gtnewhorizons.modularui.api.math.Pos2d; +import com.gtnewhorizons.modularui.api.screen.ModularWindow; +import com.gtnewhorizons.modularui.common.widget.DrawableWidget; +import com.gtnewhorizons.modularui.common.widget.FluidSlotWidget; + +import gregtech.api.GregTech_API; +import gregtech.api.enums.Dyes; +import gregtech.api.enums.ParticleFX; +import gregtech.api.enums.SoundResource; +import gregtech.api.enums.SteamVariant; +import gregtech.api.enums.TierEU; +import gregtech.api.gui.modularui.GUITextureSet; +import gregtech.api.interfaces.ITexture; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.objects.GT_ItemStack; +import gregtech.api.objects.overclockdescriber.OverclockDescriber; +import gregtech.api.objects.overclockdescriber.SteamOverclockDescriber; +import gregtech.api.render.TextureFactory; +import gregtech.api.util.GT_Log; +import gregtech.api.util.GT_Recipe; +import gregtech.api.util.GT_Utility; +import gregtech.api.util.WorldSpawnedEventBuilder.ParticleEventBuilder; + +/** + * NEVER INCLUDE THIS FILE IN YOUR MOD!!! + * <p/> + * This is the main construct for my Basic Machines such as the Automatic Extractor Extend this class to make a simple + * Machine + */ +public abstract class GT_MetaTileEntity_BasicMachine_Bronze extends GT_MetaTileEntity_BasicMachine { + + private static final String TT_machineType = "GT5U.MBTT.MachineType"; + private static final int NEEDS_STEAM_VENTING = 64; + public boolean mNeedsSteamVenting = false; + + public GT_MetaTileEntity_BasicMachine_Bronze(int aID, String aName, String aNameRegional, String aDescription, + int aInputSlotCount, int aOutputSlotCount, boolean aHighPressure) { + super(aID, aName, aNameRegional, aHighPressure ? 2 : 1, 0, aDescription, aInputSlotCount, aOutputSlotCount); + } + + public GT_MetaTileEntity_BasicMachine_Bronze(String aName, String[] aDescription, ITexture[][][] aTextures, + int aInputSlotCount, int aOutputSlotCount, boolean aHighPressure) { + super(aName, aHighPressure ? 2 : 1, 0, aDescription, aTextures, aInputSlotCount, aOutputSlotCount); + } + + protected boolean isBricked() { + return false; + } + + @Override + public OverclockDescriber createOverclockDescriber() { + return new SteamOverclockDescriber(SteamVariant.BRONZE, 1, 2); + } + + @Override + public void saveNBTData(NBTTagCompound aNBT) { + super.saveNBTData(aNBT); + aNBT.setBoolean("mNeedsSteamVenting", mNeedsSteamVenting); + } + + @Override + public void loadNBTData(NBTTagCompound aNBT) { + super.loadNBTData(aNBT); + mNeedsSteamVenting = aNBT.getBoolean("mNeedsSteamVenting"); + } + + @Override + public boolean isElectric() { + return false; + } + + @Override + public boolean isEnetInput() { + return false; + } + + @Override + public boolean isInputFacing(ForgeDirection side) { + return false; + } + + @Override + public long maxEUStore() { + return 0; + } + + @Override + public long maxEUInput() { + return 0; + } + + @Override + public int rechargerSlotCount() { + return 0; + } + + @Override + public int dechargerSlotCount() { + return 0; + } + + @Override + public boolean isSteampowered() { + return true; + } + + @Override + public boolean isFacingValid(ForgeDirection facing) { + return super.isFacingValid(facing) && facing != mMainFacing; + } + + @Override + public long getMinimumStoredEU() { + return 1000; + } + + @Override + public long maxSteamStore() { + return 16000; + } + + @Override + public boolean isLiquidInput(ForgeDirection side) { + return side != mMainFacing; + } + + @Override + public boolean isLiquidOutput(ForgeDirection side) { + return side != mMainFacing; + } + + @Override + public boolean doesAutoOutput() { + return false; + } + + @Override + public boolean allowToCheckRecipe() { + if (mNeedsSteamVenting + && getBaseMetaTileEntity().getCoverIDAtSide(getBaseMetaTileEntity().getFrontFacing()) == 0 + && !GT_Utility.hasBlockHitBox( + getBaseMetaTileEntity().getWorld(), + getBaseMetaTileEntity().getOffsetX(getBaseMetaTileEntity().getFrontFacing(), 1), + getBaseMetaTileEntity().getOffsetY(getBaseMetaTileEntity().getFrontFacing(), 1), + getBaseMetaTileEntity().getOffsetZ(getBaseMetaTileEntity().getFrontFacing(), 1))) { + sendSound((byte) 9); + mNeedsSteamVenting = false; + try { + for (EntityLivingBase tLiving : getBaseMetaTileEntity().getWorld() + .getEntitiesWithinAABB( + EntityLivingBase.class, + AxisAlignedBB.getBoundingBox( + getBaseMetaTileEntity().getOffsetX(getBaseMetaTileEntity().getFrontFacing(), 1), + getBaseMetaTileEntity().getOffsetY(getBaseMetaTileEntity().getFrontFacing(), 1), + getBaseMetaTileEntity().getOffsetZ(getBaseMetaTileEntity().getFrontFacing(), 1), + getBaseMetaTileEntity().getOffsetX(getBaseMetaTileEntity().getFrontFacing(), 1) + 1, + getBaseMetaTileEntity().getOffsetY(getBaseMetaTileEntity().getFrontFacing(), 1) + 1, + getBaseMetaTileEntity().getOffsetZ(getBaseMetaTileEntity().getFrontFacing(), 1) + 1))) { + GT_Utility.applyHeatDamage(tLiving, getSteamDamage()); + } + } catch (Throwable e) { + if (D1) e.printStackTrace(GT_Log.err); + } + } + return !mNeedsSteamVenting; + } + + @Override + public int checkRecipe() { + GT_Recipe tRecipe = getRecipeMap().findRecipe(getBaseMetaTileEntity(), false, TierEU.LV, null, getAllInputs()); + if ((tRecipe != null) && (canOutput(tRecipe.mOutputs)) + && (tRecipe.isRecipeInputEqual(true, null, getAllInputs()))) { + this.mOutputItems[0] = tRecipe.getOutput(0); + calculateCustomOverclock(tRecipe); + return FOUND_AND_SUCCESSFULLY_USED_RECIPE; + } + return DID_NOT_FIND_RECIPE; + } + + @Override + public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) { + super.onPostTick(aBaseMetaTileEntity, aTick); + // Super already zeroed out setErrorDisplayID, add additional error codes here. + aBaseMetaTileEntity.setErrorDisplayID(aBaseMetaTileEntity.getErrorDisplayID() | (mNeedsSteamVenting ? 64 : 0)); + } + + @Override + public void endProcess() { + if (isSteampowered()) mNeedsSteamVenting = true; + } + + @Override + public void doSound(byte aIndex, double aX, double aY, double aZ) { + super.doSound(aIndex, aX, aY, aZ); + if (aIndex == 9) { + GT_Utility.doSoundAtClient(SoundResource.RANDOM_FIZZ, 5, 1.0F, aX, aY, aZ); + + new ParticleEventBuilder().setIdentifier(ParticleFX.CLOUD) + .setWorld(getBaseMetaTileEntity().getWorld()) + .setMotion( + getBaseMetaTileEntity().getFrontFacing().offsetX / 5.0, + getBaseMetaTileEntity().getFrontFacing().offsetY / 5.0, + getBaseMetaTileEntity().getFrontFacing().offsetZ / 5.0) + .<ParticleEventBuilder>times( + 8, + x -> x + .setPosition( + aX - 0.5 + XSTR_INSTANCE.nextFloat(), + aY - 0.5 + XSTR_INSTANCE.nextFloat(), + aZ - 0.5 + XSTR_INSTANCE.nextFloat()) + .run()); + } + } + + @Override + public boolean isGivingInformation() { + return false; + } + + @Override + public boolean allowCoverOnSide(ForgeDirection side, GT_ItemStack aCoverID) { + return GregTech_API.getCoverBehaviorNew(aCoverID.toStack()) + .isSimpleCover() && super.allowCoverOnSide(side, aCoverID); + } + + public float getSteamDamage() { + return 6.0F * mTier; + } + + @Override + public ITexture[] getSideFacingActive(byte aColor) { + return new ITexture[] { TextureFactory.of( + isBricked() ? MACHINE_BRONZEBRICKS_SIDE : MACHINE_BRONZE_SIDE, + Dyes.getModulation(aColor, Dyes._NULL.mRGBa)) }; + } + + @Override + public ITexture[] getSideFacingInactive(byte aColor) { + return new ITexture[] { TextureFactory.of( + isBricked() ? MACHINE_BRONZEBRICKS_SIDE : MACHINE_BRONZE_SIDE, + Dyes.getModulation(aColor, Dyes._NULL.mRGBa)) }; + } + + @Override + public ITexture[] getFrontFacingActive(byte aColor) { + return new ITexture[] { TextureFactory.of( + isBricked() ? MACHINE_BRONZEBRICKS_SIDE : MACHINE_BRONZE_SIDE, + Dyes.getModulation(aColor, Dyes._NULL.mRGBa)) }; + } + + @Override + public ITexture[] getFrontFacingInactive(byte aColor) { + return new ITexture[] { TextureFactory.of( + isBricked() ? MACHINE_BRONZEBRICKS_SIDE : MACHINE_BRONZE_SIDE, + Dyes.getModulation(aColor, Dyes._NULL.mRGBa)) }; + } + + @Override + public ITexture[] getTopFacingActive(byte aColor) { + return new ITexture[] { TextureFactory.of( + isBricked() ? MACHINE_BRONZEBRICKS_TOP : MACHINE_BRONZE_TOP, + Dyes.getModulation(aColor, Dyes._NULL.mRGBa)) }; + } + + @Override + public ITexture[] getTopFacingInactive(byte aColor) { + return new ITexture[] { TextureFactory.of( + isBricked() ? MACHINE_BRONZEBRICKS_TOP : MACHINE_BRONZE_TOP, + Dyes.getModulation(aColor, Dyes._NULL.mRGBa)) }; + } + + @Override + public ITexture[] getBottomFacingActive(byte aColor) { + return new ITexture[] { TextureFactory.of( + isBricked() ? MACHINE_BRONZEBRICKS_BOTTOM : MACHINE_BRONZE_BOTTOM, + Dyes.getModulation(aColor, Dyes._NULL.mRGBa)) }; + } + + @Override + public ITexture[] getBottomFacingInactive(byte aColor) { + return new ITexture[] { TextureFactory.of( + isBricked() ? MACHINE_BRONZEBRICKS_BOTTOM : MACHINE_BRONZE_BOTTOM, + Dyes.getModulation(aColor, Dyes._NULL.mRGBa)) }; + } + + @Override + public ITexture[] getBottomFacingPipeActive(byte aColor) { + return new ITexture[] { TextureFactory.of( + isBricked() ? MACHINE_BRONZEBRICKS_BOTTOM : MACHINE_BRONZE_BOTTOM, + Dyes.getModulation(aColor, Dyes._NULL.mRGBa)), TextureFactory.of(OVERLAY_PIPE_OUT) }; + } + + @Override + public ITexture[] getBottomFacingPipeInactive(byte aColor) { + return new ITexture[] { TextureFactory.of( + isBricked() ? MACHINE_BRONZEBRICKS_BOTTOM : MACHINE_BRONZE_BOTTOM, + Dyes.getModulation(aColor, Dyes._NULL.mRGBa)), TextureFactory.of(OVERLAY_PIPE_OUT) }; + } + + @Override + public ITexture[] getTopFacingPipeActive(byte aColor) { + return new ITexture[] { TextureFactory.of( + isBricked() ? MACHINE_BRONZEBRICKS_TOP : MACHINE_BRONZE_TOP, + Dyes.getModulation(aColor, Dyes._NULL.mRGBa)), TextureFactory.of(OVERLAY_PIPE_OUT) }; + } + + @Override + public ITexture[] getTopFacingPipeInactive(byte aColor) { + return new ITexture[] { TextureFactory.of( + isBricked() ? MACHINE_BRONZEBRICKS_TOP : MACHINE_BRONZE_TOP, + Dyes.getModulation(aColor, Dyes._NULL.mRGBa)), TextureFactory.of(OVERLAY_PIPE_OUT) }; + } + + @Override + public ITexture[] getSideFacingPipeActive(byte aColor) { + return new ITexture[] { TextureFactory.of( + isBricked() ? MACHINE_BRONZEBRICKS_SIDE : MACHINE_BRONZE_SIDE, + Dyes.getModulation(aColor, Dyes._NULL.mRGBa)), TextureFactory.of(OVERLAY_PIPE_OUT) }; + } + + @Override + public ITexture[] getSideFacingPipeInactive(byte aColor) { + return new ITexture[] { TextureFactory.of( + isBricked() ? MACHINE_BRONZEBRICKS_SIDE : MACHINE_BRONZE_SIDE, + Dyes.getModulation(aColor, Dyes._NULL.mRGBa)), TextureFactory.of(OVERLAY_PIPE_OUT) }; + } + + @Override + public SteamVariant getSteamVariant() { + return SteamVariant.BRONZE; + } + + @Override + public GUITextureSet getGUITextureSet() { + return GUITextureSet.STEAM.apply(getSteamVariant()); + } + + @Override + public void addGregTechLogo(ModularWindow.Builder builder) { + builder.widget( + new DrawableWidget().setDrawable(getGUITextureSet().getGregTechLogo()) + .setSize(17, 17) + .setPos(152, 63)); + } + + @Override + protected FluidSlotWidget createFluidInputSlot(IDrawable[] backgrounds, Pos2d pos) { + return null; + } + + @Override + protected FluidSlotWidget createFluidOutputSlot(IDrawable[] backgrounds, Pos2d pos) { + return null; + } + + @Override + public String[] getDescription() { + String[] description = Arrays.copyOf(mDescriptionArray, mDescriptionArray.length + 1); + description[mDescriptionArray.length] = StatCollector.translateToLocal(TT_machineType) + ": " + + EnumChatFormatting.YELLOW + + StatCollector.translateToLocal(this.getRecipeMap().unlocalizedName) + + EnumChatFormatting.RESET; + return description; + } +} diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicMachine_GT_Recipe.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicMachine_GT_Recipe.java new file mode 100644 index 0000000000..8bba0fee25 --- /dev/null +++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicMachine_GT_Recipe.java @@ -0,0 +1,820 @@ +package gregtech.api.metatileentity.implementations; + +import static gregtech.api.enums.GT_Values.V; +import static gregtech.api.enums.GT_Values.VN; +import static gregtech.api.enums.GT_Values.W; +import static gregtech.api.enums.GT_Values.ticksBetweenSounds; +import static gregtech.api.enums.Mods.BartWorks; +import static gregtech.api.objects.XSTR.XSTR_INSTANCE; +import static net.minecraftforge.common.util.ForgeDirection.UP; + +import java.util.Locale; + +import net.minecraft.block.Block; +import net.minecraft.init.Blocks; +import net.minecraft.item.ItemStack; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.common.util.ForgeDirection; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.oredict.OreDictionary; + +import com.gtnewhorizons.modularui.api.drawable.FallbackableUITexture; +import com.gtnewhorizons.modularui.api.drawable.UITexture; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import gregtech.api.enums.ItemList; +import gregtech.api.enums.Materials; +import gregtech.api.enums.OrePrefixes; +import gregtech.api.enums.ParticleFX; +import gregtech.api.enums.SoundResource; +import gregtech.api.enums.Textures.BlockIcons.CustomIcon; +import gregtech.api.enums.Tier; +import gregtech.api.gui.modularui.GT_UITextures; +import gregtech.api.interfaces.ITexture; +import gregtech.api.interfaces.metatileentity.IMetaTileEntity; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.metatileentity.BaseMetaTileEntity; +import gregtech.api.recipe.BasicUIProperties; +import gregtech.api.recipe.RecipeMap; +import gregtech.api.render.TextureFactory; +import gregtech.api.util.ExternalMaterials; +import gregtech.api.util.GT_ModHandler; +import gregtech.api.util.GT_Utility; +import gregtech.api.util.WorldSpawnedEventBuilder.ParticleEventBuilder; +import ic2.core.Ic2Items; + +/** + * NEVER INCLUDE THIS FILE IN YOUR MOD!!! + * <p/> + * This is the main construct for my Basic Machines such as the Automatic Extractor Extend this class to make a simple + * Machine + */ +public class GT_MetaTileEntity_BasicMachine_GT_Recipe extends GT_MetaTileEntity_BasicMachine { + + private final RecipeMap<?> mRecipes; + private final int mTankCapacity; + private final SpecialEffects mSpecialEffect; + private final ResourceLocation mSoundResourceLocation; + private FallbackableUITexture progressBarTexture; + private int recipeCatalystPriority; + + /** + * Registers machine with single-line description, specific tank capacity, and sound specified by ResourceLocation. + */ + public GT_MetaTileEntity_BasicMachine_GT_Recipe(int aID, String aName, String aNameRegional, int aTier, + String aDescription, RecipeMap<?> aRecipes, int aInputSlots, int aOutputSlots, int aTankCapacity, + ResourceLocation aSound, SpecialEffects aSpecialEffect, String aOverlays, Object[] aRecipe) { + this( + aID, + aName, + aNameRegional, + aTier, + new String[] { aDescription }, + aRecipes, + aInputSlots, + aOutputSlots, + aTankCapacity, + aSound, + aSpecialEffect, + aOverlays, + aRecipe); + } + + /** + * Registers machine with multi-line descriptions, specific tank capacity, and sound specified by ResourceLocation. + */ + public GT_MetaTileEntity_BasicMachine_GT_Recipe(int aID, String aName, String aNameRegional, int aTier, + String[] aDescription, RecipeMap<?> aRecipes, int aInputSlots, int aOutputSlots, int aTankCapacity, + ResourceLocation aSound, SpecialEffects aSpecialEffect, String aOverlays, Object[] aRecipe) { + super( + aID, + aName, + aNameRegional, + aTier, + aRecipes.getAmperage(), + aDescription, + aInputSlots, + aOutputSlots, + TextureFactory.of( + TextureFactory.of( + new CustomIcon("basicmachines/" + aOverlays.toLowerCase(Locale.ENGLISH) + "/OVERLAY_SIDE_ACTIVE")), + TextureFactory.builder() + .addIcon( + (new CustomIcon( + "basicmachines/" + aOverlays.toLowerCase(Locale.ENGLISH) + "/OVERLAY_SIDE_ACTIVE_GLOW"))) + .glow() + .build()), + TextureFactory.of( + TextureFactory + .of(new CustomIcon("basicmachines/" + aOverlays.toLowerCase(Locale.ENGLISH) + "/OVERLAY_SIDE")), + TextureFactory.builder() + .addIcon( + (new CustomIcon( + "basicmachines/" + aOverlays.toLowerCase(Locale.ENGLISH) + "/OVERLAY_SIDE_GLOW"))) + .glow() + .build()), + TextureFactory.of( + TextureFactory.of( + new CustomIcon("basicmachines/" + aOverlays.toLowerCase(Locale.ENGLISH) + "/OVERLAY_FRONT_ACTIVE")), + TextureFactory.builder() + .addIcon( + (new CustomIcon( + "basicmachines/" + aOverlays.toLowerCase(Locale.ENGLISH) + "/OVERLAY_FRONT_ACTIVE_GLOW"))) + .glow() + .build()), + TextureFactory.of( + TextureFactory + .of(new CustomIcon("basicmachines/" + aOverlays.toLowerCase(Locale.ENGLISH) + "/OVERLAY_FRONT")), + TextureFactory.builder() + .addIcon( + (new CustomIcon( + "basicmachines/" + aOverlays.toLowerCase(Locale.ENGLISH) + "/OVERLAY_FRONT_GLOW"))) + .glow() + .build()), + TextureFactory.of( + TextureFactory.of( + new CustomIcon("basicmachines/" + aOverlays.toLowerCase(Locale.ENGLISH) + "/OVERLAY_TOP_ACTIVE")), + TextureFactory.builder() + .addIcon( + (new CustomIcon( + "basicmachines/" + aOverlays.toLowerCase(Locale.ENGLISH) + "/OVERLAY_TOP_ACTIVE_GLOW"))) + .glow() + .build()), + TextureFactory.of( + TextureFactory + .of(new CustomIcon("basicmachines/" + aOverlays.toLowerCase(Locale.ENGLISH) + "/OVERLAY_TOP")), + TextureFactory.builder() + .addIcon( + (new CustomIcon( + "basicmachines/" + aOverlays.toLowerCase(Locale.ENGLISH) + "/OVERLAY_TOP_GLOW"))) + .glow() + .build()), + TextureFactory.of( + TextureFactory.of( + new CustomIcon( + "basicmachines/" + aOverlays.toLowerCase(Locale.ENGLISH) + "/OVERLAY_BOTTOM_ACTIVE")), + TextureFactory.builder() + .addIcon( + (new CustomIcon( + "basicmachines/" + aOverlays.toLowerCase(Locale.ENGLISH) + "/OVERLAY_BOTTOM_ACTIVE_GLOW"))) + .glow() + .build()), + TextureFactory.of( + TextureFactory + .of(new CustomIcon("basicmachines/" + aOverlays.toLowerCase(Locale.ENGLISH) + "/OVERLAY_BOTTOM")), + TextureFactory.builder() + .addIcon( + (new CustomIcon( + "basicmachines/" + aOverlays.toLowerCase(Locale.ENGLISH) + "/OVERLAY_BOTTOM_GLOW"))) + .glow() + .build())); + this.mTankCapacity = aTankCapacity; + this.mSpecialEffect = aSpecialEffect; + this.mRecipes = aRecipes; + this.mSoundResourceLocation = aSound; + this.progressBarTexture = mRecipes.getFrontend() + .getUIProperties().progressBarTexture; + + // TODO: CHECK + if (aRecipe != null) { + for (int i = 3; i < aRecipe.length; i++) { + if (!(aRecipe[i] instanceof X)) continue; + + // spotless:off + aRecipe[i] = switch ((X) aRecipe[i]) { + case CIRCUIT -> Tier.ELECTRIC[this.mTier].mManagingObject; + case BETTER_CIRCUIT -> Tier.ELECTRIC[this.mTier].mBetterManagingObject; + case HULL -> Tier.ELECTRIC[this.mTier].mHullObject; + case WIRE -> Tier.ELECTRIC[this.mTier].mConductingObject; + case WIRE4 -> Tier.ELECTRIC[this.mTier].mLargerConductingObject; + case STICK_DISTILLATION -> OrePrefixes.stick.get(Materials.Blaze); + + case GLASS -> switch (this.mTier) { + case 0, 1, 2, 3 -> new ItemStack(Blocks.glass, 1, W); + case 4, 5, 6, 7, 8 -> BartWorks.isModLoaded() ? "blockGlass" + VN[aTier] : Ic2Items.reinforcedGlass; + default -> BartWorks.isModLoaded() ? "blockGlass" + VN[8] : Ic2Items.reinforcedGlass; + }; + + case PLATE -> switch (this.mTier) { + case 0, 1 -> OrePrefixes.plate.get(Materials.Steel); + case 2 -> OrePrefixes.plate.get(Materials.Aluminium); + case 3 -> OrePrefixes.plate.get(Materials.StainlessSteel); + case 4 -> OrePrefixes.plate.get(Materials.Titanium); + case 5 -> OrePrefixes.plate.get(Materials.TungstenSteel); + case 6 -> OrePrefixes.plate.get(Materials.HSSG); + case 7 -> OrePrefixes.plate.get(Materials.HSSE); + default -> OrePrefixes.plate.get(Materials.Neutronium); + }; + + case PIPE -> switch (this.mTier) { + case 0, 1 -> OrePrefixes.pipeMedium.get(Materials.Bronze); + case 2 -> OrePrefixes.pipeMedium.get(Materials.Steel); + case 3 -> OrePrefixes.pipeMedium.get(Materials.StainlessSteel); + case 4 -> OrePrefixes.pipeMedium.get(Materials.Titanium); + case 5 -> OrePrefixes.pipeMedium.get(Materials.TungstenSteel); + case 6 -> OrePrefixes.pipeSmall.get(Materials.Ultimate); + case 7 -> OrePrefixes.pipeMedium.get(Materials.Ultimate); + case 8 -> OrePrefixes.pipeLarge.get(Materials.Ultimate); + default -> OrePrefixes.pipeHuge.get(Materials.Ultimate); + }; + + case COIL_HEATING -> switch (this.mTier) { + case 0, 1 -> OrePrefixes.wireGt02.get(Materials.AnyCopper); + case 2 -> OrePrefixes.wireGt02.get(Materials.Cupronickel); + case 3 -> OrePrefixes.wireGt02.get(Materials.Kanthal); + case 4 -> OrePrefixes.wireGt02.get(Materials.Nichrome); + case 5 -> OrePrefixes.wireGt02.get(Materials.TPV); + case 6 -> OrePrefixes.wireGt02.get(Materials.HSSG); + case 7 -> OrePrefixes.wireGt02.get(Materials.Naquadah); + case 8 -> OrePrefixes.wireGt02.get(Materials.NaquadahAlloy); + case 9 -> OrePrefixes.wireGt04.get(Materials.NaquadahAlloy); + default -> OrePrefixes.wireGt08.get(Materials.NaquadahAlloy); + }; + + case COIL_HEATING_DOUBLE -> switch (this.mTier) { + case 0, 1 -> OrePrefixes.wireGt04.get(Materials.AnyCopper); + case 2 -> OrePrefixes.wireGt04.get(Materials.Cupronickel); + case 3 -> OrePrefixes.wireGt04.get(Materials.Kanthal); + case 4 -> OrePrefixes.wireGt04.get(Materials.Nichrome); + case 5 -> OrePrefixes.wireGt04.get(Materials.TPV); + case 6 -> OrePrefixes.wireGt04.get(Materials.HSSG); + case 7 -> OrePrefixes.wireGt04.get(Materials.Naquadah); + case 8 -> OrePrefixes.wireGt04.get(Materials.NaquadahAlloy); + case 9 -> OrePrefixes.wireGt08.get(Materials.NaquadahAlloy); + default -> OrePrefixes.wireGt16.get(Materials.NaquadahAlloy); + }; + + case STICK_MAGNETIC -> switch (this.mTier) { + case 0, 1 -> OrePrefixes.stick.get(Materials.IronMagnetic); + case 2, 3 -> OrePrefixes.stick.get(Materials.SteelMagnetic); + case 4, 5 -> OrePrefixes.stick.get(Materials.NeodymiumMagnetic); + case 6, 7, 8, 9 -> OrePrefixes.stick.get(Materials.SamariumMagnetic); + default -> OrePrefixes.stick.get(Materials.TengamAttuned); + }; + + case STICK_ELECTROMAGNETIC -> switch (this.mTier) { + case 0, 1 -> OrePrefixes.stick.get(Materials.AnyIron); + case 2, 3 -> OrePrefixes.stick.get(Materials.Steel); + case 4 -> OrePrefixes.stick.get(Materials.Neodymium); + default -> OrePrefixes.stick.get(Materials.VanadiumGallium); + }; + + case COIL_ELECTRIC -> switch (this.mTier) { + case 0 -> OrePrefixes.wireGt01.get(Materials.Lead); + case 1 -> OrePrefixes.wireGt02.get(Materials.Tin); + case 2 -> OrePrefixes.wireGt02.get(Materials.AnyCopper); + case 3 -> OrePrefixes.wireGt04.get(Materials.AnyCopper); + case 4 -> OrePrefixes.wireGt08.get(Materials.AnnealedCopper); + case 5 -> OrePrefixes.wireGt16.get(Materials.AnnealedCopper); + case 6 -> OrePrefixes.wireGt04.get(Materials.YttriumBariumCuprate); + case 7 -> OrePrefixes.wireGt08.get(Materials.Iridium); + default -> OrePrefixes.wireGt16.get(Materials.Osmium); + }; + + case ROBOT_ARM -> switch (this.mTier) { + case 0, 1 -> ItemList.Robot_Arm_LV; + case 2 -> ItemList.Robot_Arm_MV; + case 3 -> ItemList.Robot_Arm_HV; + case 4 -> ItemList.Robot_Arm_EV; + case 5 -> ItemList.Robot_Arm_IV; + case 6 -> ItemList.Robot_Arm_LuV; + case 7 -> ItemList.Robot_Arm_ZPM; + case 8 -> ItemList.Robot_Arm_UV; + case 9 -> ItemList.Robot_Arm_UHV; + case 10 -> ItemList.Robot_Arm_UEV; + case 11 -> ItemList.Robot_Arm_UIV; + case 12 -> ItemList.Robot_Arm_UMV; + case 13 -> ItemList.Robot_Arm_UXV; + default -> ItemList.Robot_Arm_MAX; + }; + + case PUMP -> switch (this.mTier) { + case 0, 1 -> ItemList.Electric_Pump_LV; + case 2 -> ItemList.Electric_Pump_MV; + case 3 -> ItemList.Electric_Pump_HV; + case 4 -> ItemList.Electric_Pump_EV; + case 5 -> ItemList.Electric_Pump_IV; + case 6 -> ItemList.Electric_Pump_LuV; + case 7 -> ItemList.Electric_Pump_ZPM; + case 8 -> ItemList.Electric_Pump_UV; + case 9 -> ItemList.Electric_Pump_UHV; + case 10 -> ItemList.Electric_Pump_UEV; + case 11 -> ItemList.Electric_Pump_UIV; + case 12 -> ItemList.Electric_Pump_UMV; + case 13 -> ItemList.Electric_Pump_UXV; + default -> ItemList.Electric_Pump_MAX; + }; + + case MOTOR -> switch (this.mTier) { + case 0, 1 -> ItemList.Electric_Motor_LV; + case 2 -> ItemList.Electric_Motor_MV; + case 3 -> ItemList.Electric_Motor_HV; + case 4 -> ItemList.Electric_Motor_EV; + case 5 -> ItemList.Electric_Motor_IV; + case 6 -> ItemList.Electric_Motor_LuV; + case 7 -> ItemList.Electric_Motor_ZPM; + case 8 -> ItemList.Electric_Motor_UV; + case 9 -> ItemList.Electric_Motor_UHV; + case 10 -> ItemList.Electric_Motor_UEV; + case 11 -> ItemList.Electric_Motor_UIV; + case 12 -> ItemList.Electric_Motor_UMV; + case 13 -> ItemList.Electric_Motor_UXV; + default -> ItemList.Electric_Motor_MAX; + }; + + case PISTON -> switch (this.mTier) { + case 0, 1 -> ItemList.Electric_Piston_LV; + case 2 -> ItemList.Electric_Piston_MV; + case 3 -> ItemList.Electric_Piston_HV; + case 4 -> ItemList.Electric_Piston_EV; + case 5 -> ItemList.Electric_Piston_IV; + case 6 -> ItemList.Electric_Piston_LuV; + case 7 -> ItemList.Electric_Piston_ZPM; + case 8 -> ItemList.Electric_Piston_UV; + case 9 -> ItemList.Electric_Piston_UHV; + case 10 -> ItemList.Electric_Piston_UEV; + case 11 -> ItemList.Electric_Piston_UIV; + case 12 -> ItemList.Electric_Piston_UMV; + case 13 -> ItemList.Electric_Piston_UXV; + default -> ItemList.Electric_Piston_MAX; + }; + + case CONVEYOR -> switch (this.mTier) { + case 0, 1 -> ItemList.Conveyor_Module_LV; + case 2 -> ItemList.Conveyor_Module_MV; + case 3 -> ItemList.Conveyor_Module_HV; + case 4 -> ItemList.Conveyor_Module_EV; + case 5 -> ItemList.Conveyor_Module_IV; + case 6 -> ItemList.Conveyor_Module_LuV; + case 7 -> ItemList.Conveyor_Module_ZPM; + case 8 -> ItemList.Conveyor_Module_UV; + case 9 -> ItemList.Conveyor_Module_UHV; + case 10 -> ItemList.Conveyor_Module_UEV; + case 11 -> ItemList.Conveyor_Module_UIV; + case 12 -> ItemList.Conveyor_Module_UMV; + case 13 -> ItemList.Conveyor_Module_UXV; + default -> ItemList.Conveyor_Module_MAX; + }; + + case EMITTER -> switch (this.mTier) { + case 0, 1 -> ItemList.Emitter_LV; + case 2 -> ItemList.Emitter_MV; + case 3 -> ItemList.Emitter_HV; + case 4 -> ItemList.Emitter_EV; + case 5 -> ItemList.Emitter_IV; + case 6 -> ItemList.Emitter_LuV; + case 7 -> ItemList.Emitter_ZPM; + case 8 -> ItemList.Emitter_UV; + case 9 -> ItemList.Emitter_UHV; + case 10 -> ItemList.Emitter_UEV; + case 11 -> ItemList.Emitter_UIV; + case 12 -> ItemList.Emitter_UMV; + case 13 -> ItemList.Emitter_UXV; + default -> ItemList.Emitter_MAX; + }; + + case SENSOR -> switch (this.mTier) { + case 0, 1 -> ItemList.Sensor_LV; + case 2 -> ItemList.Sensor_MV; + case 3 -> ItemList.Sensor_HV; + case 4 -> ItemList.Sensor_EV; + case 5 -> ItemList.Sensor_IV; + case 6 -> ItemList.Sensor_LuV; + case 7 -> ItemList.Sensor_ZPM; + case 8 -> ItemList.Sensor_UV; + case 9 -> ItemList.Sensor_UHV; + case 10 -> ItemList.Sensor_UEV; + case 11 -> ItemList.Sensor_UIV; + case 12 -> ItemList.Sensor_UMV; + case 13 -> ItemList.Sensor_UXV; + default -> ItemList.Sensor_MAX; + }; + + case FIELD_GENERATOR -> switch (this.mTier) { + case 0, 1 -> ItemList.Field_Generator_LV; + case 2 -> ItemList.Field_Generator_MV; + case 3 -> ItemList.Field_Generator_HV; + case 4 -> ItemList.Field_Generator_EV; + case 5 -> ItemList.Field_Generator_IV; + case 6 -> ItemList.Field_Generator_LuV; + case 7 -> ItemList.Field_Generator_ZPM; + case 8 -> ItemList.Field_Generator_UV; + case 9 -> ItemList.Field_Generator_UHV; + case 10 -> ItemList.Field_Generator_UEV; + case 11 -> ItemList.Field_Generator_UIV; + case 12 -> ItemList.Field_Generator_UMV; + case 13 -> ItemList.Field_Generator_UXV; + default -> ItemList.Field_Generator_MAX; + }; + + case ROTOR -> switch (this.mTier) { + case 0, 1 -> OrePrefixes.rotor.get(Materials.Tin); + case 2 -> OrePrefixes.rotor.get(Materials.Bronze); + case 3 -> OrePrefixes.rotor.get(Materials.Steel); + case 4 -> OrePrefixes.rotor.get(Materials.StainlessSteel); + case 5 -> OrePrefixes.rotor.get(Materials.TungstenSteel); + case 6 -> OrePrefixes.rotor.get(ExternalMaterials.getRhodiumPlatedPalladium()); + case 7 -> OrePrefixes.rotor.get(Materials.Iridium); + default -> OrePrefixes.rotor.get(Materials.Osmium); + }; + + default -> throw new IllegalArgumentException("MISSING TIER MAPPING FOR: " + aRecipe[i] + " AT TIER " + this.mTier); + }; + // spotless:on + } + + if (!GT_ModHandler.addCraftingRecipe( + getStackForm(1), + GT_ModHandler.RecipeBits.DISMANTLEABLE | GT_ModHandler.RecipeBits.BUFFERED + | GT_ModHandler.RecipeBits.NOT_REMOVABLE + | GT_ModHandler.RecipeBits.REVERSIBLE, + aRecipe)) { + throw new IllegalArgumentException("INVALID CRAFTING RECIPE FOR: " + getStackForm(1).getDisplayName()); + } + } + } + + /** + * Registers machine with single-line description, auto-scaled fluid tank, and sound specified by SoundResource. + */ + public GT_MetaTileEntity_BasicMachine_GT_Recipe(int aID, String aName, String aNameRegional, int aTier, + String aDescription, RecipeMap<?> aRecipes, int aInputSlots, int aOutputSlots, boolean usesFluids, + SoundResource aSound, SpecialEffects aSpecialEffect, String aOverlays, Object[] aRecipe) { + this( + aID, + aName, + aNameRegional, + aTier, + aDescription, + aRecipes, + aInputSlots, + aOutputSlots, + usesFluids ? getCapacityForTier(aTier) : 0, + aSound.resourceLocation, + aSpecialEffect, + aOverlays, + aRecipe); + } + + /** + * Registers machine with multi-line descriptions, auto-scaled fluid tank, and sound specified by SoundResource. + */ + public GT_MetaTileEntity_BasicMachine_GT_Recipe(int aID, String aName, String aNameRegional, int aTier, + String[] aDescription, RecipeMap<?> aRecipes, int aInputSlots, int aOutputSlots, boolean usesFluids, + SoundResource aSound, SpecialEffects aSpecialEffect, String aOverlays, Object[] aRecipe) { + this( + aID, + aName, + aNameRegional, + aTier, + aDescription, + aRecipes, + aInputSlots, + aOutputSlots, + usesFluids ? getCapacityForTier(aTier) : 0, + aSound.resourceLocation, + aSpecialEffect, + aOverlays, + aRecipe); + } + + /** + * Registers machine with single-line description, specific tank capacity, and sound specified by SoundResource. + */ + public GT_MetaTileEntity_BasicMachine_GT_Recipe(int aID, String aName, String aNameRegional, int aTier, + String aDescription, RecipeMap<?> aRecipes, int aInputSlots, int aOutputSlots, int aTankCapacity, + SoundResource aSound, SpecialEffects aSpecialEffect, String aOverlays, Object[] aRecipe) { + this( + aID, + aName, + aNameRegional, + aTier, + aDescription, + aRecipes, + aInputSlots, + aOutputSlots, + aTankCapacity, + aSound.resourceLocation, + aSpecialEffect, + aOverlays, + aRecipe); + } + + /** + * Registers machine with multi-line descriptions, specific tank capacity, and sound specified by SoundResource. + */ + public GT_MetaTileEntity_BasicMachine_GT_Recipe(int aID, String aName, String aNameRegional, int aTier, + String[] aDescription, RecipeMap<?> aRecipes, int aInputSlots, int aOutputSlots, int aTankCapacity, + SoundResource aSound, SpecialEffects aSpecialEffect, String aOverlays, Object[] aRecipe) { + this( + aID, + aName, + aNameRegional, + aTier, + aDescription, + aRecipes, + aInputSlots, + aOutputSlots, + aTankCapacity, + aSound.resourceLocation, + aSpecialEffect, + aOverlays, + aRecipe); + } + + /** + * For {@link #newMetaEntity}. + */ + public GT_MetaTileEntity_BasicMachine_GT_Recipe(String aName, int aTier, String[] aDescription, + RecipeMap<?> aRecipes, int aInputSlots, int aOutputSlots, int aTankCapacity, int aAmperage, + ITexture[][][] aTextures, ResourceLocation aSound, SpecialEffects aSpecialEffect) { + super(aName, aTier, aAmperage, aDescription, aTextures, aInputSlots, aOutputSlots); + this.mTankCapacity = aTankCapacity; + this.mSpecialEffect = aSpecialEffect; + this.mRecipes = aRecipes; + this.mSoundResourceLocation = aSound; + } + + @Override + public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) { + return new GT_MetaTileEntity_BasicMachine_GT_Recipe( + this.mName, + this.mTier, + this.mDescriptionArray, + this.mRecipes, + this.mInputSlotCount, + this.mOutputItems == null ? 0 : this.mOutputItems.length, + this.mTankCapacity, + this.mAmperage, + this.mTextures, + this.mSoundResourceLocation, + this.mSpecialEffect).setProgressBarTexture(this.progressBarTexture) + .setRecipeCatalystPriority(this.recipeCatalystPriority); + } + + public GT_MetaTileEntity_BasicMachine_GT_Recipe setProgressBarTexture(FallbackableUITexture progressBarTexture) { + this.progressBarTexture = progressBarTexture; + return this; + } + + public GT_MetaTileEntity_BasicMachine_GT_Recipe setProgressBarTextureName(String name, UITexture fallback) { + return setProgressBarTexture(GT_UITextures.fallbackableProgressbar(name, fallback)); + } + + public GT_MetaTileEntity_BasicMachine_GT_Recipe setProgressBarTextureName(String name) { + return setProgressBarTextureName(name, GT_UITextures.PROGRESSBAR_ARROW); + } + + @Override + protected boolean allowPutStackValidated(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side, + ItemStack aStack) { + if (!super.allowPutStackValidated(aBaseMetaTileEntity, aIndex, side, aStack)) return false; + switch (this.mInputSlotCount) { + case 0 -> { + return false; + } + case 1 -> { + if (this.getFillableStack() == null) return this.getRecipeMap() + .containsInput(aStack); + else return this.getRecipeMap() + .findRecipe( + this.getBaseMetaTileEntity(), + this.mLastRecipe, + true, + true, + V[this.mTier], + new FluidStack[] { this.getFillableStack() }, + this.getSpecialSlot(), + appendSelectedCircuit(aStack)) + != null; + } + case 2 -> { + return ((this.getInputAt(0) != null && this.getInputAt(1) != null) + || (this.getInputAt(0) == null && this.getInputAt(1) == null ? this.getRecipeMap() + .containsInput(aStack) + : (this.getRecipeMap() + .containsInput(aStack) + && this.getRecipeMap() + .findRecipe( + this.getBaseMetaTileEntity(), + this.mLastRecipe, + true, + true, + V[this.mTier], + new FluidStack[] { this.getFillableStack() }, + this.getSpecialSlot(), + aIndex == this.getInputSlot() ? appendSelectedCircuit(aStack, this.getInputAt(1)) + : appendSelectedCircuit(this.getInputAt(0), aStack)) + != null))); + } + default -> { + int tID = this.getBaseMetaTileEntity() + .getMetaTileID(); + if (tID >= 211 && tID <= 218 || tID >= 1180 && tID <= 1187 || tID >= 10780 && tID <= 10786) { // assembler + // lv-iv; + // circuit + // asseblers + // lv - + // uv; + // assemblers + // luv-uev + if (GT_Utility.isStackValid(aStack)) for (int oreID : OreDictionary.getOreIDs(aStack)) { + if (OreDictionary.getOreName(oreID) + .startsWith("circuit")) return true; + } + } + return this.getRecipeMap() + .containsInput(aStack); + } + } + } + + @Override + public boolean allowSelectCircuit() { + return true; + } + + @Override + public void onPreTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) { + super.onPreTick(aBaseMetaTileEntity, aTick); + if (aBaseMetaTileEntity.isClientSide() && aBaseMetaTileEntity.isActive()) { + // noinspection SwitchStatementWithTooFewBranches + switch (this.mSpecialEffect) { + case TOP_SMOKE -> { + if (aBaseMetaTileEntity.getFrontFacing() != UP && aBaseMetaTileEntity.getCoverIDAtSide(UP) == 0 + && !aBaseMetaTileEntity.getOpacityAtSide(UP)) { + + new ParticleEventBuilder().setMotion(0.0D, 0.0D, 0.0D) + .setIdentifier(ParticleFX.SMOKE) + .setPosition( + aBaseMetaTileEntity.getXCoord() + 0.8F - XSTR_INSTANCE.nextFloat() * 0.6F, + aBaseMetaTileEntity.getYCoord() + 0.9F + XSTR_INSTANCE.nextFloat() * 0.2F, + aBaseMetaTileEntity.getZCoord() + 0.8F - XSTR_INSTANCE.nextFloat() * 0.6F) + .setWorld(aBaseMetaTileEntity.getWorld()) + .run(); + } + } + default -> {} + } + } + } + + /** + * Handles {@link Block#randomDisplayTick} driven Special Effects + * + * @param aBaseMetaTileEntity The entity that will handle the {@see Block#randomDisplayTick} + */ + @SideOnly(Side.CLIENT) + @Override + public void onRandomDisplayTick(IGregTechTileEntity aBaseMetaTileEntity) { + + // noinspection SwitchStatementWithTooFewBranches + switch (this.mSpecialEffect) { + case MAIN_RANDOM_SPARKS -> { + // Random Sparkles at main face + if (aBaseMetaTileEntity.isActive() && XSTR_INSTANCE.nextInt(3) == 0) { + + final ForgeDirection mainFacing = this.mMainFacing; + + if ((mainFacing.flag & (ForgeDirection.UP.flag | ForgeDirection.DOWN.flag)) == 0 + && aBaseMetaTileEntity.getCoverIDAtSide(mainFacing) == 0 + && !aBaseMetaTileEntity.getOpacityAtSide(mainFacing)) { + + final double oX = aBaseMetaTileEntity.getXCoord(); + final double oY = aBaseMetaTileEntity.getYCoord(); + final double oZ = aBaseMetaTileEntity.getZCoord(); + final double offset = 0.02D; + final double horizontal = 0.5D + XSTR_INSTANCE.nextFloat() * 8D / 16D - 4D / 16D; + + final double x, y, z, mX, mZ; + + y = oY + XSTR_INSTANCE.nextFloat() * 10D / 16D + 5D / 16D; + + if (mainFacing == ForgeDirection.WEST) { + x = oX - offset; + mX = -.05D; + z = oZ + horizontal; + mZ = 0D; + } else if (mainFacing == ForgeDirection.EAST) { + x = oX + offset; + mX = .05D; + z = oZ + horizontal; + mZ = 0D; + } else if (mainFacing == ForgeDirection.NORTH) { + x = oX + horizontal; + mX = 0D; + z = oZ - offset; + mZ = -.05D; + } else // if (frontFacing == ForgeDirection.SOUTH.ordinal()) + { + x = oX + horizontal; + mX = 0D; + z = oZ + offset; + mZ = .05D; + } + + ParticleEventBuilder particleEventBuilder = (new ParticleEventBuilder()).setMotion(mX, 0, mZ) + .setPosition(x, y, z) + .setWorld(getBaseMetaTileEntity().getWorld()); + particleEventBuilder.setIdentifier(ParticleFX.LAVA) + .run(); + } + } + } + default -> {} + } + } + + @Override + public RecipeMap<?> getRecipeMap() { + return this.mRecipes; + } + + @Override + public int getRecipeCatalystPriority() { + return recipeCatalystPriority; + } + + public GT_MetaTileEntity_BasicMachine_GT_Recipe setRecipeCatalystPriority(int recipeCatalystPriority) { + this.recipeCatalystPriority = recipeCatalystPriority; + return this; + } + + @Override + public int getCapacity() { + return this.mTankCapacity; + } + + @Override + public void startSoundLoop(byte aIndex, double aX, double aY, double aZ) { + super.startSoundLoop(aIndex, aX, aY, aZ); + if (aIndex == 1 && this.mSoundResourceLocation != null + && GT_Utility.isStringValid(this.mSoundResourceLocation.getResourceDomain()) + && GT_Utility.isStringValid(this.mSoundResourceLocation.getResourcePath())) + GT_Utility.doSoundAtClient(this.mSoundResourceLocation, 100, 1.0F, aX, aY, aZ); + } + + @Override + public void startProcess() { + BaseMetaTileEntity myMetaTileEntity = ((BaseMetaTileEntity) this.getBaseMetaTileEntity()); + // Added to throttle sounds. To reduce lag, this is on the server side so BlockUpdate packets aren't sent. + if (myMetaTileEntity.mTickTimer > (myMetaTileEntity.mLastSoundTick + ticksBetweenSounds)) { + if (this.mSoundResourceLocation != null + && GT_Utility.isStringValid(this.mSoundResourceLocation.getResourceDomain()) + && GT_Utility.isStringValid(this.mSoundResourceLocation.getResourcePath())) + this.sendLoopStart((byte) 1); + // Does not have overflow protection, but they are longs. + myMetaTileEntity.mLastSoundTick = myMetaTileEntity.mTickTimer; + } + } + + @Override + protected BasicUIProperties getUIProperties() { + return super.getUIProperties().toBuilder() + .progressBarTexture(progressBarTexture) + .build(); + } + + public enum X { + PUMP, + WIRE, + WIRE4, + HULL, + PIPE, + GLASS, + PLATE, + MOTOR, + ROTOR, + SENSOR, + PISTON, + CIRCUIT, + EMITTER, + CONVEYOR, + ROBOT_ARM, + COIL_HEATING, + COIL_ELECTRIC, + STICK_MAGNETIC, + STICK_DISTILLATION, + BETTER_CIRCUIT, + FIELD_GENERATOR, + COIL_HEATING_DOUBLE, + STICK_ELECTROMAGNETIC + } + + /** + * Special Effects + */ + public enum SpecialEffects { + + NONE, + TOP_SMOKE, + MAIN_RANDOM_SPARKS; + + static final SpecialEffects[] VALID_SPECIAL_EFFECTS = { NONE, TOP_SMOKE, MAIN_RANDOM_SPARKS }; + + static SpecialEffects fromId(int id) { + return id >= 0 && id < VALID_SPECIAL_EFFECTS.length ? VALID_SPECIAL_EFFECTS[id] : NONE; + } + } +} diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicMachine_Steel.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicMachine_Steel.java new file mode 100644 index 0000000000..d6ae385430 --- /dev/null +++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicMachine_Steel.java @@ -0,0 +1,150 @@ +package gregtech.api.metatileentity.implementations; + +import static gregtech.api.enums.Textures.BlockIcons.MACHINE_STEELBRICKS_BOTTOM; +import static gregtech.api.enums.Textures.BlockIcons.MACHINE_STEELBRICKS_SIDE; +import static gregtech.api.enums.Textures.BlockIcons.MACHINE_STEELBRICKS_TOP; +import static gregtech.api.enums.Textures.BlockIcons.MACHINE_STEEL_BOTTOM; +import static gregtech.api.enums.Textures.BlockIcons.MACHINE_STEEL_SIDE; +import static gregtech.api.enums.Textures.BlockIcons.MACHINE_STEEL_TOP; +import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_PIPE_OUT; + +import gregtech.api.enums.Dyes; +import gregtech.api.enums.SteamVariant; +import gregtech.api.interfaces.ITexture; +import gregtech.api.interfaces.modularui.IGetTitleColor; +import gregtech.api.objects.overclockdescriber.OverclockDescriber; +import gregtech.api.objects.overclockdescriber.SteamOverclockDescriber; +import gregtech.api.render.TextureFactory; + +/** + * NEVER INCLUDE THIS FILE IN YOUR MOD!!! + * <p/> + * This is the main construct for my Basic Machines such as the Automatic Extractor Extend this class to make a simple + * Machine + */ +public abstract class GT_MetaTileEntity_BasicMachine_Steel extends GT_MetaTileEntity_BasicMachine_Bronze + implements IGetTitleColor { + + public GT_MetaTileEntity_BasicMachine_Steel(int aID, String aName, String aNameRegional, String aDescription, + int aInputSlotCount, int aOutputSlotCount, boolean aHighPressure) { + super(aID, aName, aNameRegional, aDescription, aInputSlotCount, aOutputSlotCount, aHighPressure); + } + + public GT_MetaTileEntity_BasicMachine_Steel(String aName, String[] aDescription, ITexture[][][] aTextures, + int aInputSlotCount, int aOutputSlotCount, boolean aHighPressure) { + super(aName, aDescription, aTextures, aInputSlotCount, aOutputSlotCount, aHighPressure); + } + + @Override + public OverclockDescriber createOverclockDescriber() { + return new SteamOverclockDescriber(SteamVariant.STEEL, 2, 1); + } + + @Override + public ITexture[] getSideFacingActive(byte aColor) { + return new ITexture[] { TextureFactory.of( + isBricked() ? MACHINE_STEELBRICKS_SIDE : MACHINE_STEEL_SIDE, + Dyes.getModulation(aColor, Dyes._NULL.mRGBa)) }; + } + + @Override + public ITexture[] getSideFacingInactive(byte aColor) { + return new ITexture[] { TextureFactory.of( + isBricked() ? MACHINE_STEELBRICKS_SIDE : MACHINE_STEEL_SIDE, + Dyes.getModulation(aColor, Dyes._NULL.mRGBa)) }; + } + + @Override + public ITexture[] getFrontFacingActive(byte aColor) { + return new ITexture[] { TextureFactory.of( + isBricked() ? MACHINE_STEELBRICKS_SIDE : MACHINE_STEEL_SIDE, + Dyes.getModulation(aColor, Dyes._NULL.mRGBa)) }; + } + + @Override + public ITexture[] getFrontFacingInactive(byte aColor) { + return new ITexture[] { TextureFactory.of( + isBricked() ? MACHINE_STEELBRICKS_SIDE : MACHINE_STEEL_SIDE, + Dyes.getModulation(aColor, Dyes._NULL.mRGBa)) }; + } + + @Override + public ITexture[] getTopFacingActive(byte aColor) { + return new ITexture[] { TextureFactory.of( + isBricked() ? MACHINE_STEELBRICKS_TOP : MACHINE_STEEL_TOP, + Dyes.getModulation(aColor, Dyes._NULL.mRGBa)) }; + } + + @Override + public ITexture[] getTopFacingInactive(byte aColor) { + return new ITexture[] { TextureFactory.of( + isBricked() ? MACHINE_STEELBRICKS_TOP : MACHINE_STEEL_TOP, + Dyes.getModulation(aColor, Dyes._NULL.mRGBa)) }; + } + + @Override + public ITexture[] getBottomFacingActive(byte aColor) { + return new ITexture[] { TextureFactory.of( + isBricked() ? MACHINE_STEELBRICKS_BOTTOM : MACHINE_STEEL_BOTTOM, + Dyes.getModulation(aColor, Dyes._NULL.mRGBa)) }; + } + + @Override + public ITexture[] getBottomFacingInactive(byte aColor) { + return new ITexture[] { TextureFactory.of( + isBricked() ? MACHINE_STEELBRICKS_BOTTOM : MACHINE_STEEL_BOTTOM, + Dyes.getModulation(aColor, Dyes._NULL.mRGBa)) }; + } + + @Override + public ITexture[] getBottomFacingPipeActive(byte aColor) { + return new ITexture[] { TextureFactory.of( + isBricked() ? MACHINE_STEELBRICKS_BOTTOM : MACHINE_STEEL_BOTTOM, + Dyes.getModulation(aColor, Dyes._NULL.mRGBa)), TextureFactory.of(OVERLAY_PIPE_OUT) }; + } + + @Override + public ITexture[] getBottomFacingPipeInactive(byte aColor) { + return new ITexture[] { TextureFactory.of( + isBricked() ? MACHINE_STEELBRICKS_BOTTOM : MACHINE_STEEL_BOTTOM, + Dyes.getModulation(aColor, Dyes._NULL.mRGBa)), TextureFactory.of(OVERLAY_PIPE_OUT) }; + } + + @Override + public ITexture[] getTopFacingPipeActive(byte aColor) { + return new ITexture[] { TextureFactory.of( + isBricked() ? MACHINE_STEELBRICKS_TOP : MACHINE_STEEL_TOP, + Dyes.getModulation(aColor, Dyes._NULL.mRGBa)), TextureFactory.of(OVERLAY_PIPE_OUT) }; + } + + @Override + public ITexture[] getTopFacingPipeInactive(byte aColor) { + return new ITexture[] { TextureFactory.of( + isBricked() ? MACHINE_STEELBRICKS_TOP : MACHINE_STEEL_TOP, + Dyes.getModulation(aColor, Dyes._NULL.mRGBa)), TextureFactory.of(OVERLAY_PIPE_OUT) }; + } + + @Override + public ITexture[] getSideFacingPipeActive(byte aColor) { + return new ITexture[] { TextureFactory.of( + isBricked() ? MACHINE_STEELBRICKS_SIDE : MACHINE_STEEL_SIDE, + Dyes.getModulation(aColor, Dyes._NULL.mRGBa)), TextureFactory.of(OVERLAY_PIPE_OUT) }; + } + + @Override + public ITexture[] getSideFacingPipeInactive(byte aColor) { + return new ITexture[] { TextureFactory.of( + isBricked() ? MACHINE_STEELBRICKS_SIDE : MACHINE_STEEL_SIDE, + Dyes.getModulation(aColor, Dyes._NULL.mRGBa)), TextureFactory.of(OVERLAY_PIPE_OUT) }; + } + + @Override + public SteamVariant getSteamVariant() { + return SteamVariant.STEEL; + } + + @Override + public int getTitleColor() { + return COLOR_TITLE_WHITE.get(); + } +} diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicTank.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicTank.java new file mode 100644 index 0000000000..c810614161 --- /dev/null +++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicTank.java @@ -0,0 +1,348 @@ +package gregtech.api.metatileentity.implementations; + +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraftforge.common.util.ForgeDirection; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.FluidTankInfo; + +import com.gtnewhorizons.modularui.api.NumberFormatMUI; +import com.gtnewhorizons.modularui.api.screen.ModularWindow; +import com.gtnewhorizons.modularui.api.screen.UIBuildContext; +import com.gtnewhorizons.modularui.common.fluid.FluidStackTank; +import com.gtnewhorizons.modularui.common.widget.DrawableWidget; +import com.gtnewhorizons.modularui.common.widget.FluidSlotWidget; +import com.gtnewhorizons.modularui.common.widget.SlotWidget; +import com.gtnewhorizons.modularui.common.widget.TextWidget; + +import gregtech.api.gui.GT_Container_BasicTank; +import gregtech.api.gui.GT_GUIContainer_BasicTank; +import gregtech.api.gui.modularui.GT_UITextures; +import gregtech.api.interfaces.ITexture; +import gregtech.api.interfaces.modularui.IAddUIWidgets; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.util.GT_Utility; + +/** + * NEVER INCLUDE THIS FILE IN YOUR MOD!!! + * <p> + * This is the main construct for my generic Tanks. Filling and emptying behavior have to be implemented manually + */ +public abstract class GT_MetaTileEntity_BasicTank extends GT_MetaTileEntity_TieredMachineBlock + implements IAddUIWidgets { + + public FluidStack mFluid; + // Due to class initializing order, getCapacity might not work properly at this time. + // So we pass supplier instead of current value here. + protected final FluidStackTank fluidTank = new FluidStackTank( + () -> mFluid, + fluidStack -> mFluid = fluidStack, + this::getRealCapacity); + + /** + * @param aInvSlotCount should be 3 + */ + public GT_MetaTileEntity_BasicTank(int aID, String aName, String aNameRegional, int aTier, int aInvSlotCount, + String aDescription, ITexture... aTextures) { + super(aID, aName, aNameRegional, aTier, aInvSlotCount, aDescription, aTextures); + } + + public GT_MetaTileEntity_BasicTank(int aID, String aName, String aNameRegional, int aTier, int aInvSlotCount, + String[] aDescription, ITexture... aTextures) { + super(aID, aName, aNameRegional, aTier, aInvSlotCount, aDescription, aTextures); + } + + public GT_MetaTileEntity_BasicTank(String aName, int aTier, int aInvSlotCount, String aDescription, + ITexture[][][] aTextures) { + super(aName, aTier, aInvSlotCount, aDescription, aTextures); + } + + public GT_MetaTileEntity_BasicTank(String aName, int aTier, int aInvSlotCount, String[] aDescription, + ITexture[][][] aTextures) { + super(aName, aTier, aInvSlotCount, aDescription, aTextures); + } + + @Override + public boolean isSimpleMachine() { + return false; + } + + @Override + public boolean isValidSlot(int aIndex) { + return aIndex != getStackDisplaySlot(); + } + + @Override + public void saveNBTData(NBTTagCompound aNBT) { + if (mFluid != null) aNBT.setTag("mFluid", mFluid.writeToNBT(new NBTTagCompound())); + } + + @Override + public void loadNBTData(NBTTagCompound aNBT) { + mFluid = FluidStack.loadFluidStackFromNBT(aNBT.getCompoundTag("mFluid")); + } + + public abstract boolean doesFillContainers(); + + public abstract boolean doesEmptyContainers(); + + public abstract boolean canTankBeFilled(); + + public abstract boolean canTankBeEmptied(); + + public abstract boolean displaysItemStack(); + + /** + * @return If fluid amount is shown on FluidDisplayItem + */ + public abstract boolean displaysStackSize(); + + public int getInputSlot() { + return 0; + } + + public int getOutputSlot() { + return 1; + } + + public int getStackDisplaySlot() { + return 2; + } + + public boolean isFluidInputAllowed(FluidStack aFluid) { + return true; + } + + public boolean isFluidChangingAllowed() { + return true; + } + + public FluidStack getFillableStack() { + return mFluid; + } + + public FluidStack setFillableStack(FluidStack aFluid) { + mFluid = aFluid; + return mFluid; + } + + /** + * If you override this and change the field returned, be sure to override {@link #isDrainableStackSeparate()} as + * well! + */ + public FluidStack getDrainableStack() { + return mFluid; + } + + public FluidStack setDrainableStack(FluidStack aFluid) { + mFluid = aFluid; + return mFluid; + } + + public boolean isDrainableStackSeparate() { + return false; + } + + @Deprecated + public FluidStack getDisplayedFluid() { + return getDrainableStack(); + } + + @Deprecated + @Override + public Object getServerGUI(int aID, InventoryPlayer aPlayerInventory, IGregTechTileEntity aBaseMetaTileEntity) { + return new GT_Container_BasicTank(aPlayerInventory, aBaseMetaTileEntity); + } + + @Deprecated + @Override + public Object getClientGUI(int aID, InventoryPlayer aPlayerInventory, IGregTechTileEntity aBaseMetaTileEntity) { + return new GT_GUIContainer_BasicTank(aPlayerInventory, aBaseMetaTileEntity, getLocalName()); + } + + @Override + public void onPreTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) { + if (aBaseMetaTileEntity.isServerSide()) { + if (isFluidChangingAllowed() && getFillableStack() != null && getFillableStack().amount <= 0) + setFillableStack(null); + + if (doesEmptyContainers()) { + FluidStack tFluid = GT_Utility.getFluidForFilledItem(mInventory[getInputSlot()], true); + if (tFluid != null && isFluidInputAllowed(tFluid)) { + if (getFillableStack() == null) { + if (isFluidInputAllowed(tFluid) && tFluid.amount <= getCapacity()) { + if (aBaseMetaTileEntity.addStackToSlot( + getOutputSlot(), + GT_Utility.getContainerForFilledItem(mInventory[getInputSlot()], true), + 1)) { + setFillableStack(tFluid.copy()); + this.onEmptyingContainerWhenEmpty(); + aBaseMetaTileEntity.decrStackSize(getInputSlot(), 1); + } + } + } else { + if (tFluid.isFluidEqual(getFillableStack()) + && ((long) tFluid.amount + getFillableStack().amount) <= (long) getCapacity()) { + if (aBaseMetaTileEntity.addStackToSlot( + getOutputSlot(), + GT_Utility.getContainerForFilledItem(mInventory[getInputSlot()], true), + 1)) { + getFillableStack().amount += tFluid.amount; + aBaseMetaTileEntity.decrStackSize(getInputSlot(), 1); + } + } + } + } + } + + if (doesFillContainers()) { + ItemStack tOutput = GT_Utility + .fillFluidContainer(getDrainableStack(), mInventory[getInputSlot()], false, true); + if (tOutput != null && aBaseMetaTileEntity.addStackToSlot(getOutputSlot(), tOutput, 1)) { + FluidStack tFluid = GT_Utility.getFluidForFilledItem(tOutput, true); + aBaseMetaTileEntity.decrStackSize(getInputSlot(), 1); + if (tFluid != null) getDrainableStack().amount -= tFluid.amount; + if (getDrainableStack().amount <= 0 && isFluidChangingAllowed()) setDrainableStack(null); + } + } + } + } + + @Override + public FluidStack getFluid() { + return getDrainableStack(); + } + + @Override + public int getFluidAmount() { + return getDrainableStack() != null ? getDrainableStack().amount : 0; + } + + @Override + public int fill(FluidStack aFluid, boolean doFill) { + if (aFluid == null || aFluid.getFluid() + .getID() <= 0 || aFluid.amount <= 0 || !canTankBeFilled() || !isFluidInputAllowed(aFluid)) return 0; + + if (getFillableStack() == null || getFillableStack().getFluid() + .getID() <= 0) { + if (aFluid.amount <= getCapacity()) { + if (doFill) { + setFillableStack(aFluid.copy()); + getBaseMetaTileEntity().markDirty(); + } + return aFluid.amount; + } + if (doFill) { + setFillableStack(aFluid.copy()); + getFillableStack().amount = getCapacity(); + getBaseMetaTileEntity().markDirty(); + } + return getCapacity(); + } + + if (!getFillableStack().isFluidEqual(aFluid)) return 0; + + int space = getCapacity() - getFillableStack().amount; + if (aFluid.amount <= space) { + if (doFill) { + getFillableStack().amount += aFluid.amount; + getBaseMetaTileEntity().markDirty(); + } + return aFluid.amount; + } + if (doFill) getFillableStack().amount = getCapacity(); + return space; + } + + @Override + public FluidStack drain(int maxDrain, boolean doDrain) { + if (getDrainableStack() == null || !canTankBeEmptied()) return null; + if (getDrainableStack().amount <= 0 && isFluidChangingAllowed()) { + setDrainableStack(null); + getBaseMetaTileEntity().markDirty(); + return null; + } + + int used = maxDrain; + if (getDrainableStack().amount < used) used = getDrainableStack().amount; + + if (doDrain) { + getDrainableStack().amount -= used; + getBaseMetaTileEntity().markDirty(); + } + + FluidStack drained = getDrainableStack().copy(); + drained.amount = used; + + if (getDrainableStack().amount <= 0 && isFluidChangingAllowed()) { + setDrainableStack(null); + getBaseMetaTileEntity().markDirty(); + } + + return drained; + } + + @Override + public FluidTankInfo[] getTankInfo(ForgeDirection side) { + if (getCapacity() <= 0 && !getBaseMetaTileEntity().hasSteamEngineUpgrade()) return new FluidTankInfo[] {}; + if (isDrainableStackSeparate()) { + return new FluidTankInfo[] { new FluidTankInfo(getFillableStack(), getCapacity()), + new FluidTankInfo(getDrainableStack(), getCapacity()) }; + } else { + return new FluidTankInfo[] { new FluidTankInfo(this) }; + } + } + + @Override + public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side, + ItemStack aStack) { + return aIndex == getOutputSlot(); + } + + @Override + public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side, + ItemStack aStack) { + return aIndex == getInputSlot(); + } + + protected void onEmptyingContainerWhenEmpty() { + // Do nothing + } + + protected static final NumberFormatMUI numberFormat = new NumberFormatMUI(); + + @Override + public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) { + builder.widget( + new DrawableWidget().setDrawable(GT_UITextures.PICTURE_SCREEN_BLACK) + .setPos(7, 16) + .setSize(71, 45)) + .widget( + new DrawableWidget().setDrawable(GT_UITextures.PICTURE_GAUGE) + .setPos(79, 34) + .setSize(18, 18)) + .widget( + new SlotWidget(inventoryHandler, getInputSlot()) + .setBackground(getGUITextureSet().getItemSlot(), GT_UITextures.OVERLAY_SLOT_IN) + .setPos(79, 16)) + .widget( + new SlotWidget(inventoryHandler, getOutputSlot()).setAccess(true, false) + .setBackground(getGUITextureSet().getItemSlot(), GT_UITextures.OVERLAY_SLOT_OUT) + .setPos(79, 52)) + .widget( + createFluidSlot().setBackground(GT_UITextures.TRANSPARENT) + .setPos(58, 41)) + .widget( + new TextWidget("Liquid Amount").setDefaultColor(COLOR_TEXT_WHITE.get()) + .setPos(10, 20)) + .widget( + new TextWidget().setStringSupplier(() -> numberFormat.format(mFluid != null ? mFluid.amount : 0)) + .setDefaultColor(COLOR_TEXT_WHITE.get()) + .setPos(10, 30)); + } + + protected FluidSlotWidget createFluidSlot() { + return new FluidSlotWidget(fluidTank); + } +} diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Buffer.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Buffer.java new file mode 100644 index 0000000000..1fc347da1f --- /dev/null +++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Buffer.java @@ -0,0 +1,568 @@ +package gregtech.api.metatileentity.implementations; + +import static gregtech.api.enums.GT_Values.V; +import static gregtech.api.enums.Textures.BlockIcons.ARROW_DOWN; +import static gregtech.api.enums.Textures.BlockIcons.ARROW_DOWN_GLOW; +import static gregtech.api.enums.Textures.BlockIcons.ARROW_LEFT; +import static gregtech.api.enums.Textures.BlockIcons.ARROW_LEFT_GLOW; +import static gregtech.api.enums.Textures.BlockIcons.ARROW_RIGHT; +import static gregtech.api.enums.Textures.BlockIcons.ARROW_RIGHT_GLOW; +import static gregtech.api.enums.Textures.BlockIcons.ARROW_UP; +import static gregtech.api.enums.Textures.BlockIcons.ARROW_UP_GLOW; +import static gregtech.api.enums.Textures.BlockIcons.MACHINE_CASINGS; +import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_PIPE_OUT; +import static gregtech.api.metatileentity.BaseTileEntity.TOOLTIP_DELAY; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Supplier; +import java.util.stream.IntStream; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.StatCollector; +import net.minecraftforge.common.util.ForgeDirection; + +import com.gtnewhorizons.modularui.api.drawable.UITexture; +import com.gtnewhorizons.modularui.api.screen.ModularWindow; +import com.gtnewhorizons.modularui.api.screen.UIBuildContext; +import com.gtnewhorizons.modularui.api.widget.Widget; +import com.gtnewhorizons.modularui.common.widget.CycleButtonWidget; +import com.gtnewhorizons.modularui.common.widget.SlotGroup; + +import gregtech.api.gui.modularui.GT_UIInfos; +import gregtech.api.gui.modularui.GT_UITextures; +import gregtech.api.interfaces.ITexture; +import gregtech.api.interfaces.modularui.IAddUIWidgets; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.render.TextureFactory; +import gregtech.api.util.GT_TooltipDataCache; +import gregtech.api.util.GT_Utility; + +public abstract class GT_MetaTileEntity_Buffer extends GT_MetaTileEntity_TieredMachineBlock implements IAddUIWidgets { + + private static final int OUTPUT_INDEX = 0; + private static final int ARROW_RIGHT_INDEX = 1; + private static final int ARROW_DOWN_INDEX = 2; + private static final int ARROW_LEFT_INDEX = 3; + private static final int ARROW_UP_INDEX = 4; + private static final int FRONT_INDEX = 5; + + private static final String EMIT_ENERGY_TOOLTIP = "GT5U.machines.emit_energy.tooltip"; + private static final String EMIT_REDSTONE_IF_FULL_TOOLTIP = "GT5U.machines.emit_redstone_if_full.tooltip"; + private static final String INVERT_REDSTONE_TOOLTIP = "GT5U.machines.invert_redstone.tooltip"; + private static final String STOCKING_MODE_TOOLTIP = "GT5U.machines.buffer_stocking_mode.tooltip"; + private static final String SORTING_MODE_TOOLTIP = "GT5U.machines.sorting_mode.tooltip"; + private static final int BUTTON_SIZE = 18; + + public int mMaxStackSize = 64; + public static int MAX = 8; + public boolean bOutput = false, bRedstoneIfFull = false, bInvert = false, bStockingMode = false, + bSortStacks = false; + public int mSuccess = 0, mTargetStackSize = 0; + private int uiButtonCount = 0; + + public GT_MetaTileEntity_Buffer(int aID, String aName, String aNameRegional, int aTier, int aInvSlotCount, + String aDescription) { + super(aID, aName, aNameRegional, aTier, aInvSlotCount, aDescription); + } + + public GT_MetaTileEntity_Buffer(int aID, String aName, String aNameRegional, int aTier, int aInvSlotCount, + String[] aDescription) { + super(aID, aName, aNameRegional, aTier, aInvSlotCount, aDescription); + } + + public GT_MetaTileEntity_Buffer(String aName, int aTier, int aInvSlotCount, String aDescription, + ITexture[][][] aTextures) { + super(aName, aTier, aInvSlotCount, aDescription, aTextures); + } + + public GT_MetaTileEntity_Buffer(String aName, int aTier, int aInvSlotCount, String[] aDescription, + ITexture[][][] aTextures) { + super(aName, aTier, aInvSlotCount, aDescription, aTextures); + } + + @Override + public ITexture[][][] getTextureSet(ITexture[] aTextures) { + ITexture[][][] rTextures = new ITexture[ForgeDirection.VALID_DIRECTIONS.length][17][]; + ITexture tIcon = getOverlayIcon(); + ITexture tOut = TextureFactory.of(OVERLAY_PIPE_OUT); + ITexture tUp = TextureFactory.of( + TextureFactory.of(ARROW_UP), + TextureFactory.builder() + .addIcon(ARROW_UP_GLOW) + .glow() + .build()); + ITexture tDown = TextureFactory.of( + TextureFactory.of(ARROW_DOWN), + TextureFactory.builder() + .addIcon(ARROW_DOWN_GLOW) + .glow() + .build()); + ITexture tLeft = TextureFactory.of( + TextureFactory.of(ARROW_LEFT), + TextureFactory.builder() + .addIcon(ARROW_LEFT_GLOW) + .glow() + .build()); + ITexture tRight = TextureFactory.of( + TextureFactory.of(ARROW_RIGHT), + TextureFactory.builder() + .addIcon(ARROW_RIGHT_GLOW) + .glow() + .build()); + for (int i = 0; i < rTextures[0].length; i++) { + rTextures[OUTPUT_INDEX][i] = new ITexture[] { MACHINE_CASINGS[mTier][i], tOut }; + rTextures[ARROW_RIGHT_INDEX][i] = new ITexture[] { MACHINE_CASINGS[mTier][i], tRight, tIcon }; + rTextures[ARROW_DOWN_INDEX][i] = new ITexture[] { MACHINE_CASINGS[mTier][i], tDown, tIcon }; + rTextures[ARROW_LEFT_INDEX][i] = new ITexture[] { MACHINE_CASINGS[mTier][i], tLeft, tIcon }; + rTextures[ARROW_UP_INDEX][i] = new ITexture[] { MACHINE_CASINGS[mTier][i], tUp, tIcon }; + rTextures[FRONT_INDEX][i] = new ITexture[] { MACHINE_CASINGS[mTier][i], tIcon }; + } + return rTextures; + } + + @Override + public ITexture[] getTexture(IGregTechTileEntity baseMetaTileEntity, ForgeDirection sideDirection, + ForgeDirection facingDirection, int colorIndex, boolean active, boolean redstoneLevel) { + colorIndex = colorIndex + 1; + if (sideDirection == facingDirection) return mTextures[FRONT_INDEX][colorIndex]; + if (sideDirection.getOpposite() == facingDirection) return mTextures[OUTPUT_INDEX][colorIndex]; + switch (facingDirection) { + case DOWN -> { + return mTextures[ARROW_UP_INDEX][colorIndex]; // ARROW_UP + } + case UP -> { + return mTextures[ARROW_DOWN_INDEX][colorIndex]; // ARROW_DOWN + } + case NORTH -> { + switch (sideDirection) { + case DOWN, UP -> { + return mTextures[ARROW_DOWN_INDEX][colorIndex]; // ARROW_DOWN + } + case WEST -> { + return mTextures[ARROW_RIGHT_INDEX][colorIndex]; // ARROW_RIGHT + } + case EAST -> { + return mTextures[ARROW_LEFT_INDEX][colorIndex]; // ARROW_LEFT + } + default -> {} + } + } + case SOUTH -> { + switch (sideDirection) { + case DOWN, UP -> { + return mTextures[ARROW_UP_INDEX][colorIndex]; // ARROW_UP + } + case WEST -> { + return mTextures[ARROW_LEFT_INDEX][colorIndex]; // ARROW_LEFT + } + case EAST -> { + return mTextures[ARROW_RIGHT_INDEX][colorIndex]; // ARROW_RIGHT + } + default -> {} + } + } + case WEST -> { + switch (sideDirection) { + case UP, SOUTH -> { + return mTextures[ARROW_RIGHT_INDEX][colorIndex]; // ARROW_RIGHT + } + case DOWN, NORTH -> { + return mTextures[ARROW_LEFT_INDEX][colorIndex]; // ARROW_LEFT + } + default -> {} + } + } + case EAST -> { + switch (sideDirection) { + case UP, SOUTH -> { + return mTextures[ARROW_LEFT_INDEX][colorIndex]; // ARROW_LEFT + } + case DOWN, NORTH -> { + return mTextures[ARROW_RIGHT_INDEX][colorIndex]; // ARROW_RIGHT + } + default -> {} + } + } + default -> {} + } + return mTextures[FRONT_INDEX][colorIndex]; + } + + @Override + public boolean isSimpleMachine() { + return false; + } + + @Override + public boolean isValidSlot(int aIndex) { + return aIndex < mInventory.length - 1; + } + + @Override + public boolean isFacingValid(ForgeDirection facing) { + return true; + } + + @Override + public boolean isEnetInput() { + return true; + } + + @Override + public boolean isEnetOutput() { + return true; + } + + @Override + public boolean isInputFacing(ForgeDirection side) { + return !isOutputFacing(side); + } + + @Override + public boolean isOutputFacing(ForgeDirection side) { + return getBaseMetaTileEntity().getBackFacing() == side; + } + + @Override + public boolean isTeleporterCompatible() { + return false; + } + + @Override + public long getMinimumStoredEU() { + return 512L; + } + + @Override + public long maxEUStore() { + return 512L + V[mTier] * 50L; + } + + @Override + public long maxEUInput() { + return V[mTier]; + } + + @Override + public long maxEUOutput() { + // Return full value if we're an item and don't exist in the world for tooltip purposes + return getBaseMetaTileEntity().getWorld() == null || bOutput ? V[mTier] : 0L; + } + + @Override + public long maxAmperesIn() { + return 2; + } + + @Override + public long maxAmperesOut() { + return 2; + } + + @Override + public boolean isAccessAllowed(EntityPlayer aPlayer) { + return true; + } + + public abstract ITexture getOverlayIcon(); + + @Override + public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer) { + GT_UIInfos.openGTTileEntityUI(aBaseMetaTileEntity, aPlayer); + return true; + } + + @Override + public void saveNBTData(NBTTagCompound aNBT) { + aNBT.setBoolean("bInvert", bInvert); + aNBT.setBoolean("bOutput", bOutput); + aNBT.setBoolean("bRedstoneIfFull", bRedstoneIfFull); + aNBT.setBoolean("bStockingMode", bStockingMode); + aNBT.setInteger("mTargetStackSize", mTargetStackSize); + aNBT.setBoolean("bSortStacks", bSortStacks); + } + + @Override + public void loadNBTData(NBTTagCompound aNBT) { + bInvert = aNBT.getBoolean("bInvert"); + bOutput = aNBT.getBoolean("bOutput"); + bRedstoneIfFull = aNBT.getBoolean("bRedstoneIfFull"); + bSortStacks = aNBT.getBoolean("bSortStacks"); + bStockingMode = aNBT.getBoolean("bStockingMode"); + mTargetStackSize = aNBT.getInteger("mTargetStackSize"); + } + + @Override + public void setItemNBT(NBTTagCompound aNBT) { + super.setItemNBT(aNBT); + if (mTargetStackSize > 0) aNBT.setInteger("mTargetStackSize", mTargetStackSize); + } + + @Override + public void onScrewdriverRightClick(ForgeDirection side, EntityPlayer aPlayer, float aX, float aY, float aZ) { + if (side == getBaseMetaTileEntity().getBackFacing()) { + + mTargetStackSize = (byte) ((mTargetStackSize + (aPlayer.isSneaking() ? -1 : 1)) % 65); + if (mTargetStackSize < 0) { + mTargetStackSize = mMaxStackSize; + } + if (mTargetStackSize == 0) { + GT_Utility.sendChatToPlayer(aPlayer, GT_Utility.trans("098", "Do not regulate Item Stack Size")); + } else { + GT_Utility.sendChatToPlayer( + aPlayer, + GT_Utility.trans("099", "Regulate Item Stack Size to: ") + mTargetStackSize); + } + } + } + + @Override + public boolean onWrenchRightClick(ForgeDirection side, ForgeDirection wrenchingSide, EntityPlayer entityPlayer, + float aX, float aY, float aZ) { + wrenchingSide = wrenchingSide.getOpposite(); + if (getBaseMetaTileEntity().isValidFacing(wrenchingSide)) { + getBaseMetaTileEntity().setFrontFacing(wrenchingSide); + return true; + } + return false; + } + + protected void handleRedstoneOutput(IGregTechTileEntity aBaseMetaTileEntity) { + int redstoneOutput = getRedstoneOutput(); + Arrays.stream(ForgeDirection.VALID_DIRECTIONS) + .forEach(side -> aBaseMetaTileEntity.setInternalOutputRedstoneSignal(side, (byte) redstoneOutput)); + } + + protected int getRedstoneOutput() { + return (!bRedstoneIfFull || (bInvert ^ hasEmptySlots())) ? 0 : 15; + } + + private boolean hasEmptySlots() { + return IntStream.range(0, mInventory.length) + .anyMatch(i -> isValidSlot(i) && mInventory[i] == null); + } + + @Override + public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTimer) { + if (aBaseMetaTileEntity.isAllowedToWork() && aBaseMetaTileEntity.isServerSide() + && (aBaseMetaTileEntity.hasWorkJustBeenEnabled() || aBaseMetaTileEntity.hasInventoryBeenModified() + || aTimer % 200 == 0 + || mSuccess > 0)) { + mSuccess--; + updateSlots(); + moveItems(aBaseMetaTileEntity, aTimer); + handleRedstoneOutput(aBaseMetaTileEntity); + } + } + + @Override + public void onFirstTick(IGregTechTileEntity aBaseMetaTileEntity) { + for (ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) + aBaseMetaTileEntity.setInternalOutputRedstoneSignal(side, (byte) 0); + } + + protected void moveItems(IGregTechTileEntity aBaseMetaTileEntity, long aTimer) { + moveItems(aBaseMetaTileEntity, aTimer, 1); + } + + protected void moveItems(IGregTechTileEntity aBaseMetaTileEntity, long ignoredTimer, int stacks) { + int tCost; + if (bStockingMode) tCost = GT_Utility.moveMultipleItemStacks( + aBaseMetaTileEntity, + aBaseMetaTileEntity.getTileEntityAtSide(aBaseMetaTileEntity.getBackFacing()), + aBaseMetaTileEntity.getBackFacing(), + aBaseMetaTileEntity.getFrontFacing(), + null, + false, + mTargetStackSize == 0 ? 64 : (byte) mTargetStackSize, + mTargetStackSize == 0 ? 1 : (byte) mTargetStackSize, + (byte) 64, + (byte) 1, + stacks); + else tCost = GT_Utility.moveMultipleItemStacks( + aBaseMetaTileEntity, + aBaseMetaTileEntity.getTileEntityAtSide(aBaseMetaTileEntity.getBackFacing()), + aBaseMetaTileEntity.getBackFacing(), + aBaseMetaTileEntity.getFrontFacing(), + null, + false, + (byte) 64, + (byte) 1, + mTargetStackSize == 0 ? 64 : (byte) mTargetStackSize, + mTargetStackSize == 0 ? 1 : (byte) mTargetStackSize, + stacks); + + if (tCost > 0 || aBaseMetaTileEntity.hasInventoryBeenModified()) { + mSuccess = 50; + } + } + + @Override + public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side, + ItemStack aStack) { + return true; + } + + @Override + public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side, + ItemStack aStack) { + return side != aBaseMetaTileEntity.getBackFacing(); + } + + @Override + public boolean allowGeneralRedstoneOutput() { + return true; + } + + public void updateSlots() { + for (int i = 0; i < mInventory.length; i++) + if (mInventory[i] != null && mInventory[i].stackSize <= 0) mInventory[i] = null; + if (bSortStacks) fillStacksIntoFirstSlots(); + } + + protected void fillStacksIntoFirstSlots() { + HashMap<GT_Utility.ItemId, Integer> slots = new HashMap<>(mInventory.length); + HashMap<GT_Utility.ItemId, ItemStack> stacks = new HashMap<>(mInventory.length); + List<GT_Utility.ItemId> order = new ArrayList<>(mInventory.length); + List<Integer> validSlots = new ArrayList<>(mInventory.length); + for (int i = 0; i < mInventory.length - 1; i++) { + if (!isValidSlot(i)) continue; + validSlots.add(i); + ItemStack s = mInventory[i]; + if (s == null) continue; + GT_Utility.ItemId sID = GT_Utility.ItemId.createNoCopy(s); + slots.merge(sID, s.stackSize, Integer::sum); + if (!stacks.containsKey(sID)) stacks.put(sID, s); + order.add(sID); + mInventory[i] = null; + } + int slotindex = 0; + for (GT_Utility.ItemId sID : order) { + int toSet = slots.get(sID); + if (toSet == 0) continue; + int slot = validSlots.get(slotindex); + slotindex++; + mInventory[slot] = stacks.get(sID) + .copy(); + toSet = Math.min(toSet, mInventory[slot].getMaxStackSize()); + mInventory[slot].stackSize = toSet; + slots.merge(sID, toSet, (a, b) -> a - b); + } + } + + @Override + public boolean onSolderingToolRightClick(ForgeDirection side, ForgeDirection wrenchingSide, + EntityPlayer entityPlayer, float aX, float aY, float aZ) { + if (entityPlayer.isSneaking()) { + // I was so proud of all this but I literally just copied code from OutputBus + bSortStacks = !bSortStacks; + GT_Utility.sendChatToPlayer( + entityPlayer, + GT_Utility.trans("200", "Sort mode: ") + + (bSortStacks ? GT_Utility.trans("088", "Enabled") : GT_Utility.trans("087", "Disabled"))); + return true; + } + return super.onSolderingToolRightClick(side, wrenchingSide, entityPlayer, aX, aY, aZ); + } + + @Override + public boolean useModularUI() { + return true; + } + + protected void addEmitEnergyButton(ModularWindow.Builder builder) { + builder.widget( + createToggleButton( + () -> bOutput, + val -> bOutput = val, + GT_UITextures.OVERLAY_BUTTON_EMIT_ENERGY, + this::getEmitEnergyButtonTooltip)); + } + + private GT_TooltipDataCache.TooltipData getEmitEnergyButtonTooltip() { + return mTooltipCache.getData( + EMIT_ENERGY_TOOLTIP, + EnumChatFormatting.GREEN + GT_Utility.formatNumbers(V[mTier]) + + " (" + + GT_Utility.getColoredTierNameFromTier(mTier) + + EnumChatFormatting.GREEN + + ")" + + EnumChatFormatting.GRAY, + maxAmperesOut()); + } + + protected void addEmitRedstoneIfFullButton(ModularWindow.Builder builder) { + builder.widget( + createToggleButton( + () -> bRedstoneIfFull, + val -> bRedstoneIfFull = val, + GT_UITextures.OVERLAY_BUTTON_EMIT_REDSTONE, + this::getEmitRedstoneIfFullButtonTooltip).setUpdateTooltipEveryTick(true)); + } + + private GT_TooltipDataCache.TooltipData getEmitRedstoneIfFullButtonTooltip() { + return mTooltipCache.getUncachedTooltipData( + EMIT_REDSTONE_IF_FULL_TOOLTIP, + StatCollector.translateToLocal(hasEmptySlots() ? "gui.yes" : "gui.no"), + getRedstoneOutput()); + } + + protected void addInvertRedstoneButton(ModularWindow.Builder builder) { + builder.widget( + createToggleButton( + () -> bInvert, + val -> bInvert = val, + GT_UITextures.OVERLAY_BUTTON_INVERT_REDSTONE, + () -> mTooltipCache.getData(INVERT_REDSTONE_TOOLTIP))); + } + + protected void addStockingModeButton(ModularWindow.Builder builder) { + builder.widget( + createToggleButton( + () -> bStockingMode, + val -> bStockingMode = val, + GT_UITextures.OVERLAY_BUTTON_STOCKING_MODE, + () -> mTooltipCache.getData(STOCKING_MODE_TOOLTIP))); + } + + protected void addSortStacksButton(ModularWindow.Builder builder) { + builder.widget( + createToggleButton( + () -> bSortStacks, + val -> bSortStacks = val, + GT_UITextures.OVERLAY_BUTTON_SORTING_MODE, + () -> mTooltipCache.getData(SORTING_MODE_TOOLTIP))); + } + + @Override + public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) { + buildContext.addCloseListener(() -> uiButtonCount = 0); + addEmitEnergyButton(builder); + } + + protected Widget createToggleButton(Supplier<Boolean> getter, Consumer<Boolean> setter, UITexture picture, + Supplier<GT_TooltipDataCache.TooltipData> tooltipDataSupplier) { + return new CycleButtonWidget().setToggle(getter, setter) + .setStaticTexture(picture) + .setVariableBackground(GT_UITextures.BUTTON_STANDARD_TOGGLE) + .setTooltipShowUpDelay(TOOLTIP_DELAY) + .setPos(7 + (uiButtonCount++ * BUTTON_SIZE), 62) + .setSize(BUTTON_SIZE, BUTTON_SIZE) + .setGTTooltip(tooltipDataSupplier); + } + + protected void addInventorySlots(ModularWindow.Builder builder) { + builder.widget( + SlotGroup.ofItemHandler(inventoryHandler, 9) + .endAtSlot(26) + .build() + .setPos(7, 4)); + } +} diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_CubicMultiBlockBase.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_CubicMultiBlockBase.java new file mode 100644 index 0000000000..efb91e3a26 --- /dev/null +++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_CubicMultiBlockBase.java @@ -0,0 +1,141 @@ +package gregtech.api.metatileentity.implementations; + +import static com.gtnewhorizon.structurelib.structure.StructureUtility.lazy; +import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofChain; +import static com.gtnewhorizon.structurelib.structure.StructureUtility.onElementPass; +import static com.gtnewhorizon.structurelib.structure.StructureUtility.transpose; +import static gregtech.api.enums.GT_HatchElement.Energy; +import static gregtech.api.enums.GT_HatchElement.InputBus; +import static gregtech.api.enums.GT_HatchElement.InputHatch; +import static gregtech.api.enums.GT_HatchElement.Maintenance; +import static gregtech.api.enums.GT_HatchElement.Muffler; +import static gregtech.api.enums.GT_HatchElement.OutputBus; +import static gregtech.api.enums.GT_HatchElement.OutputHatch; + +import java.util.List; + +import net.minecraft.item.ItemStack; + +import com.google.common.collect.ImmutableList; +import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable; +import com.gtnewhorizon.structurelib.structure.IStructureDefinition; +import com.gtnewhorizon.structurelib.structure.IStructureElement; +import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment; +import com.gtnewhorizon.structurelib.structure.StructureDefinition; + +import gregtech.api.interfaces.IHatchElement; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.util.GT_StructureUtility; + +/** + * A simple 3x3x3 hollow cubic multiblock, that can be arbitrarily rotated, made of a single type of machine casing and + * accepts hatches everywhere. Controller will be placed in front center of the structure. + * <p> + * Note: You cannot use different casing for the same Class. Make a new subclass for it. You also should not change the + * casing dynamically, i.e. it should be a dumb method returning some sort of constant. + * <p> + * Implementation tips: 1. To restrict hatches, override {@link #addDynamoToMachineList(IGregTechTileEntity, int)} and + * its cousins instead of overriding the whole {@link #getStructureDefinition()} or change + * {@link #checkHatches(IGregTechTileEntity, ItemStack)}. The former is a total overkill, while the later cannot stop + * the structure check early. 2. To limit rotation, override {@link #getInitialAlignmentLimits()} + * + * @param <T> + */ +public abstract class GT_MetaTileEntity_CubicMultiBlockBase<T extends GT_MetaTileEntity_CubicMultiBlockBase<T>> + extends GT_MetaTileEntity_EnhancedMultiBlockBase<T> implements ISurvivalConstructable { + + protected static final String STRUCTURE_PIECE_MAIN = "main"; + protected static final ClassValue<IStructureDefinition<GT_MetaTileEntity_CubicMultiBlockBase<?>>> STRUCTURE_DEFINITION = new ClassValue<>() { + + @Override + protected IStructureDefinition<GT_MetaTileEntity_CubicMultiBlockBase<?>> computeValue(Class<?> type) { + return StructureDefinition.<GT_MetaTileEntity_CubicMultiBlockBase<?>>builder() + .addShape( + STRUCTURE_PIECE_MAIN, + transpose( + new String[][] { { "hhh", "hhh", "hhh" }, { "h~h", "h-h", "hhh" }, { "hhh", "hhh", "hhh" }, })) + .addElement( + 'h', + ofChain( + lazy( + t -> GT_StructureUtility.<GT_MetaTileEntity_CubicMultiBlockBase<?>>buildHatchAdder() + .atLeastList(t.getAllowedHatches()) + .casingIndex(t.getHatchTextureIndex()) + .dot(1) + .build()), + onElementPass( + GT_MetaTileEntity_CubicMultiBlockBase::onCorrectCasingAdded, + lazy(GT_MetaTileEntity_CubicMultiBlockBase::getCasingElement)))) + .build(); + } + }; + private int mCasingAmount = 0; + + protected GT_MetaTileEntity_CubicMultiBlockBase(int aID, String aName, String aNameRegional) { + super(aID, aName, aNameRegional); + } + + protected GT_MetaTileEntity_CubicMultiBlockBase(String aName) { + super(aName); + } + + /** + * Create a simple 3x3x3 hollow cubic structure made of a single type of machine casing and accepts hatches + * everywhere. + * <p> + * The created definition contains a single piece named {@link #STRUCTURE_PIECE_MAIN}. + */ + @Override + @SuppressWarnings("unchecked") + public IStructureDefinition<T> getStructureDefinition() { + return (IStructureDefinition<T>) STRUCTURE_DEFINITION.get(getClass()); + } + + @Override + public void construct(ItemStack aStack, boolean aHintsOnly) { + buildPiece(STRUCTURE_PIECE_MAIN, aStack, aHintsOnly, 1, 1, 0); + } + + @Override + public int survivalConstruct(ItemStack stackSize, int elementBudget, ISurvivalBuildEnvironment env) { + if (mMachine) return -1; + return survivialBuildPiece(STRUCTURE_PIECE_MAIN, stackSize, 1, 1, 0, elementBudget, env, false, true); + } + + @Override + public boolean checkMachine(IGregTechTileEntity aBaseMetaTileEntity, ItemStack aStack) { + mCasingAmount = 0; + return checkPiece(STRUCTURE_PIECE_MAIN, 1, 1, 0) && mCasingAmount >= getRequiredCasingCount() + && checkHatches(aBaseMetaTileEntity, aStack); + } + + /** + * Called by {@link #checkMachine(IGregTechTileEntity, ItemStack)} to check if all required hatches are present. + * <p> + * Default implementation requires EXACTLY ONE maintenance hatch to be present, and ignore all other conditions. + * + * @param aBaseMetaTileEntity the tile entity of self + * @param aStack The item stack inside the controller + * @return true if the test passes, false otherwise + */ + protected boolean checkHatches(IGregTechTileEntity aBaseMetaTileEntity, ItemStack aStack) { + return mMaintenanceHatches.size() == 1; + } + + protected abstract IStructureElement<GT_MetaTileEntity_CubicMultiBlockBase<?>> getCasingElement(); + + protected List<IHatchElement<? super GT_MetaTileEntity_CubicMultiBlockBase<?>>> getAllowedHatches() { + return ImmutableList.of(InputHatch, OutputHatch, InputBus, OutputBus, Muffler, Maintenance, Energy); + } + + /** + * The hatch's texture index. + */ + protected abstract int getHatchTextureIndex(); + + protected abstract int getRequiredCasingCount(); + + protected void onCorrectCasingAdded() { + mCasingAmount++; + } +} diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_EnhancedMultiBlockBase.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_EnhancedMultiBlockBase.java new file mode 100644 index 0000000000..c7f0e6cdd5 --- /dev/null +++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_EnhancedMultiBlockBase.java @@ -0,0 +1,318 @@ +package gregtech.api.metatileentity.implementations; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraftforge.common.util.ForgeDirection; + +import com.gtnewhorizon.structurelib.StructureLibAPI; +import com.gtnewhorizon.structurelib.alignment.IAlignment; +import com.gtnewhorizon.structurelib.alignment.IAlignmentLimits; +import com.gtnewhorizon.structurelib.alignment.IAlignmentProvider; +import com.gtnewhorizon.structurelib.alignment.constructable.IConstructable; +import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing; +import com.gtnewhorizon.structurelib.alignment.enumerable.Flip; +import com.gtnewhorizon.structurelib.alignment.enumerable.Rotation; +import com.gtnewhorizon.structurelib.structure.IItemSource; +import com.gtnewhorizon.structurelib.structure.IStructureDefinition; +import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment; + +import cpw.mods.fml.common.network.NetworkRegistry; +import gregtech.api.GregTech_API; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.util.GT_Multiblock_Tooltip_Builder; +import gregtech.api.util.shutdown.ShutDownReasonRegistry; + +/** + * Enhanced multiblock base class, featuring following improvement over {@link GT_MetaTileEntity_MultiBlockBase} + * <p> + * 1. TecTech style declarative structure check utilizing StructureLib. 2. Arbitrarily rotating the whole structure, if + * allowed to. + * + * @param <T> type of this + */ +public abstract class GT_MetaTileEntity_EnhancedMultiBlockBase<T extends GT_MetaTileEntity_EnhancedMultiBlockBase<T>> + extends GT_MetaTileEntity_TooltipMultiBlockBase implements IAlignment, IConstructable { + + private ExtendedFacing mExtendedFacing = ExtendedFacing.DEFAULT; + private IAlignmentLimits mLimits = getInitialAlignmentLimits(); + + protected GT_MetaTileEntity_EnhancedMultiBlockBase(int aID, String aName, String aNameRegional) { + super(aID, aName, aNameRegional); + } + + protected GT_MetaTileEntity_EnhancedMultiBlockBase(String aName) { + super(aName); + } + + @Override + public ExtendedFacing getExtendedFacing() { + return mExtendedFacing; + } + + @Override + public void setExtendedFacing(ExtendedFacing newExtendedFacing) { + if (mExtendedFacing != newExtendedFacing) { + if (mMachine) stopMachine(ShutDownReasonRegistry.STRUCTURE_INCOMPLETE); + mExtendedFacing = newExtendedFacing; + final IGregTechTileEntity base = getBaseMetaTileEntity(); + mMachine = false; + mUpdated = false; + mUpdate = 100; + if (getBaseMetaTileEntity().isServerSide() + && !GregTech_API.isDummyWorld(getBaseMetaTileEntity().getWorld())) { + StructureLibAPI.sendAlignment( + (IAlignmentProvider) base, + new NetworkRegistry.TargetPoint( + base.getWorld().provider.dimensionId, + base.getXCoord(), + base.getYCoord(), + base.getZCoord(), + 512)); + } else { + base.issueTextureUpdate(); + } + } + } + + @Override + public final boolean isFacingValid(ForgeDirection facing) { + return canSetToDirectionAny(facing); + } + + @Override + public boolean onWrenchRightClick(ForgeDirection side, ForgeDirection wrenchingSide, EntityPlayer entityPlayer, + float aX, float aY, float aZ) { + if (wrenchingSide != getBaseMetaTileEntity().getFrontFacing()) + return super.onWrenchRightClick(side, wrenchingSide, entityPlayer, aX, aY, aZ); + if (entityPlayer.isSneaking()) { + if (isFlipChangeAllowed()) { + toolSetFlip(getFlip().isHorizontallyFlipped() ? Flip.NONE : Flip.HORIZONTAL); + } else { + return false; + } + } else { + if (isRotationChangeAllowed()) { + toolSetRotation(null); + } else { + return false; + } + } + return true; + } + + @Override + public void onFacingChange() { + toolSetDirection(getBaseMetaTileEntity().getFrontFacing()); + } + + @Override + public IAlignmentLimits getAlignmentLimits() { + return mLimits; + } + + protected void setAlignmentLimits(IAlignmentLimits mLimits) { + this.mLimits = mLimits; + } + + /** + * Due to limitation of Java type system, you might need to do an unchecked cast. HOWEVER, the returned + * IStructureDefinition is expected to be evaluated against current instance only, and should not be used against + * other instances, even for those of the same class. + */ + public abstract IStructureDefinition<T> getStructureDefinition(); + + protected abstract GT_Multiblock_Tooltip_Builder createTooltip(); + + @Override + public String[] getStructureDescription(ItemStack stackSize) { + return getTooltip().getStructureHint(); + } + + protected IAlignmentLimits getInitialAlignmentLimits() { + return (d, r, f) -> !f.isVerticallyFliped(); + } + + @Override + public void saveNBTData(NBTTagCompound aNBT) { + super.saveNBTData(aNBT); + aNBT.setByte( + "eRotation", + (byte) mExtendedFacing.getRotation() + .getIndex()); + aNBT.setByte( + "eFlip", + (byte) mExtendedFacing.getFlip() + .getIndex()); + } + + @Override + public void loadNBTData(NBTTagCompound aNBT) { + super.loadNBTData(aNBT); + mExtendedFacing = ExtendedFacing.of( + getBaseMetaTileEntity().getFrontFacing(), + Rotation.byIndex(aNBT.getByte("eRotation")), + Flip.byIndex(aNBT.getByte("eFlip"))); + if (!getAlignmentLimits().isNewExtendedFacingValid(mExtendedFacing)) { + mExtendedFacing = getCorrectedAlignment(mExtendedFacing); + } + } + + /** + * When an old {@link ExtendedFacing} is loaded upon MTE deserialization, and the loaded facing cannot pass test of + * current {@link #getAlignmentLimits()}, this method will be called to retrieve a corrected version. This method + * is currently only intended to be used as a mean to migrate alignment limits, so if you never change the alignment + * limit then you can probably just use the default implementation. + * + * The returned new facing must be able to pass the test of {@link #isNewExtendedFacingValid(ExtendedFacing)} + */ + protected ExtendedFacing getCorrectedAlignment(ExtendedFacing aOldFacing) { + if (isNewExtendedFacingValid(ExtendedFacing.DEFAULT)) { + return ExtendedFacing.DEFAULT; + } + for (ExtendedFacing facing : ExtendedFacing.VALUES) { + if (facing.getFlip() + .isVerticallyFliped()) { + continue; + } + if (isNewExtendedFacingValid(facing)) { + return facing; + } + } + throw new AssertionError("No ExtendedFacing can pass the test of isNewExtendedFacingValid"); + } + + @SuppressWarnings("unchecked") + private IStructureDefinition<GT_MetaTileEntity_EnhancedMultiBlockBase<T>> getCastedStructureDefinition() { + return (IStructureDefinition<GT_MetaTileEntity_EnhancedMultiBlockBase<T>>) getStructureDefinition(); + } + + /** + * Explanation of the world coordinate these offset means: + * <p> + * Imagine you stand in front of the controller, with controller facing towards you not rotated or flipped. + * <p> + * The horizontalOffset would be the number of blocks on the left side of the controller, not counting controller + * itself. The verticalOffset would be the number of blocks on the top side of the controller, not counting + * controller itself. The depthOffset would be the number of blocks between you and controller, not counting + * controller itself. + * <p> + * All these offsets can be negative. + */ + protected final boolean checkPiece(String piece, int horizontalOffset, int verticalOffset, int depthOffset) { + final IGregTechTileEntity tTile = getBaseMetaTileEntity(); + return getCastedStructureDefinition().check( + this, + piece, + tTile.getWorld(), + getExtendedFacing(), + tTile.getXCoord(), + tTile.getYCoord(), + tTile.getZCoord(), + horizontalOffset, + verticalOffset, + depthOffset, + !mMachine); + } + + protected final boolean buildPiece(String piece, ItemStack trigger, boolean hintOnly, int horizontalOffset, + int verticalOffset, int depthOffset) { + final IGregTechTileEntity tTile = getBaseMetaTileEntity(); + return getCastedStructureDefinition().buildOrHints( + this, + trigger, + piece, + tTile.getWorld(), + getExtendedFacing(), + tTile.getXCoord(), + tTile.getYCoord(), + tTile.getZCoord(), + horizontalOffset, + verticalOffset, + depthOffset, + hintOnly); + } + + @Deprecated + protected final int survivialBuildPiece(String piece, ItemStack trigger, int horizontalOffset, int verticalOffset, + int depthOffset, int elementsBudget, IItemSource source, EntityPlayerMP actor, boolean check) { + final IGregTechTileEntity tTile = getBaseMetaTileEntity(); + return getCastedStructureDefinition().survivalBuild( + this, + trigger, + piece, + tTile.getWorld(), + getExtendedFacing(), + tTile.getXCoord(), + tTile.getYCoord(), + tTile.getZCoord(), + horizontalOffset, + verticalOffset, + depthOffset, + elementsBudget, + source, + actor, + check); + } + + protected final int survivialBuildPiece(String piece, ItemStack trigger, int horizontalOffset, int verticalOffset, + int depthOffset, int elementsBudget, ISurvivalBuildEnvironment env, boolean check) { + final IGregTechTileEntity tTile = getBaseMetaTileEntity(); + return getCastedStructureDefinition().survivalBuild( + this, + trigger, + piece, + tTile.getWorld(), + getExtendedFacing(), + tTile.getXCoord(), + tTile.getYCoord(), + tTile.getZCoord(), + horizontalOffset, + verticalOffset, + depthOffset, + elementsBudget, + env, + check); + } + + @Deprecated + protected final int survivialBuildPiece(String piece, ItemStack trigger, int horizontalOffset, int verticalOffset, + int depthOffset, int elementsBudget, IItemSource source, EntityPlayerMP actor, boolean check, + boolean checkIfPlaced) { + int built = survivialBuildPiece( + piece, + trigger, + horizontalOffset, + verticalOffset, + depthOffset, + elementsBudget, + source, + actor, + check); + if (checkIfPlaced && built > 0) checkStructure(true, getBaseMetaTileEntity()); + return built; + } + + protected final int survivialBuildPiece(String piece, ItemStack trigger, int horizontalOffset, int verticalOffset, + int depthOffset, int elementsBudget, ISurvivalBuildEnvironment env, boolean check, boolean checkIfPlaced) { + int built = survivialBuildPiece( + piece, + trigger, + horizontalOffset, + verticalOffset, + depthOffset, + elementsBudget, + env, + check); + if (checkIfPlaced && built > 0) checkStructure(true, getBaseMetaTileEntity()); + return built; + } + + @Override + public void onFirstTick(IGregTechTileEntity aBaseMetaTileEntity) { + super.onFirstTick(aBaseMetaTileEntity); + if (aBaseMetaTileEntity.isClientSide()) + StructureLibAPI.queryAlignment((IAlignmentProvider) aBaseMetaTileEntity); + } +} diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_ExtendedPowerMultiBlockBase.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_ExtendedPowerMultiBlockBase.java new file mode 100644 index 0000000000..b7f34f264c --- /dev/null +++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_ExtendedPowerMultiBlockBase.java @@ -0,0 +1,242 @@ +package gregtech.api.metatileentity.implementations; + +import static gregtech.api.enums.GT_Values.VN; +import static gregtech.api.util.GT_Utility.filterValidMTEs; + +import java.util.ArrayList; +import java.util.List; + +import javax.annotation.Nonnull; + +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.StatCollector; +import net.minecraft.world.World; + +import org.jetbrains.annotations.NotNull; + +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.logic.ProcessingLogic; +import gregtech.api.recipe.check.CheckRecipeResult; +import gregtech.api.util.GT_ExoticEnergyInputHelper; +import gregtech.api.util.GT_OverclockCalculator; +import gregtech.api.util.GT_Utility; +import gregtech.api.util.shutdown.ShutDownReason; +import gregtech.api.util.shutdown.ShutDownReasonRegistry; + +/** + * Multiblock base class that allows machine to use power over int. + */ +public abstract class GT_MetaTileEntity_ExtendedPowerMultiBlockBase<T extends GT_MetaTileEntity_EnhancedMultiBlockBase<T>> + extends GT_MetaTileEntity_EnhancedMultiBlockBase<T> { + + public long lEUt; + + protected GT_MetaTileEntity_ExtendedPowerMultiBlockBase(int aID, String aName, String aNameRegional) { + super(aID, aName, aNameRegional); + } + + protected GT_MetaTileEntity_ExtendedPowerMultiBlockBase(String aName) { + super(aName); + } + + @Override + public void loadNBTData(NBTTagCompound aNBT) { + super.loadNBTData(aNBT); + // NBT can be loaded as any primitive type, so we can load it as long. + this.lEUt = aNBT.getLong("mEUt"); + } + + @Override + public void saveNBTData(NBTTagCompound aNBT) { + super.saveNBTData(aNBT); + aNBT.setLong("mEUt", this.lEUt); + } + + @Override + protected void calculateOverclockedNessMultiInternal(long aEUt, int aDuration, int mAmperage, long maxInputVoltage, + boolean perfectOC) { + GT_OverclockCalculator calculator = new GT_OverclockCalculator().setRecipeEUt(aEUt) + .setEUt(maxInputVoltage * mAmperage) + .setDuration(aDuration) + .setDurationDecreasePerOC(perfectOC ? 2 : 1) + .calculate(); + lEUt = calculator.getConsumption(); + mMaxProgresstime = calculator.getDuration(); + } + + @Override + public boolean onRunningTick(ItemStack aStack) { + if (this.lEUt > 0) { + addEnergyOutput((this.lEUt * mEfficiency) / 10000); + return true; + } + if (this.lEUt < 0) { + if (!drainEnergyInput(getActualEnergyUsage())) { + stopMachine(ShutDownReasonRegistry.POWER_LOSS); + return false; + } + } + return true; + } + + @Override + public void stopMachine(@NotNull ShutDownReason reason) { + this.lEUt = 0; + super.stopMachine(reason); + } + + @Override + protected long getActualEnergyUsage() { + return (-this.lEUt * 10000) / Math.max(1000, mEfficiency); + } + + public List<GT_MetaTileEntity_Hatch> getExoticAndNormalEnergyHatchList() { + List<GT_MetaTileEntity_Hatch> tHatches = new ArrayList<>(); + tHatches.addAll(filterValidMTEs(mExoticEnergyHatches)); + tHatches.addAll(filterValidMTEs(mEnergyHatches)); + return tHatches; + } + + @Override + public boolean drainEnergyInput(long aEU) { + return GT_ExoticEnergyInputHelper.drainEnergy(aEU, getExoticAndNormalEnergyHatchList()); + } + + @Override + public void getWailaNBTData(EntityPlayerMP player, TileEntity tile, NBTTagCompound tag, World world, int x, int y, + int z) { + super.getWailaNBTData(player, tile, tag, world, x, y, z); + + final IGregTechTileEntity tileEntity = getBaseMetaTileEntity(); + if (tileEntity != null) { + if (tileEntity.isActive()) { + if (lEUt < 0) tag.setLong("energyUsage", getActualEnergyUsage()); + else tag.setLong("energyUsage", -lEUt * mEfficiency / 10000); + } + } + } + + @Override + protected void setProcessingLogicPower(ProcessingLogic logic) { + logic.setAvailableVoltage(getAverageInputVoltage()); + logic.setAvailableAmperage(getMaxInputAmps()); + logic.setAmperageOC(true); + } + + @Nonnull + @Override + protected CheckRecipeResult postCheckRecipe(@Nonnull CheckRecipeResult result, + @Nonnull ProcessingLogic processingLogic) { + return result; + } + + @Override + protected void setEnergyUsage(ProcessingLogic processingLogic) { + lEUt = processingLogic.getCalculatedEut(); + if (lEUt > 0) { + lEUt = (-lEUt); + } + } + + @Override + public String[] getInfoData() { + int mPollutionReduction = 0; + for (GT_MetaTileEntity_Hatch_Muffler tHatch : filterValidMTEs(mMufflerHatches)) { + mPollutionReduction = Math.max(tHatch.calculatePollutionReduction(100), mPollutionReduction); + } + + long storedEnergy = 0; + long maxEnergy = 0; + for (GT_MetaTileEntity_Hatch tHatch : getExoticAndNormalEnergyHatchList()) { + storedEnergy += tHatch.getBaseMetaTileEntity() + .getStoredEU(); + maxEnergy += tHatch.getBaseMetaTileEntity() + .getEUCapacity(); + } + long voltage = getAverageInputVoltage(); + long amps = getMaxInputAmps(); + + return new String[] { + /* 1 */ StatCollector.translateToLocal("GT5U.multiblock.Progress") + ": " + + EnumChatFormatting.GREEN + + GT_Utility.formatNumbers(mProgresstime / 20) + + EnumChatFormatting.RESET + + " s / " + + EnumChatFormatting.YELLOW + + GT_Utility.formatNumbers(mMaxProgresstime / 20) + + EnumChatFormatting.RESET + + " s", + /* 2 */ StatCollector.translateToLocal("GT5U.multiblock.energy") + ": " + + EnumChatFormatting.GREEN + + GT_Utility.formatNumbers(storedEnergy) + + EnumChatFormatting.RESET + + " EU / " + + EnumChatFormatting.YELLOW + + GT_Utility.formatNumbers(maxEnergy) + + EnumChatFormatting.RESET + + " EU", + /* 3 */ StatCollector.translateToLocal("GT5U.multiblock.usage") + ": " + + EnumChatFormatting.RED + + GT_Utility.formatNumbers(getActualEnergyUsage()) + + EnumChatFormatting.RESET + + " EU/t", + /* 4 */ StatCollector.translateToLocal("GT5U.multiblock.mei") + ": " + + EnumChatFormatting.YELLOW + + GT_Utility.formatNumbers(voltage) + + EnumChatFormatting.RESET + + " EU/t(*" + + amps + + " A)" + + StatCollector.translateToLocal("GT5U.machines.tier") + + ": " + + EnumChatFormatting.YELLOW + + VN[GT_Utility.getTier(voltage)] + + EnumChatFormatting.RESET, + /* 5 */ StatCollector.translateToLocal("GT5U.multiblock.problems") + ": " + + EnumChatFormatting.RED + + (getIdealStatus() - getRepairStatus()) + + EnumChatFormatting.RESET + + " " + + StatCollector.translateToLocal("GT5U.multiblock.efficiency") + + ": " + + EnumChatFormatting.YELLOW + + mEfficiency / 100.0F + + EnumChatFormatting.RESET + + " %", + /* 6 */ StatCollector.translateToLocal("GT5U.multiblock.pollution") + ": " + + EnumChatFormatting.GREEN + + mPollutionReduction + + EnumChatFormatting.RESET + + " %" }; + } + + @Override + public long getMaxInputVoltage() { + return GT_ExoticEnergyInputHelper.getMaxInputVoltageMulti(getExoticAndNormalEnergyHatchList()); + } + + @Override + public long getAverageInputVoltage() { + return GT_ExoticEnergyInputHelper.getAverageInputVoltageMulti(getExoticAndNormalEnergyHatchList()); + } + + @Override + public long getMaxInputAmps() { + return GT_ExoticEnergyInputHelper.getMaxWorkingInputAmpsMulti(getExoticAndNormalEnergyHatchList()); + } + + @Override + public long getMaxInputEu() { + return GT_ExoticEnergyInputHelper.getTotalEuMulti(getExoticAndNormalEnergyHatchList()); + } + + @Override + public void clearHatches() { + super.clearHatches(); + mExoticEnergyHatches.clear(); + } +} diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_FilterBase.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_FilterBase.java new file mode 100644 index 0000000000..896cb223f3 --- /dev/null +++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_FilterBase.java @@ -0,0 +1,106 @@ +package gregtech.api.metatileentity.implementations; + +import net.minecraft.nbt.NBTTagCompound; + +import com.gtnewhorizons.modularui.api.screen.ModularWindow; +import com.gtnewhorizons.modularui.api.screen.UIBuildContext; + +import gregtech.api.gui.modularui.GT_UITextures; +import gregtech.api.interfaces.ITexture; +import gregtech.api.util.GT_TooltipDataCache; + +public abstract class GT_MetaTileEntity_FilterBase extends GT_MetaTileEntity_Buffer { + + private static final String INVERT_FILTER_TOOLTIP = "GT5U.machines.invert_filter.tooltip"; + protected static final int FILTER_SLOT_INDEX = 9; + protected static final int NUM_INVENTORY_SLOTS = 9; + private static final String EMIT_REDSTONE_GRADUALLY_TOOLTIP = "GT5U" + ".machines.emit_redstone_gradually.tooltip"; + protected boolean invertFilter = false; + + public GT_MetaTileEntity_FilterBase(int aID, String aName, String aNameRegional, int aTier, int aInvSlotCount, + String aDescription) { + super(aID, aName, aNameRegional, aTier, aInvSlotCount, aDescription); + } + + public GT_MetaTileEntity_FilterBase(int aID, String aName, String aNameRegional, int aTier, int aInvSlotCount, + String[] aDescription) { + super(aID, aName, aNameRegional, aTier, aInvSlotCount, aDescription); + } + + public GT_MetaTileEntity_FilterBase(String aName, int aTier, int aInvSlotCount, String aDescription, + ITexture[][][] aTextures) { + super(aName, aTier, aInvSlotCount, aDescription, aTextures); + } + + public GT_MetaTileEntity_FilterBase(String aName, int aTier, int aInvSlotCount, String[] aDescription, + ITexture[][][] aTextures) { + super(aName, aTier, aInvSlotCount, aDescription, aTextures); + } + + @Override + public boolean isValidSlot(int aIndex) { + return aIndex < NUM_INVENTORY_SLOTS; + } + + @Override + public void saveNBTData(NBTTagCompound aNBT) { + super.saveNBTData(aNBT); + aNBT.setBoolean("bInvertFilter", this.invertFilter); + } + + @Override + public void loadNBTData(NBTTagCompound aNBT) { + super.loadNBTData(aNBT); + this.invertFilter = aNBT.getBoolean("bInvertFilter"); + } + + @Override + protected int getRedstoneOutput() { + if (!bRedstoneIfFull) { + return 0; + } + int redstoneOutput = getEmptySlots(); + if (!bInvert) redstoneOutput = NUM_INVENTORY_SLOTS - redstoneOutput; + return redstoneOutput; + } + + private int getEmptySlots() { + int emptySlots = 0; + for (int i = 0; i < NUM_INVENTORY_SLOTS; i++) { + if (mInventory[i] == null) ++emptySlots; + } + return emptySlots; + } + + @Override + public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) { + super.addUIWidgets(builder, buildContext); + addSortStacksButton(builder); + addEmitRedstoneGraduallyButton(builder); + addInvertRedstoneButton(builder); + addInvertFilterButton(builder); + } + + private void addEmitRedstoneGraduallyButton(ModularWindow.Builder builder) { + builder.widget( + createToggleButton( + () -> bRedstoneIfFull, + val -> bRedstoneIfFull = val, + GT_UITextures.OVERLAY_BUTTON_EMIT_REDSTONE, + this::getEmitRedstoneGraduallyButtonTooltip).setUpdateTooltipEveryTick(true)); + } + + private GT_TooltipDataCache.TooltipData getEmitRedstoneGraduallyButtonTooltip() { + return mTooltipCache + .getUncachedTooltipData(EMIT_REDSTONE_GRADUALLY_TOOLTIP, getEmptySlots(), getRedstoneOutput()); + } + + private void addInvertFilterButton(ModularWindow.Builder builder) { + builder.widget( + createToggleButton( + () -> invertFilter, + val -> invertFilter = val, + GT_UITextures.OVERLAY_BUTTON_INVERT_FILTER, + () -> mTooltipCache.getData(INVERT_FILTER_TOOLTIP))); + } +} diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch.java new file mode 100644 index 0000000000..1e974f5e9d --- /dev/null +++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch.java @@ -0,0 +1,252 @@ +package gregtech.api.metatileentity.implementations; + +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraftforge.common.util.ForgeDirection; + +import appeng.api.crafting.ICraftingIconProvider; +import gregtech.api.enums.GT_Values; +import gregtech.api.enums.Textures; +import gregtech.api.interfaces.ITexture; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; + +/** + * Handles texture changes internally. No special calls are necessary other than updateTexture in add***ToMachineList. + */ +public abstract class GT_MetaTileEntity_Hatch extends GT_MetaTileEntity_BasicTank implements ICraftingIconProvider { + + public enum ConnectionType { + CABLE, + WIRELESS, + LASER + } + + /** + * Uses new texture changing methods to avoid limitations of byte as texture index... + */ + @Deprecated + public byte mMachineBlock = 0; + + private byte mTexturePage = 0; + private byte actualTexture = 0; + + private ItemStack ae2CraftingIcon; + + public GT_MetaTileEntity_Hatch(int aID, String aName, String aNameRegional, int aTier, int aInvSlotCount, + String aDescription, ITexture... aTextures) { + super(aID, aName, aNameRegional, aTier, aInvSlotCount, aDescription, aTextures); + } + + public GT_MetaTileEntity_Hatch(int aID, String aName, String aNameRegional, int aTier, int aInvSlotCount, + String[] aDescription, ITexture... aTextures) { + super(aID, aName, aNameRegional, aTier, aInvSlotCount, aDescription, aTextures); + } + + public GT_MetaTileEntity_Hatch(String aName, int aTier, int aInvSlotCount, String aDescription, + ITexture[][][] aTextures) { + super(aName, aTier, aInvSlotCount, aDescription, aTextures); + } + + public GT_MetaTileEntity_Hatch(String aName, int aTier, int aInvSlotCount, String[] aDescription, + ITexture[][][] aTextures) { + super(aName, aTier, aInvSlotCount, aDescription, aTextures); + } + + public static int getSlots(int aTier) { + return aTier < 1 ? 1 : aTier == 1 ? 4 : aTier == 2 ? 9 : 16; + } + + @Override + public ITexture[][][] getTextureSet(ITexture[] aTextures) { + return new ITexture[0][0][0]; + } + + public abstract ITexture[] getTexturesActive(ITexture aBaseTexture); + + public abstract ITexture[] getTexturesInactive(ITexture aBaseTexture); + + @Override + public ITexture[] getTexture(IGregTechTileEntity aBaseMetaTileEntity, ForgeDirection side, ForgeDirection aFacing, + int colorIndex, boolean aActive, boolean redstoneLevel) { + int texturePointer = (byte) (actualTexture & 0x7F); // just to be sure, from my testing the 8th bit cannot be + // set clientside + int textureIndex = texturePointer | (mTexturePage << 7); // Shift seven since one page is 128 textures! + try { + if (side != aFacing) { + if (textureIndex > 0) + return new ITexture[] { Textures.BlockIcons.casingTexturePages[mTexturePage][texturePointer] }; + else return new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][colorIndex + 1] }; + } else { + if (textureIndex > 0) { + if (aActive) + return getTexturesActive(Textures.BlockIcons.casingTexturePages[mTexturePage][texturePointer]); + else return getTexturesInactive( + Textures.BlockIcons.casingTexturePages[mTexturePage][texturePointer]); + } else { + if (aActive) return getTexturesActive(Textures.BlockIcons.MACHINE_CASINGS[mTier][colorIndex + 1]); + else return getTexturesInactive(Textures.BlockIcons.MACHINE_CASINGS[mTier][colorIndex + 1]); + } + } + } catch (NullPointerException npe) { + return new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[0][0] }; + } + } + + @Override + public void saveNBTData(NBTTagCompound aNBT) { + super.saveNBTData(aNBT); + aNBT.setByte("mMachineBlock", actualTexture); + aNBT.setByte("mTexturePage", mTexturePage); + } + + @Override + public void loadNBTData(NBTTagCompound aNBT) { + super.loadNBTData(aNBT); + actualTexture = aNBT.getByte("mMachineBlock"); + mTexturePage = aNBT.getByte("mTexturePage"); + + if (mTexturePage != 0 && GT_Values.GT.isServerSide()) actualTexture |= 0x80; // <- lets just hope no one needs + // the correct value for that on + // server + mMachineBlock = actualTexture; + } + + /** + * Sets texture with page and index, called on add to machine list + * + * @param id (page<<7)+index of the texture + */ + public final void updateTexture(int id) { + onValueUpdate((byte) id); + onTexturePageUpdate((byte) (id >> 7)); + } + + /** + * Sets the icon for the owning multiblock used for AE2 crafting display of attached interfaces, called on add to + * machine list + */ + public final void updateCraftingIcon(ItemStack icon) { + this.ae2CraftingIcon = icon; + } + + @Override + public ItemStack getMachineCraftingIcon() { + return this.ae2CraftingIcon; + } + + /** + * Some multiblocks restrict hatches by tier. This method allows hatches to specify custom tier used for + * structure check, while keeping {@link #mTier} for other uses. + * + * @return Tier used for multiblock structure + */ + public byte getTierForStructure() { + return mTier; + } + + /** + * Sets texture with page and index, rather unusable, but kept FFS + * + * @param page page of texure + * @param index index of texure + */ + @Deprecated + public final void updateTexture(byte page, byte index) { + onValueUpdate(index); + onTexturePageUpdate(page); + } + + @Override + public final void onValueUpdate(byte aValue) { + actualTexture = (byte) (aValue & 0x7F); + mMachineBlock = actualTexture; + mTexturePage = 0; + } + + /** + * Get the maximum amount of amperes to work with, which excludes the additional amps in for loss + * + * @return Working amps + */ + public long maxWorkingAmperesIn() { + return maxAmperesIn(); + } + + /** + * Get the type of connection this hatch allows + * + * @return Connection type + */ + public ConnectionType getConnectionType() { + return ConnectionType.CABLE; + } + + @Override + public final byte getUpdateData() { + return (byte) (actualTexture & 0x7F); + } + + public final void onTexturePageUpdate(byte aValue) { + mTexturePage = (byte) (aValue & 0x7F); + if (mTexturePage != 0 && getBaseMetaTileEntity().isServerSide()) { // just to be sure + mMachineBlock |= 0x80; // <- lets just hope no one needs the correct value for that on server + actualTexture = mMachineBlock; + } + // set last bit to allow working of the page reset-er to 0 in rare case when texture id is the same but page + // changes to 0 + } + + public final byte getTexturePage() { + return (byte) (mTexturePage & 0x7F); + } + + @Override + public boolean doesFillContainers() { + return false; + } + + @Override + public boolean doesEmptyContainers() { + return false; + } + + @Override + public boolean canTankBeFilled() { + return false; + } + + @Override + public boolean canTankBeEmptied() { + return false; + } + + @Override + public boolean displaysItemStack() { + return false; + } + + @Override + public boolean displaysStackSize() { + return false; + } + + @Override + public void onPreTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) { // in that method since it is usually + // not overriden, especially for + // hatches. + if (actualTexture != mMachineBlock) { // revert to page 0 on edition of the field - old code way + actualTexture = (byte) (mMachineBlock & 0x7F); + mMachineBlock = actualTexture; // clear last bit in mMachineBlock since now we are at page 0 after the + // direct field + // change + mTexturePage = 0; // assuming old code only supports page 0 + } + super.onPreTick(aBaseMetaTileEntity, aTick); + } + + // To change to other page -> use the setter method -> updateTexture + + public int getCircuitSlot() { + return -1; + } +} diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_DataAccess.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_DataAccess.java new file mode 100644 index 0000000000..122dcfa746 --- /dev/null +++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_DataAccess.java @@ -0,0 +1,157 @@ +package gregtech.api.metatileentity.implementations; + +import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_DATA_ACCESS; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraftforge.common.util.ForgeDirection; + +import com.gtnewhorizons.modularui.api.screen.ModularWindow; +import com.gtnewhorizons.modularui.api.screen.UIBuildContext; + +import gregtech.api.gui.modularui.GT_UIInfos; +import gregtech.api.gui.modularui.GT_UITextures; +import gregtech.api.interfaces.ITexture; +import gregtech.api.interfaces.modularui.IAddUIWidgets; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.metatileentity.MetaTileEntity; +import gregtech.api.render.TextureFactory; +import gregtech.api.util.GT_AssemblyLineUtils; + +public class GT_MetaTileEntity_Hatch_DataAccess extends GT_MetaTileEntity_Hatch implements IAddUIWidgets { + + private int timeout = 4; + + public GT_MetaTileEntity_Hatch_DataAccess(int aID, String aName, String aNameRegional, int aTier) { + super( + aID, + aName, + aNameRegional, + aTier, + 16, + new String[] { "Data Access for Multiblocks", + "Adds " + (aTier == 4 ? 4 : 16) + " extra slots for Data Sticks" }); + } + + public GT_MetaTileEntity_Hatch_DataAccess(String aName, int aTier, String aDescription, ITexture[][][] aTextures) { + super(aName, aTier, aTier == 4 ? 4 : 16, aDescription, aTextures); + } + + public GT_MetaTileEntity_Hatch_DataAccess(String aName, int aTier, String[] aDescription, + ITexture[][][] aTextures) { + super(aName, aTier, aTier == 4 ? 4 : 16, aDescription, aTextures); + } + + @Override + public ITexture[] getTexturesActive(ITexture aBaseTexture) { + return new ITexture[] { aBaseTexture, TextureFactory.of(OVERLAY_DATA_ACCESS) }; + } + + @Override + public ITexture[] getTexturesInactive(ITexture aBaseTexture) { + return new ITexture[] { aBaseTexture, TextureFactory.of(OVERLAY_DATA_ACCESS) }; + } + + @Override + public boolean isSimpleMachine() { + return true; + } + + @Override + public boolean isFacingValid(ForgeDirection facing) { + return true; + } + + @Override + public boolean isAccessAllowed(EntityPlayer aPlayer) { + return true; + } + + @Override + public boolean isValidSlot(int aIndex) { + return true; + } + + @Override + public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) { + return new GT_MetaTileEntity_Hatch_DataAccess(mName, mTier, mDescriptionArray, mTextures); + } + + @Override + public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer) { + GT_UIInfos.openGTTileEntityUI(aBaseMetaTileEntity, aPlayer); + return true; + } + + @Override + public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side, + ItemStack aStack) { + return mTier >= 8 && !aBaseMetaTileEntity.isActive(); + } + + @Override + public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side, + ItemStack aStack) { + return mTier >= 8 && !aBaseMetaTileEntity.isActive(); + } + + @Override + public int getInventoryStackLimit() { + return 1; + } + + @Override + public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) { + if (aBaseMetaTileEntity.isServerSide() && aBaseMetaTileEntity.isActive()) { + timeout--; + if (timeout <= 0) { + aBaseMetaTileEntity.setActive(false); + } + } + } + + public void setActive(boolean mActive) { + getBaseMetaTileEntity().setActive(mActive); + timeout = mActive ? 4 : 0; + } + + @Override + public void loadNBTData(NBTTagCompound aNBT) { + super.loadNBTData(aNBT); + if (aNBT.getByte("mSticksUpdated") != 1) { + for (int i = 0; i < getSizeInventory(); i++) { + GT_AssemblyLineUtils.processDataStick(getStackInSlot(i)); + } + } + } + + @Override + public void saveNBTData(NBTTagCompound aNBT) { + super.saveNBTData(aNBT); + // reminder: remove this marker after many years + aNBT.setByte("mSticksUpdated", (byte) 1); + } + + @Override + public void setInventorySlotContents(int aIndex, ItemStack aStack) { + super.setInventorySlotContents(aIndex, aStack); + GT_AssemblyLineUtils.processDataStick(aStack); + } + + @Override + public boolean useModularUI() { + return true; + } + + @Override + public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) { + if (mTier == 4) { + getBaseMetaTileEntity() + .add2by2Slots(builder, getGUITextureSet().getItemSlot(), GT_UITextures.OVERLAY_SLOT_CIRCUIT); + } else { + getBaseMetaTileEntity() + .add4by4Slots(builder, getGUITextureSet().getItemSlot(), GT_UITextures.OVERLAY_SLOT_CIRCUIT); + } + } +} diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_Dynamo.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_Dynamo.java new file mode 100644 index 0000000000..8e621434db --- /dev/null +++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_Dynamo.java @@ -0,0 +1,110 @@ +package gregtech.api.metatileentity.implementations; + +import static gregtech.api.enums.GT_Values.V; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraftforge.common.util.ForgeDirection; + +import gregtech.api.enums.Textures; +import gregtech.api.interfaces.ITexture; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.metatileentity.MetaTileEntity; + +public class GT_MetaTileEntity_Hatch_Dynamo extends GT_MetaTileEntity_Hatch { + + public GT_MetaTileEntity_Hatch_Dynamo(int aID, String aName, String aNameRegional, int aTier) { + super( + aID, + aName, + aNameRegional, + aTier, + 0, + new String[] { "Generating electric Energy from Multiblocks", "Puts out up to 1 Amp" }); + } + + public GT_MetaTileEntity_Hatch_Dynamo(int aID, String aName, String aNameRegional, int aTier, + String[] aDescription) { + super(aID, aName, aNameRegional, aTier, 0, aDescription); + } + + public GT_MetaTileEntity_Hatch_Dynamo(String aName, int aTier, String aDescription, ITexture[][][] aTextures) { + super(aName, aTier, 0, aDescription, aTextures); + } + + public GT_MetaTileEntity_Hatch_Dynamo(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures) { + super(aName, aTier, 0, aDescription, aTextures); + } + + @Override + public ITexture[] getTexturesActive(ITexture aBaseTexture) { + return new ITexture[] { aBaseTexture, Textures.BlockIcons.OVERLAYS_ENERGY_OUT_MULTI[mTier] }; + } + + @Override + public ITexture[] getTexturesInactive(ITexture aBaseTexture) { + return new ITexture[] { aBaseTexture, Textures.BlockIcons.OVERLAYS_ENERGY_OUT_MULTI[mTier] }; + } + + @Override + public boolean isSimpleMachine() { + return true; + } + + @Override + public boolean isFacingValid(ForgeDirection facing) { + return true; + } + + @Override + public boolean isAccessAllowed(EntityPlayer aPlayer) { + return true; + } + + @Override + public boolean isEnetOutput() { + return true; + } + + @Override + public boolean isOutputFacing(ForgeDirection side) { + return side == getBaseMetaTileEntity().getFrontFacing(); + } + + @Override + public boolean isValidSlot(int aIndex) { + return false; + } + + @Override + public long getMinimumStoredEU() { + return 512; + } + + @Override + public long maxEUOutput() { + return V[mTier]; + } + + @Override + public long maxEUStore() { + return 512L + V[mTier + 1] * 2L; + } + + @Override + public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) { + return new GT_MetaTileEntity_Hatch_Dynamo(mName, mTier, mDescriptionArray, mTextures); + } + + @Override + public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side, + ItemStack aStack) { + return false; + } + + @Override + public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side, + ItemStack aStack) { + return false; + } +} diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_Energy.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_Energy.java new file mode 100644 index 0000000000..d9be12671d --- /dev/null +++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_Energy.java @@ -0,0 +1,125 @@ +package gregtech.api.metatileentity.implementations; + +import static gregtech.api.enums.GT_Values.V; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraftforge.common.util.ForgeDirection; + +import gregtech.api.enums.Textures; +import gregtech.api.interfaces.ITexture; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.metatileentity.MetaTileEntity; + +public class GT_MetaTileEntity_Hatch_Energy extends GT_MetaTileEntity_Hatch { + + public GT_MetaTileEntity_Hatch_Energy(int aID, String aName, String aNameRegional, int aTier, + String[] aDescription) { + super(aID, aName, aNameRegional, aTier, 0, aDescription); + } + + public GT_MetaTileEntity_Hatch_Energy(int aID, String aName, String aNameRegional, int aTier) { + super( + aID, + aName, + aNameRegional, + aTier, + 0, + new String[] { "Energy Injector for Multiblocks", "Accepts up to 2 Amps" }); + } + + public GT_MetaTileEntity_Hatch_Energy(int aID, String aName, String aNameRegional, int aTier, int aInvSlotCount, + String[] aDescription, ITexture... aTextures) { + super(aID, aName, aNameRegional, aTier, aInvSlotCount, aDescription, aTextures); + } + + public GT_MetaTileEntity_Hatch_Energy(String aName, int aTier, String aDescription, ITexture[][][] aTextures) { + super(aName, aTier, 0, aDescription, aTextures); + } + + public GT_MetaTileEntity_Hatch_Energy(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures) { + super(aName, aTier, 0, aDescription, aTextures); + } + + public GT_MetaTileEntity_Hatch_Energy(String aName, int aTier, int aInvSlotCount, String[] aDescription, + ITexture[][][] aTextures) { + super(aName, aTier, aInvSlotCount, aDescription, aTextures); + } + + @Override + public ITexture[] getTexturesActive(ITexture aBaseTexture) { + return new ITexture[] { aBaseTexture, Textures.BlockIcons.OVERLAYS_ENERGY_IN_MULTI[mTier] }; + } + + @Override + public ITexture[] getTexturesInactive(ITexture aBaseTexture) { + return new ITexture[] { aBaseTexture, Textures.BlockIcons.OVERLAYS_ENERGY_IN_MULTI[mTier] }; + } + + @Override + public boolean isSimpleMachine() { + return true; + } + + @Override + public boolean isFacingValid(ForgeDirection facing) { + return true; + } + + @Override + public boolean isAccessAllowed(EntityPlayer aPlayer) { + return true; + } + + @Override + public boolean isEnetInput() { + return true; + } + + @Override + public boolean isInputFacing(ForgeDirection side) { + return side == getBaseMetaTileEntity().getFrontFacing(); + } + + @Override + public boolean isValidSlot(int aIndex) { + return false; + } + + @Override + public long getMinimumStoredEU() { + return 512; + } + + @Override + public long maxEUInput() { + return V[mTier]; + } + + @Override + public long maxEUStore() { + return 512L + V[mTier] * 8L; + } + + @Override + public long maxAmperesIn() { + return 2; + } + + @Override + public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) { + return new GT_MetaTileEntity_Hatch_Energy(mName, mTier, mDescriptionArray, mTextures); + } + + @Override + public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side, + ItemStack aStack) { + return false; + } + + @Override + public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side, + ItemStack aStack) { + return false; + } +} diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_Input.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_Input.java new file mode 100644 index 0000000000..18aef371b6 --- /dev/null +++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_Input.java @@ -0,0 +1,201 @@ +package gregtech.api.metatileentity.implementations; + +import static gregtech.api.enums.Textures.BlockIcons.FLUID_IN_SIGN; +import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_PIPE_IN; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraftforge.common.util.ForgeDirection; +import net.minecraftforge.fluids.FluidStack; + +import gregtech.GT_Mod; +import gregtech.api.gui.modularui.GT_UIInfos; +import gregtech.api.interfaces.ITexture; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.metatileentity.MetaTileEntity; +import gregtech.api.recipe.RecipeMap; +import gregtech.api.render.TextureFactory; +import gregtech.api.util.GT_Utility; + +public class GT_MetaTileEntity_Hatch_Input extends GT_MetaTileEntity_Hatch { + + public RecipeMap<?> mRecipeMap = null; + + public GT_MetaTileEntity_Hatch_Input(int aID, String aName, String aNameRegional, int aTier) { + this( + aID, + aName, + aNameRegional, + aTier, + new String[] { "Fluid Input for Multiblocks", + "Capacity: " + GT_Utility.formatNumbers(8000L * (1L << aTier)) + "L" }); + } + + public GT_MetaTileEntity_Hatch_Input(int aID, String aName, String aNameRegional, int aTier, + String[] aDescription) { + this(aID, 3, aName, aNameRegional, aTier, aDescription); + } + + public GT_MetaTileEntity_Hatch_Input(int aID, int aSlot, String aName, String aNameRegional, int aTier) { + this( + aID, + aSlot, + aName, + aNameRegional, + aTier, + new String[] { "Fluid Input for Multiblocks", "", "Can hold " + aSlot + " types of fluid." }); + mDescriptionArray[1] = "Capacity: " + GT_Utility.formatNumbers(getCapacityPerTank(aTier, aSlot)) + "L"; + } + + public GT_MetaTileEntity_Hatch_Input(int aID, int aSlot, String aName, String aNameRegional, int aTier, + String[] aDescription) { + super(aID, aName, aNameRegional, aTier, aSlot, aDescription); + } + + public GT_MetaTileEntity_Hatch_Input(int aID, String aName, String aNameRegional, int aTier, int allSlotCount, + String[] strings) { + super(aID, aName, aNameRegional, aTier, allSlotCount, strings); + } + + public int getCapacityPerTank(int aTier, int aSlot) { + return (int) (8000L * (1L << aTier) / aSlot); + } + + public GT_MetaTileEntity_Hatch_Input(String aName, int aTier, String aDescription, ITexture[][][] aTextures) { + super(aName, aTier, 3, aDescription, aTextures); + } + + public GT_MetaTileEntity_Hatch_Input(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures) { + super(aName, aTier, 3, aDescription, aTextures); + } + + public GT_MetaTileEntity_Hatch_Input(String aName, int aSlots, int aTier, String[] aDescription, + ITexture[][][] aTextures) { + super(aName, aTier, aSlots, aDescription, aTextures); + } + + @Override + public ITexture[] getTexturesActive(ITexture aBaseTexture) { + return GT_Mod.gregtechproxy.mRenderIndicatorsOnHatch + ? new ITexture[] { aBaseTexture, TextureFactory.of(OVERLAY_PIPE_IN), TextureFactory.of(FLUID_IN_SIGN) } + : new ITexture[] { aBaseTexture, TextureFactory.of(OVERLAY_PIPE_IN) }; + } + + @Override + public ITexture[] getTexturesInactive(ITexture aBaseTexture) { + return GT_Mod.gregtechproxy.mRenderIndicatorsOnHatch + ? new ITexture[] { aBaseTexture, TextureFactory.of(OVERLAY_PIPE_IN), TextureFactory.of(FLUID_IN_SIGN) } + : new ITexture[] { aBaseTexture, TextureFactory.of(OVERLAY_PIPE_IN) }; + } + + @Override + public boolean isSimpleMachine() { + return true; + } + + @Override + public boolean isFacingValid(ForgeDirection facing) { + return true; + } + + @Override + public boolean isAccessAllowed(EntityPlayer aPlayer) { + return true; + } + + @Override + public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) { + return new GT_MetaTileEntity_Hatch_Input(mName, mTier, mDescriptionArray, mTextures); + } + + @Override + public void saveNBTData(NBTTagCompound aNBT) { + super.saveNBTData(aNBT); + if (mRecipeMap != null) { + aNBT.setString("recipeMap", mRecipeMap.unlocalizedName); + } + } + + @Override + public void loadNBTData(NBTTagCompound aNBT) { + super.loadNBTData(aNBT); + mRecipeMap = RecipeMap.getFromOldIdentifier(aNBT.getString("recipeMap")); + } + + @Override + public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer) { + GT_UIInfos.openGTTileEntityUI(aBaseMetaTileEntity, aPlayer); + return true; + } + + @Override + public boolean doesFillContainers() { + // return true; + return false; + } + + @Override + public boolean doesEmptyContainers() { + return true; + } + + @Override + public boolean canTankBeFilled() { + return true; + } + + @Override + public boolean canTankBeEmptied() { + return true; + } + + @Override + public boolean displaysItemStack() { + return true; + } + + @Override + public boolean displaysStackSize() { + return false; + } + + public void updateSlots() { + if (mInventory[getInputSlot()] != null && mInventory[getInputSlot()].stackSize <= 0) + mInventory[getInputSlot()] = null; + } + + @Override + public boolean isFluidInputAllowed(FluidStack aFluid) { + return mRecipeMap == null || mRecipeMap.containsInput(aFluid); + } + + @Override + public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side, + ItemStack aStack) { + return side == aBaseMetaTileEntity.getFrontFacing() && aIndex == 1; + } + + @Override + public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side, + ItemStack aStack) { + return side == aBaseMetaTileEntity.getFrontFacing() && aIndex == 0 + && (mRecipeMap == null || mRecipeMap.containsInput(aStack) + || mRecipeMap.containsInput(GT_Utility.getFluidForFilledItem(aStack, true))); + } + + @Override + public int getCapacity() { + return 8000 * (1 << mTier); + } + + @Override + public int getTankPressure() { + return -100; + } + + @Override + public boolean useModularUI() { + return true; + } +} diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_InputBus.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_InputBus.java new file mode 100644 index 0000000000..f4c4eb6a14 --- /dev/null +++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_InputBus.java @@ -0,0 +1,323 @@ +package gregtech.api.metatileentity.implementations; + +import static gregtech.api.enums.Textures.BlockIcons.ITEM_IN_SIGN; +import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_PIPE_IN; +import static gregtech.api.metatileentity.BaseTileEntity.TOOLTIP_DELAY; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Supplier; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.StatCollector; +import net.minecraftforge.common.util.ForgeDirection; + +import com.gtnewhorizons.modularui.api.drawable.UITexture; +import com.gtnewhorizons.modularui.api.screen.ModularWindow; +import com.gtnewhorizons.modularui.api.screen.UIBuildContext; +import com.gtnewhorizons.modularui.api.widget.Widget; +import com.gtnewhorizons.modularui.common.widget.CycleButtonWidget; + +import gregtech.GT_Mod; +import gregtech.api.gui.modularui.GT_UIInfos; +import gregtech.api.gui.modularui.GT_UITextures; +import gregtech.api.interfaces.IConfigurationCircuitSupport; +import gregtech.api.interfaces.ITexture; +import gregtech.api.interfaces.modularui.IAddUIWidgets; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.metatileentity.MetaTileEntity; +import gregtech.api.recipe.RecipeMap; +import gregtech.api.render.TextureFactory; +import gregtech.api.util.GT_ClientPreference; +import gregtech.api.util.GT_OreDictUnificator; +import gregtech.api.util.GT_TooltipDataCache; +import gregtech.api.util.GT_Utility; +import gregtech.api.util.extensions.ArrayExt; + +public class GT_MetaTileEntity_Hatch_InputBus extends GT_MetaTileEntity_Hatch + implements IConfigurationCircuitSupport, IAddUIWidgets { + + private static final String SORTING_MODE_TOOLTIP = "GT5U.machines.sorting_mode.tooltip"; + private static final String ONE_STACK_LIMIT_TOOLTIP = "GT5U.machines.one_stack_limit.tooltip"; + private static final int BUTTON_SIZE = 18; + + public RecipeMap<?> mRecipeMap = null; + public boolean disableSort; + public boolean disableFilter = true; + public boolean disableLimited = true; + private int uiButtonCount = 0; + + public GT_MetaTileEntity_Hatch_InputBus(int id, String name, String nameRegional, int tier) { + this(id, name, nameRegional, tier, getSlots(tier) + 1); + } + + protected GT_MetaTileEntity_Hatch_InputBus(int id, String name, String nameRegional, int tier, int slots, + String[] description) { + super(id, name, nameRegional, tier, slots, description); + } + + public GT_MetaTileEntity_Hatch_InputBus(int id, String name, String nameRegional, int tier, int slots) { + super( + id, + name, + nameRegional, + tier, + slots, + ArrayExt.of( + "Item Input for Multiblocks", + "Shift + right click with screwdriver to turn Sort mode on/off", + "Capacity: " + getSlots(tier) + " stack" + (getSlots(tier) >= 2 ? "s" : ""))); + } + + @Deprecated + // having too many constructors is bad, don't be so lazy, use GT_MetaTileEntity_Hatch_InputBus(String, int, + // String[], ITexture[][][]) + public GT_MetaTileEntity_Hatch_InputBus(String aName, int aTier, String aDescription, ITexture[][][] aTextures) { + this(aName, aTier, ArrayExt.of(aDescription), aTextures); + } + + public GT_MetaTileEntity_Hatch_InputBus(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures) { + this(aName, aTier, getSlots(aTier) + 1, aDescription, aTextures); + } + + public GT_MetaTileEntity_Hatch_InputBus(String aName, int aTier, int aSlots, String[] aDescription, + ITexture[][][] aTextures) { + super(aName, aTier, aSlots, aDescription, aTextures); + } + + @Override + public ITexture[] getTexturesActive(ITexture aBaseTexture) { + return GT_Mod.gregtechproxy.mRenderIndicatorsOnHatch + ? new ITexture[] { aBaseTexture, TextureFactory.of(OVERLAY_PIPE_IN), TextureFactory.of(ITEM_IN_SIGN) } + : new ITexture[] { aBaseTexture, TextureFactory.of(OVERLAY_PIPE_IN) }; + } + + @Override + public ITexture[] getTexturesInactive(ITexture aBaseTexture) { + return GT_Mod.gregtechproxy.mRenderIndicatorsOnHatch + ? new ITexture[] { aBaseTexture, TextureFactory.of(OVERLAY_PIPE_IN), TextureFactory.of(ITEM_IN_SIGN) } + : new ITexture[] { aBaseTexture, TextureFactory.of(OVERLAY_PIPE_IN) }; + } + + @Override + public boolean isSimpleMachine() { + return true; + } + + @Override + public boolean isFacingValid(ForgeDirection facing) { + return true; + } + + @Override + public boolean isAccessAllowed(EntityPlayer aPlayer) { + return true; + } + + @Override + public boolean isValidSlot(int aIndex) { + return aIndex != getCircuitSlot(); + } + + @Override + public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) { + return new GT_MetaTileEntity_Hatch_InputBus(mName, mTier, mDescriptionArray, mTextures); + } + + @Override + public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer) { + GT_UIInfos.openGTTileEntityUI(aBaseMetaTileEntity, aPlayer); + return true; + } + + @Override + public int getCircuitSlotX() { + return 153; + } + + @Override + public int getCircuitSlotY() { + return 63; + } + + @Override + public void initDefaultModes(NBTTagCompound aNBT) { + if (!getBaseMetaTileEntity().getWorld().isRemote) { + GT_ClientPreference tPreference = GT_Mod.gregtechproxy + .getClientPreference(getBaseMetaTileEntity().getOwnerUuid()); + if (tPreference != null) disableFilter = !tPreference.isInputBusInitialFilterEnabled(); + } + } + + @Override + public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTimer) { + if (aBaseMetaTileEntity.isServerSide() && aBaseMetaTileEntity.hasInventoryBeenModified()) { + updateSlots(); + } + } + + public void updateSlots() { + for (int i = 0; i < mInventory.length - 1; i++) + if (mInventory[i] != null && mInventory[i].stackSize <= 0) mInventory[i] = null; + if (!disableSort) fillStacksIntoFirstSlots(); + } + + protected void fillStacksIntoFirstSlots() { + final int L = mInventory.length - 1; + HashMap<GT_Utility.ItemId, Integer> slots = new HashMap<>(L); + HashMap<GT_Utility.ItemId, ItemStack> stacks = new HashMap<>(L); + List<GT_Utility.ItemId> order = new ArrayList<>(L); + List<Integer> validSlots = new ArrayList<>(L); + for (int i = 0; i < L; i++) { + if (!isValidSlot(i)) continue; + validSlots.add(i); + ItemStack s = mInventory[i]; + if (s == null) continue; + GT_Utility.ItemId sID = GT_Utility.ItemId.createNoCopy(s); + slots.merge(sID, s.stackSize, Integer::sum); + if (!stacks.containsKey(sID)) stacks.put(sID, s); + order.add(sID); + mInventory[i] = null; + } + int slotindex = 0; + for (GT_Utility.ItemId sID : order) { + int toSet = slots.get(sID); + if (toSet == 0) continue; + int slot = validSlots.get(slotindex); + slotindex++; + mInventory[slot] = stacks.get(sID) + .copy(); + toSet = Math.min(toSet, mInventory[slot].getMaxStackSize()); + mInventory[slot].stackSize = toSet; + slots.merge(sID, toSet, (a, b) -> a - b); + } + } + + @Override + public void saveNBTData(NBTTagCompound aNBT) { + super.saveNBTData(aNBT); + aNBT.setBoolean("disableSort", disableSort); + aNBT.setBoolean("disableFilter", disableFilter); + aNBT.setBoolean("disableLimited", disableLimited); + if (mRecipeMap != null) { + aNBT.setString("recipeMap", mRecipeMap.unlocalizedName); + } + } + + @Override + public void loadNBTData(NBTTagCompound aNBT) { + super.loadNBTData(aNBT); + disableSort = aNBT.getBoolean("disableSort"); + disableFilter = aNBT.getBoolean("disableFilter"); + if (aNBT.hasKey("disableLimited")) { + disableLimited = aNBT.getBoolean("disableLimited"); + } + mRecipeMap = RecipeMap.getFromOldIdentifier(aNBT.getString("recipeMap")); + } + + @Override + public void onScrewdriverRightClick(ForgeDirection side, EntityPlayer aPlayer, float aX, float aY, float aZ) { + if (!getBaseMetaTileEntity().getCoverInfoAtSide(side) + .isGUIClickable()) return; + if (aPlayer.isSneaking()) { + if (disableSort) { + disableSort = false; + } else { + if (disableLimited) { + disableLimited = false; + } else { + disableSort = true; + disableLimited = true; + } + } + GT_Utility.sendChatToPlayer( + aPlayer, + StatCollector.translateToLocal("GT5U.hatch.disableSort." + disableSort) + " " + + StatCollector.translateToLocal("GT5U.hatch.disableLimited." + disableLimited)); + } else { + disableFilter = !disableFilter; + GT_Utility + .sendChatToPlayer(aPlayer, StatCollector.translateToLocal("GT5U.hatch.disableFilter." + disableFilter)); + } + } + + @Override + public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side, + ItemStack aStack) { + if (aIndex == getCircuitSlot()) return false; + return side == getBaseMetaTileEntity().getFrontFacing(); + } + + @Override + public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side, + ItemStack aStack) { + return side == getBaseMetaTileEntity().getFrontFacing() && aIndex != getCircuitSlot() + && (mRecipeMap == null || disableFilter || mRecipeMap.containsInput(aStack)) + && (disableLimited || limitedAllowPutStack(aIndex, aStack)); + } + + protected boolean limitedAllowPutStack(int aIndex, ItemStack aStack) { + for (int i = 0; i < getSizeInventory(); i++) + if (GT_Utility.areStacksEqual(GT_OreDictUnificator.get_nocopy(aStack), mInventory[i])) return i == aIndex; + return mInventory[aIndex] == null; + } + + @Override + public boolean allowSelectCircuit() { + return true; + } + + @Override + public int getCircuitSlot() { + return getSlots(mTier); + } + + @Override + public boolean useModularUI() { + return true; + } + + private void addSortStacksButton(ModularWindow.Builder builder) { + builder.widget( + createToggleButton( + () -> !disableSort, + val -> disableSort = !val, + GT_UITextures.OVERLAY_BUTTON_SORTING_MODE, + () -> mTooltipCache.getData(SORTING_MODE_TOOLTIP))); + } + + private void addOneStackLimitButton(ModularWindow.Builder builder) { + builder.widget(createToggleButton(() -> !disableLimited, val -> { + disableLimited = !val; + updateSlots(); + }, GT_UITextures.OVERLAY_BUTTON_ONE_STACK_LIMIT, () -> mTooltipCache.getData(ONE_STACK_LIMIT_TOOLTIP))); + } + + @Override + public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) { + buildContext.addCloseListener(() -> uiButtonCount = 0); + addSortStacksButton(builder); + addOneStackLimitButton(builder); + switch (mTier) { + case 0 -> getBaseMetaTileEntity().add1by1Slot(builder); + case 1 -> getBaseMetaTileEntity().add2by2Slots(builder); + case 2 -> getBaseMetaTileEntity().add3by3Slots(builder); + default -> getBaseMetaTileEntity().add4by4Slots(builder); + } + } + + private Widget createToggleButton(Supplier<Boolean> getter, Consumer<Boolean> setter, UITexture picture, + Supplier<GT_TooltipDataCache.TooltipData> tooltipDataSupplier) { + return new CycleButtonWidget().setToggle(getter, setter) + .setStaticTexture(picture) + .setVariableBackground(GT_UITextures.BUTTON_STANDARD_TOGGLE) + .setTooltipShowUpDelay(TOOLTIP_DELAY) + .setPos(7 + (uiButtonCount++ * BUTTON_SIZE), 62) + .setSize(BUTTON_SIZE, BUTTON_SIZE) + .setGTTooltip(tooltipDataSupplier); + } +} diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_Maintenance.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_Maintenance.java new file mode 100644 index 0000000000..e1b5ee8f03 --- /dev/null +++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_Maintenance.java @@ -0,0 +1,464 @@ +package gregtech.api.metatileentity.implementations; + +import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_AUTOMAINTENANCE; +import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_AUTOMAINTENANCE_GLOW; +import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_AUTOMAINTENANCE_IDLE; +import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_AUTOMAINTENANCE_IDLE_GLOW; +import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_DUCTTAPE; +import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_MAINTENANCE; + +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.inventory.IInventory; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraftforge.common.util.FakePlayer; +import net.minecraftforge.common.util.ForgeDirection; + +import com.gtnewhorizon.structurelib.StructureLibAPI; +import com.gtnewhorizon.structurelib.alignment.IAlignment; +import com.gtnewhorizon.structurelib.alignment.IAlignmentLimits; +import com.gtnewhorizon.structurelib.alignment.IAlignmentProvider; +import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing; +import com.gtnewhorizon.structurelib.alignment.enumerable.Flip; +import com.gtnewhorizon.structurelib.alignment.enumerable.Rotation; +import com.gtnewhorizons.modularui.api.screen.ModularWindow; +import com.gtnewhorizons.modularui.api.screen.UIBuildContext; +import com.gtnewhorizons.modularui.common.internal.wrapper.BaseSlot; +import com.gtnewhorizons.modularui.common.widget.DrawableWidget; +import com.gtnewhorizons.modularui.common.widget.SlotWidget; +import com.gtnewhorizons.modularui.common.widget.TextWidget; + +import cpw.mods.fml.common.network.NetworkRegistry; +import gregtech.GT_Mod; +import gregtech.api.GregTech_API; +import gregtech.api.enums.ItemList; +import gregtech.api.enums.Materials; +import gregtech.api.enums.OrePrefixes; +import gregtech.api.gui.modularui.GT_UIInfos; +import gregtech.api.gui.modularui.GT_UITextures; +import gregtech.api.interfaces.ITexture; +import gregtech.api.interfaces.modularui.IAddUIWidgets; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.metatileentity.MetaTileEntity; +import gregtech.api.render.TextureFactory; +import gregtech.api.util.GT_ModHandler; +import gregtech.api.util.GT_OreDictUnificator; +import gregtech.api.util.GT_Utility; +import ic2.core.IHasGui; +import ic2.core.item.ItemToolbox; + +public class GT_MetaTileEntity_Hatch_Maintenance extends GT_MetaTileEntity_Hatch implements IAddUIWidgets, IAlignment { + + private Rotation rotation = Rotation.NORMAL; + + private static ItemStack[] sAutoMaintenanceInputs; + public boolean mWrench = false, mScrewdriver = false, mSoftHammer = false, mHardHammer = false, + mSolderingTool = false, mCrowbar = false, mAuto; + + public GT_MetaTileEntity_Hatch_Maintenance(int aID, String aName, String aNameRegional, int aTier) { + super(aID, aName, aNameRegional, aTier, 1, "For maintaining Multiblocks"); + mAuto = false; + } + + public GT_MetaTileEntity_Hatch_Maintenance(int aID, String aName, String aNameRegional, int aTier, boolean aAuto) { + super(aID, aName, aNameRegional, aTier, 4, "For automatically maintaining Multiblocks"); + mAuto = aAuto; + } + + public GT_MetaTileEntity_Hatch_Maintenance(String aName, int aTier, String aDescription, ITexture[][][] aTextures, + boolean aAuto) { + super(aName, aTier, aAuto ? 4 : 1, aDescription, aTextures); + mAuto = aAuto; + } + + public GT_MetaTileEntity_Hatch_Maintenance(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures, + boolean aAuto) { + super(aName, aTier, aAuto ? 4 : 1, aDescription, aTextures); + mAuto = aAuto; + } + + private static ItemStack[] getAutoMaintenanceInputs() { + if (sAutoMaintenanceInputs == null) sAutoMaintenanceInputs = new ItemStack[] { ItemList.Duct_Tape.get(4), + GT_OreDictUnificator.get(OrePrefixes.cell, Materials.Lubricant, 2), + GT_OreDictUnificator.get(OrePrefixes.screw, Materials.Steel, 4), + GT_OreDictUnificator.get(OrePrefixes.circuit, Materials.Advanced, 2) }; + return sAutoMaintenanceInputs; + } + + @Override + public String[] getDescription() { + String[] desc; + if (mAuto) { + desc = new String[mDescriptionArray.length + 3]; + System.arraycopy(mDescriptionArray, 0, desc, 0, mDescriptionArray.length); + desc[mDescriptionArray.length] = "4 Ducttape, 2 Lubricant Cells"; + desc[mDescriptionArray.length + 1] = "4 Steel Screws, 2 HV Circuits"; + desc[mDescriptionArray.length + 2] = "For each autorepair"; + } else { + desc = new String[mDescriptionArray.length + 1]; + System.arraycopy(mDescriptionArray, 0, desc, 0, mDescriptionArray.length); + desc[mDescriptionArray.length] = "Cannot be shared between Multiblocks!"; + } + return desc; + } + + @Override + public ITexture[] getTexturesActive(ITexture aBaseTexture) { + if (mAuto) return new ITexture[] { aBaseTexture, TextureFactory.builder() + .addIcon(OVERLAY_AUTOMAINTENANCE_IDLE) + .extFacing() + .build(), + TextureFactory.builder() + .addIcon(OVERLAY_AUTOMAINTENANCE_IDLE_GLOW) + .extFacing() + .glow() + .build() }; + return new ITexture[] { aBaseTexture, TextureFactory.builder() + .addIcon(OVERLAY_MAINTENANCE) + .extFacing() + .build() }; + } + + @Override + public ITexture[] getTexturesInactive(ITexture aBaseTexture) { + if (mAuto) return new ITexture[] { aBaseTexture, TextureFactory.builder() + .addIcon(OVERLAY_AUTOMAINTENANCE) + .extFacing() + .build(), + TextureFactory.builder() + .addIcon(OVERLAY_AUTOMAINTENANCE_GLOW) + .extFacing() + .glow() + .build() }; + return new ITexture[] { aBaseTexture, TextureFactory.builder() + .addIcon(OVERLAY_MAINTENANCE) + .extFacing() + .build(), + TextureFactory.builder() + .addIcon(OVERLAY_DUCTTAPE) + .extFacing() + .build() }; + } + + @Override + public void initDefaultModes(NBTTagCompound aNBT) { + getBaseMetaTileEntity().setActive(true); + } + + @Override + public boolean isSimpleMachine() { + return true; + } + + @Override + public boolean isFacingValid(ForgeDirection facing) { + return true; + } + + @Override + public boolean isAccessAllowed(EntityPlayer aPlayer) { + return true; + } + + @Override + public boolean isValidSlot(int aIndex) { + return mAuto && GT_Mod.gregtechproxy.mAMHInteraction; + } + + @Override + public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) { + if (aTileEntity.getMetaTileID() == 111) + return new GT_MetaTileEntity_Hatch_Maintenance(mName, mTier, mDescriptionArray, mTextures, true); + return new GT_MetaTileEntity_Hatch_Maintenance(mName, mTier, mDescriptionArray, mTextures, false); + } + + @Override + public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer, ForgeDirection side, + float aX, float aY, float aZ) { + if (aBaseMetaTileEntity.isClientSide()) return true; + if (side == aBaseMetaTileEntity.getFrontFacing()) { + // only allow OC robot fake player + if (aPlayer instanceof FakePlayer && !aPlayer.getGameProfile() + .getName() + .endsWith(".robot")) return false; + ItemStack tStack = aPlayer.getCurrentEquippedItem(); + if (tStack != null) { + if (tStack.getItem() instanceof ItemToolbox) { + applyToolbox(tStack, aPlayer); + } else if (ItemList.Duct_Tape.isStackEqual(tStack)) { + mWrench = mScrewdriver = mSoftHammer = mHardHammer = mCrowbar = mSolderingTool = true; + getBaseMetaTileEntity().setActive(false); + if (--tStack.stackSize == 0) { + aPlayer.inventory.mainInventory[aPlayer.inventory.currentItem] = null; + } + } else GT_UIInfos.openGTTileEntityUI(aBaseMetaTileEntity, aPlayer); + } else { + GT_UIInfos.openGTTileEntityUI(aBaseMetaTileEntity, aPlayer); + } + return true; + } + return false; + } + + public void updateSlots() { + for (int i = 0; i < mInventory.length; i++) + if (mInventory[i] != null && mInventory[i].stackSize <= 0) mInventory[i] = null; + } + + @Override + public void onFirstTick(IGregTechTileEntity aBaseMetaTileEntity) { + super.onFirstTick(aBaseMetaTileEntity); + if (aBaseMetaTileEntity.isClientSide()) + StructureLibAPI.queryAlignment((IAlignmentProvider) aBaseMetaTileEntity); + } + + @Override + public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) { + super.onPostTick(aBaseMetaTileEntity, aTick); + if (aBaseMetaTileEntity.isServerSide() && mAuto && aTick % 100L == 0L) { + aBaseMetaTileEntity.setActive(!isRecipeInputEqual(false)); + } + } + + @Override + public boolean onWrenchRightClick(ForgeDirection side, ForgeDirection wrenchingSide, EntityPlayer entityPlayer, + float aX, float aY, float aZ) { + if (wrenchingSide != getBaseMetaTileEntity().getFrontFacing()) + return super.onWrenchRightClick(side, wrenchingSide, entityPlayer, aX, aY, aZ); + if (!entityPlayer.isSneaking() && isRotationChangeAllowed()) { + toolSetRotation(null); + return true; + } + return false; + } + + public boolean autoMaintainance() { + return isRecipeInputEqual(true); + } + + public boolean isRecipeInputEqual(boolean aDecreaseStacksizeBySuccess) { + ItemStack[] mInputs = getAutoMaintenanceInputs(); + + int amt; + + for (ItemStack tStack : mInputs) { + if (tStack != null) { + amt = tStack.stackSize; + boolean temp = true; + for (ItemStack aStack : mInventory) { + if ((GT_Utility.areUnificationsEqual(aStack, tStack, true) + || GT_Utility.areUnificationsEqual(GT_OreDictUnificator.get(false, aStack), tStack, true))) { + amt -= aStack.stackSize; + if (amt < 1) { + temp = false; + break; + } + } + } + if (temp) return false; + } + } + + if (aDecreaseStacksizeBySuccess) { + for (ItemStack tStack : mInputs) { + if (tStack != null) { + amt = tStack.stackSize; + for (ItemStack aStack : mInventory) { + if ((GT_Utility.areUnificationsEqual(aStack, tStack, true) || GT_Utility + .areUnificationsEqual(GT_OreDictUnificator.get(false, aStack), tStack, true))) { + if (aStack.stackSize < amt) { + amt -= aStack.stackSize; + aStack.stackSize = 0; + } else { + aStack.stackSize -= amt; + amt = 0; + break; + } + } + } + } + } + mCrowbar = true; + mHardHammer = true; + mScrewdriver = true; + mSoftHammer = true; + mSolderingTool = true; + mWrench = true; + updateSlots(); + } + return true; + } + + public void onToolClick(ItemStack aStack, EntityLivingBase aPlayer, IInventory aToolboxInventory) { + if (aStack == null || aPlayer == null) return; + + // Allow IC2 Toolbox with tools to function for maint issues. + if (aStack.getItem() instanceof ItemToolbox && aPlayer instanceof EntityPlayer) { + applyToolbox(aStack, (EntityPlayer) aPlayer); + return; + } + + if (GT_Utility.isStackInList(aStack, GregTech_API.sWrenchList) && !mWrench + && GT_ModHandler.damageOrDechargeItem(aStack, 1, 1000, aPlayer)) mWrench = true; + if (GT_Utility.isStackInList(aStack, GregTech_API.sScrewdriverList) && !mScrewdriver + && GT_ModHandler.damageOrDechargeItem(aStack, 1, 1000, aPlayer)) mScrewdriver = true; + if (GT_Utility.isStackInList(aStack, GregTech_API.sSoftHammerList) && !mSoftHammer + && GT_ModHandler.damageOrDechargeItem(aStack, 1, 1000, aPlayer)) mSoftHammer = true; + if (GT_Utility.isStackInList(aStack, GregTech_API.sHardHammerList) && !mHardHammer + && GT_ModHandler.damageOrDechargeItem(aStack, 1, 1000, aPlayer)) mHardHammer = true; + if (GT_Utility.isStackInList(aStack, GregTech_API.sCrowbarList) && !mCrowbar + && GT_ModHandler.damageOrDechargeItem(aStack, 1, 1000, aPlayer)) mCrowbar = true; + if (!mSolderingTool && GT_ModHandler.useSolderingIron(aStack, aPlayer, aToolboxInventory)) + mSolderingTool = true; + if (GT_OreDictUnificator.isItemStackInstanceOf(aStack, "craftingDuctTape")) { + mWrench = mScrewdriver = mSoftHammer = mHardHammer = mCrowbar = mSolderingTool = true; + getBaseMetaTileEntity().setActive(false); + aStack.stackSize--; + } + if (mSolderingTool && aPlayer instanceof EntityPlayerMP tPlayer) { + try { + GT_Mod.achievements.issueAchievement(tPlayer, "maintainance"); + } catch (Exception ignored) {} + } + } + + public void onToolClick(ItemStack aStack, EntityLivingBase aPlayer) { + onToolClick(aStack, aPlayer, null); + } + + private void applyToolbox(ItemStack aStack, EntityPlayer aPlayer) { + final ItemToolbox aToolbox = (ItemToolbox) aStack.getItem(); + final IHasGui aToolboxGUI = aToolbox.getInventory(aPlayer, aStack); + for (int i = 0; i < aToolboxGUI.getSizeInventory(); i++) { + if (aToolboxGUI.getStackInSlot(i) != null) { + onToolClick(aToolboxGUI.getStackInSlot(i), aPlayer, aToolboxGUI); + if (aToolboxGUI.getStackInSlot(i) != null && aToolboxGUI.getStackInSlot(i).stackSize <= 0) + aToolboxGUI.setInventorySlotContents(i, null); + } + } + } + + @Override + public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side, + ItemStack aStack) { + return mAuto && GT_Mod.gregtechproxy.mAMHInteraction; + } + + @Override + public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side, + ItemStack aStack) { + if (mAuto && GT_Mod.gregtechproxy.mAMHInteraction) { + for (int i = 0; i < getSizeInventory(); i++) if (GT_Utility.areStacksEqual( + GT_OreDictUnificator.get(false, aStack), + GT_OreDictUnificator.get(false, getStackInSlot(i)))) return i == aIndex; + for (ItemStack tInput : getAutoMaintenanceInputs()) + if (GT_Utility.areUnificationsEqual(tInput, aStack, true) + || GT_Utility.areUnificationsEqual(GT_OreDictUnificator.get(false, aStack), tInput, true)) + return true; + } + return false; + } + + @Override + public boolean useModularUI() { + return true; + } + + @Override + public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) { + if (mAuto) { + getBaseMetaTileEntity().add2by2Slots(builder); + } else { + builder.widget( + new DrawableWidget().setDrawable(GT_UITextures.SLOT_MAINTENANCE) + .setPos(78, 33) + .setSize(20, 20)) + .widget(new SlotWidget(BaseSlot.empty()) { + + @Override + public boolean handleDragAndDrop(ItemStack draggedStack, int button) { + return false; + } + + @Override + protected void phantomClick(ClickData clickData, ItemStack cursorStack) { + if (cursorStack == null) return; + onToolClick(cursorStack, getContext().getPlayer()); + if (cursorStack.stackSize < 1) { + getContext().getPlayer().inventory.setItemStack(null); + } + if (getContext().getPlayer() instanceof EntityPlayerMP) { + ((EntityPlayerMP) getContext().getPlayer()).updateHeldItem(); + } + } + }.disableShiftInsert() + .setBackground(GT_UITextures.TRANSPARENT) + .setPos(79, 34)) + .widget( + new TextWidget("Click with Tool to repair.").setDefaultColor(COLOR_TEXT_GRAY.get()) + .setPos(8, 12)); + } + } + + @Override + public void saveNBTData(NBTTagCompound aNBT) { + super.saveNBTData(aNBT); + aNBT.setByte("mRotation", (byte) rotation.getIndex()); + } + + @Override + public void loadNBTData(NBTTagCompound aNBT) { + rotation = Rotation.byIndex(aNBT.getByte("mRotation")); + super.loadNBTData(aNBT); + } + + @Override + public ExtendedFacing getExtendedFacing() { + return ExtendedFacing.of(getBaseMetaTileEntity().getFrontFacing(), rotation, Flip.NONE); + } + + @Override + public void setExtendedFacing(ExtendedFacing alignment) { + boolean changed = false; + final IGregTechTileEntity base = getBaseMetaTileEntity(); + if (base.getFrontFacing() != alignment.getDirection()) { + base.setFrontFacing(alignment.getDirection()); + changed = true; + } + if (rotation != alignment.getRotation()) { + rotation = alignment.getRotation(); + changed = true; + } + if (changed) { + if (base.isServerSide() && !GregTech_API.isDummyWorld(base.getWorld())) { + StructureLibAPI.sendAlignment( + (IAlignmentProvider) base, + new NetworkRegistry.TargetPoint( + base.getWorld().provider.dimensionId, + base.getXCoord(), + base.getYCoord(), + base.getZCoord(), + 512)); + } else { + base.issueTextureUpdate(); + } + } + } + + @Override + public IAlignmentLimits getAlignmentLimits() { + return (d, r, f) -> f.isNotFlipped(); + } + + @Override + public boolean isFlipChangeAllowed() { + return false; + } + + @Override + public boolean isRotationChangeAllowed() { + return true; + } +} diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_Muffler.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_Muffler.java new file mode 100644 index 0000000000..8707d0f804 --- /dev/null +++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_Muffler.java @@ -0,0 +1,208 @@ +package gregtech.api.metatileentity.implementations; + +import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_MUFFLER; +import static gregtech.api.objects.XSTR.XSTR_INSTANCE; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.world.World; +import net.minecraftforge.common.util.ForgeDirection; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import gregtech.GT_Mod; +import gregtech.api.enums.ParticleFX; +import gregtech.api.interfaces.ITexture; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.metatileentity.MetaTileEntity; +import gregtech.api.render.TextureFactory; +import gregtech.api.util.GT_LanguageManager; +import gregtech.api.util.WorldSpawnedEventBuilder; +import gregtech.common.GT_Pollution; + +@SuppressWarnings("unused") // Unused API is expected within scope +public class GT_MetaTileEntity_Hatch_Muffler extends GT_MetaTileEntity_Hatch { + + private static final String localizedDescFormat = GT_LanguageManager.addStringLocalization( + "gt.blockmachines.hatch.muffler.desc.format", + "Outputs the Pollution (Might cause ... things)%n" + "DO NOT OBSTRUCT THE OUTPUT!%n" + + "Reduces Pollution to %d%%%n" + + "Recovers %d%% of CO2/CO/SO2"); + private final int pollutionReduction = calculatePollutionReduction(100); + private final int pollutionRecover = 100 - pollutionReduction; + private final String[] description = String.format(localizedDescFormat, pollutionReduction, pollutionRecover) + .split("\\R"); + + public GT_MetaTileEntity_Hatch_Muffler(int aID, String aName, String aNameRegional, int aTier) { + super(aID, aName, aNameRegional, aTier, 0, ""); + } + + public GT_MetaTileEntity_Hatch_Muffler(int aID, String aName, String aNameRegional, int aTier, int aInvSlotCount, + String[] aDescription, ITexture... aTextures) { + super(aID, aName, aNameRegional, aTier, aInvSlotCount, aDescription, aTextures); + } + + public GT_MetaTileEntity_Hatch_Muffler(String aName, int aTier, String aDescription, ITexture[][][] aTextures) { + this(aName, aTier, new String[] { aDescription }, aTextures); + } + + public GT_MetaTileEntity_Hatch_Muffler(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures) { + this(aName, aTier, 0, aDescription, aTextures); + } + + public GT_MetaTileEntity_Hatch_Muffler(String aName, int aTier, int aInvSlotCount, String[] aDescription, + ITexture[][][] aTextures) { + super(aName, aTier, aInvSlotCount, aDescription, aTextures); + } + + @Override + public String[] getDescription() { + return description; + } + + @Override + public ITexture[] getTexturesActive(ITexture aBaseTexture) { + return new ITexture[] { aBaseTexture, TextureFactory.of(OVERLAY_MUFFLER) }; + } + + @Override + public ITexture[] getTexturesInactive(ITexture aBaseTexture) { + return new ITexture[] { aBaseTexture, TextureFactory.of(OVERLAY_MUFFLER) }; + } + + @Override + public boolean isSimpleMachine() { + return true; + } + + @Override + public boolean isValidSlot(int aIndex) { + return false; + } + + @Override + public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side, + ItemStack aStack) { + return false; + } + + @Override + public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side, + ItemStack aStack) { + return false; + } + + @Override + public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) { + return new GT_MetaTileEntity_Hatch_Muffler(mName, mTier, mDescriptionArray, mTextures); + } + + @Override + public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) { + super.onPostTick(aBaseMetaTileEntity, aTick); + if (aBaseMetaTileEntity.isClientSide() && this.getBaseMetaTileEntity() + .isActive()) { + pollutionParticles( + this.getBaseMetaTileEntity() + .getWorld(), + ParticleFX.LARGE_SMOKE.toString()); + } + } + + @Override + public boolean isFacingValid(ForgeDirection facing) { + return true; + } + + @Override + public boolean isAccessAllowed(EntityPlayer aPlayer) { + return true; + } + + @SideOnly(Side.CLIENT) + public void pollutionParticles(World aWorld, String name) { + boolean chk1, chk2, chk3; + float ran1 = XSTR_INSTANCE.nextFloat(), ran2, ran3; + chk1 = ran1 * 100 < calculatePollutionReduction(100); + if (GT_Pollution.getPollution(getBaseMetaTileEntity()) >= GT_Mod.gregtechproxy.mPollutionSmogLimit) { + ran2 = XSTR_INSTANCE.nextFloat(); + ran3 = XSTR_INSTANCE.nextFloat(); + chk2 = ran2 * 100 < calculatePollutionReduction(100); + chk3 = ran3 * 100 < calculatePollutionReduction(100); + if (!(chk1 || chk2 || chk3)) return; + } else { + if (!chk1) return; + ran2 = ran3 = 0.0F; + chk2 = chk3 = false; + } + + final IGregTechTileEntity aMuffler = this.getBaseMetaTileEntity(); + final ForgeDirection aDir = aMuffler.getFrontFacing(); + final float xPos = aDir.offsetX * 0.76F + aMuffler.getXCoord() + 0.25F; + final float yPos = aDir.offsetY * 0.76F + aMuffler.getYCoord() + 0.25F; + final float zPos = aDir.offsetZ * 0.76F + aMuffler.getZCoord() + 0.25F; + + final float ySpd = aDir.offsetY * 0.1F + 0.2F + 0.1F * XSTR_INSTANCE.nextFloat(); + final float xSpd; + final float zSpd; + + if (aDir.offsetY == -1) { + final float temp = XSTR_INSTANCE.nextFloat() * 2 * (float) Math.PI; + xSpd = (float) Math.sin(temp) * 0.1F; + zSpd = (float) Math.cos(temp) * 0.1F; + } else { + xSpd = aDir.offsetX * (0.1F + 0.2F * XSTR_INSTANCE.nextFloat()); + zSpd = aDir.offsetZ * (0.1F + 0.2F * XSTR_INSTANCE.nextFloat()); + } + + final WorldSpawnedEventBuilder.ParticleEventBuilder events = new WorldSpawnedEventBuilder.ParticleEventBuilder() + .setIdentifier(name) + .setWorld(aWorld) + .setMotion(xSpd, ySpd, zSpd); + + if (chk1) { + events + .setPosition( + xPos + ran1 * 0.5F, + yPos + XSTR_INSTANCE.nextFloat() * 0.5F, + zPos + XSTR_INSTANCE.nextFloat() * 0.5F) + .run(); + } + if (chk2) { + events + .setPosition( + xPos + ran2 * 0.5F, + yPos + XSTR_INSTANCE.nextFloat() * 0.5F, + zPos + XSTR_INSTANCE.nextFloat() * 0.5F) + .run(); + } + if (chk3) { + events + .setPosition( + xPos + ran3 * 0.5F, + yPos + XSTR_INSTANCE.nextFloat() * 0.5F, + zPos + XSTR_INSTANCE.nextFloat() * 0.5F) + .run(); + } + } + + public int calculatePollutionReduction(int aPollution) { + if (mTier < 2) { + return aPollution; + } + return (int) ((float) aPollution * ((100F - 12.5F * ((float) mTier - 1F)) / 100F)); + } + + /** + * @param mte The multi-block controller's {@link MetaTileEntity} MetaTileEntity is passed so newer muffler hatches + * can do wacky things with the multis + * @return pollution success + */ + public boolean polluteEnvironment(MetaTileEntity mte) { + if (getBaseMetaTileEntity().getAirAtSide(getBaseMetaTileEntity().getFrontFacing())) { + GT_Pollution.addPollution(getBaseMetaTileEntity(), calculatePollutionReduction(10000)); + return true; + } + return false; + } +} diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_MultiInput.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_MultiInput.java new file mode 100644 index 0000000000..cea2e25baa --- /dev/null +++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_MultiInput.java @@ -0,0 +1,305 @@ +package gregtech.api.metatileentity.implementations; + +import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_INPUT_HATCH_2x2; + +import net.minecraft.nbt.NBTTagCompound; +import net.minecraftforge.common.util.ForgeDirection; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.FluidTankInfo; + +import com.gtnewhorizons.modularui.api.ModularUITextures; +import com.gtnewhorizons.modularui.api.math.Pos2d; +import com.gtnewhorizons.modularui.api.screen.ModularWindow; +import com.gtnewhorizons.modularui.api.screen.UIBuildContext; +import com.gtnewhorizons.modularui.common.fluid.FluidStackTank; +import com.gtnewhorizons.modularui.common.widget.FluidSlotWidget; + +import gregtech.api.interfaces.ITexture; +import gregtech.api.interfaces.modularui.IAddUIWidgets; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.metatileentity.MetaTileEntity; +import gregtech.api.render.TextureFactory; + +public class GT_MetaTileEntity_Hatch_MultiInput extends GT_MetaTileEntity_Hatch_Input implements IAddUIWidgets { + + private final FluidStack[] mStoredFluid; + private final FluidStackTank[] fluidTanks; + public final int mCapacityPer; + + public GT_MetaTileEntity_Hatch_MultiInput(int aID, int aSlot, String aName, String aNameRegional, int aTier) { + super(aID, aSlot, aName, aNameRegional, aTier); + this.mStoredFluid = new FluidStack[aSlot]; + fluidTanks = new FluidStackTank[aSlot]; + mCapacityPer = getCapacityPerTank(aTier, aSlot); + } + + public GT_MetaTileEntity_Hatch_MultiInput(int aID, int aSlot, String aName, String aNameRegional, int aTier, + String[] aDescription) { + super(aID, aSlot, aName, aNameRegional, aTier, aDescription); + this.mStoredFluid = new FluidStack[aSlot]; + fluidTanks = new FluidStackTank[aSlot]; + mCapacityPer = getCapacityPerTank(aTier, aSlot); + } + + public GT_MetaTileEntity_Hatch_MultiInput(String aName, int aSlot, int aTier, String[] aDescription, + ITexture[][][] aTextures) { + super(aName, aSlot, aTier, aDescription, aTextures); + this.mStoredFluid = new FluidStack[aSlot]; + fluidTanks = new FluidStackTank[aSlot]; + mCapacityPer = getCapacityPerTank(aTier, aSlot); + for (int i = 0; i < aSlot; i++) { + final int index = i; + fluidTanks[i] = new FluidStackTank( + () -> mStoredFluid[index], + fluid -> mStoredFluid[index] = fluid, + mCapacityPer); + } + } + + @Override + public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) { + return new GT_MetaTileEntity_Hatch_MultiInput(mName, getMaxType(), mTier, mDescriptionArray, mTextures); + } + + @Override + public void saveNBTData(NBTTagCompound aNBT) { + super.saveNBTData(aNBT); + if (mStoredFluid != null) { + for (int i = 0; i < mStoredFluid.length; i++) { + if (mStoredFluid[i] != null) + aNBT.setTag("mFluid" + i, mStoredFluid[i].writeToNBT(new NBTTagCompound())); + } + } + } + + @Override + public void loadNBTData(NBTTagCompound aNBT) { + super.loadNBTData(aNBT); + if (mStoredFluid != null) { + for (int i = 0; i < mStoredFluid.length; i++) { + if (aNBT.hasKey("mFluid" + i)) { + mStoredFluid[i] = FluidStack.loadFluidStackFromNBT(aNBT.getCompoundTag("mFluid" + i)); + } + } + } + } + + @Override + public boolean displaysStackSize() { + return true; + } + + public FluidStack[] getStoredFluid() { + return mStoredFluid; + } + + @Override + public ITexture[] getTexturesActive(ITexture aBaseTexture) { + return new ITexture[] { aBaseTexture, TextureFactory.of(OVERLAY_INPUT_HATCH_2x2) }; + } + + @Override + public ITexture[] getTexturesInactive(ITexture aBaseTexture) { + return new ITexture[] { aBaseTexture, TextureFactory.of(OVERLAY_INPUT_HATCH_2x2) }; + } + + public int getMaxType() { + return mStoredFluid.length; + } + + @Override + public FluidStack getFluid() { + for (FluidStack tFluid : mStoredFluid) { + if (tFluid != null && tFluid.amount > 0) return tFluid; + } + return null; + } + + public FluidStack getFluid(int aSlot) { + if (mStoredFluid == null || aSlot < 0 || aSlot >= getMaxType()) return null; + return mStoredFluid[aSlot]; + } + + @Override + public int getFluidAmount() { + if (getFluid() != null) { + return getFluid().amount; + } + return 0; + } + + @Override + public int getCapacity() { + return mCapacityPer; + } + + public int getFirstEmptySlot() { + for (int i = 0; i < mStoredFluid.length; i++) { + if (mStoredFluid[i] == null) return i; + } + return -1; + } + + public boolean hasFluid(FluidStack aFluid) { + if (aFluid == null) return false; + for (FluidStack tFluid : mStoredFluid) { + if (aFluid.isFluidEqual(tFluid)) return true; + } + return false; + } + + public int getFluidSlot(FluidStack tFluid) { + if (tFluid == null) return -1; + for (int i = 0; i < mStoredFluid.length; i++) { + if (tFluid.equals(mStoredFluid[i])) return i; + } + return -1; + } + + public int getFluidAmount(FluidStack tFluid) { + int tSlot = getFluidSlot(tFluid); + if (tSlot != -1) { + return mStoredFluid[tSlot].amount; + } + return 0; + } + + public void setFluid(FluidStack aFluid, int aSlot) { + if (aSlot < 0 || aSlot >= getMaxType()) return; + mStoredFluid[aSlot] = aFluid; + } + + public void addFluid(FluidStack aFluid, int aSlot) { + if (aSlot < 0 || aSlot >= getMaxType()) return; + if (aFluid.equals(mStoredFluid[aSlot])) mStoredFluid[aSlot].amount += aFluid.amount; + if (mStoredFluid[aSlot] == null) mStoredFluid[aSlot] = aFluid.copy(); + } + + @Override + public void onPreTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) { + if (aBaseMetaTileEntity.isServerSide()) { + mFluid = getFluid(); + } + super.onPreTick(aBaseMetaTileEntity, aTick); + } + + @Override + public int fill(FluidStack aFluid, boolean doFill) { + if (aFluid == null || aFluid.getFluid() + .getID() <= 0 || aFluid.amount <= 0 || !canTankBeFilled() || !isFluidInputAllowed(aFluid)) return 0; + if (!hasFluid(aFluid) && getFirstEmptySlot() != -1) { + int tFilled = Math.min(aFluid.amount, mCapacityPer); + if (doFill) { + FluidStack tFluid = aFluid.copy(); + tFluid.amount = tFilled; + addFluid(tFluid, getFirstEmptySlot()); + getBaseMetaTileEntity().markDirty(); + } + return tFilled; + } + if (hasFluid(aFluid)) { + int tLeft = mCapacityPer - getFluidAmount(aFluid); + int tFilled = Math.min(tLeft, aFluid.amount); + if (doFill) { + FluidStack tFluid = aFluid.copy(); + tFluid.amount = tFilled; + addFluid(tFluid, getFluidSlot(tFluid)); + getBaseMetaTileEntity().markDirty(); + } + return tFilled; + } + return 0; + } + + @Override + public FluidStack drain(int maxDrain, boolean doDrain) { + if (getFluid() == null || !canTankBeEmptied()) return null; + if (getFluid().amount <= 0 && isFluidChangingAllowed()) { + setFluid(null, getFluidSlot(getFluid())); + getBaseMetaTileEntity().markDirty(); + return null; + } + FluidStack tRemove = getFluid().copy(); + tRemove.amount = Math.min(maxDrain, tRemove.amount); + if (doDrain) { + getFluid().amount -= tRemove.amount; + getBaseMetaTileEntity().markDirty(); + } + if (getFluid() == null || getFluid().amount <= 0 && isFluidChangingAllowed()) { + setFluid(null, getFluidSlot(getFluid())); + getBaseMetaTileEntity().markDirty(); + } + return tRemove; + } + + @Override + public int fill(ForgeDirection from, FluidStack resource, boolean doFill) { + return fill(resource, doFill); + } + + @Override + public FluidStack drain(ForgeDirection from, FluidStack aFluid, boolean doDrain) { + if (aFluid == null || !hasFluid(aFluid)) return null; + FluidStack tStored = mStoredFluid[getFluidSlot(aFluid)]; + if (tStored.amount <= 0 && isFluidChangingAllowed()) { + setFluid(null, getFluidSlot(tStored)); + getBaseMetaTileEntity().markDirty(); + return null; + } + FluidStack tRemove = tStored.copy(); + tRemove.amount = Math.min(aFluid.amount, tRemove.amount); + if (doDrain) { + tStored.amount -= tRemove.amount; + getBaseMetaTileEntity().markDirty(); + } + if (tStored.amount <= 0 && isFluidChangingAllowed()) { + setFluid(null, getFluidSlot(tStored)); + getBaseMetaTileEntity().markDirty(); + } + return tRemove; + } + + @Override + public FluidTankInfo[] getTankInfo(ForgeDirection from) { + FluidTankInfo[] FTI = new FluidTankInfo[getMaxType()]; + for (int i = 0; i < getMaxType(); i++) { + FTI[i] = new FluidTankInfo(mStoredFluid[i], mCapacityPer); + } + return FTI; + } + + @Override + public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) { + if (aBaseMetaTileEntity.isServerSide() && mStoredFluid != null) { + for (int i = 0; i < getMaxType(); i++) { + if (mStoredFluid[i] != null && mStoredFluid[i].amount <= 0) { + mStoredFluid[i] = null; + } + } + } + super.onPostTick(aBaseMetaTileEntity, aTick); + } + + @Override + public boolean isValidSlot(int aIndex) { + return aIndex >= 4; + } + + @Override + public boolean useModularUI() { + return true; + } + + @Override + public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) { + final int SLOT_NUMBER = 4; + final Pos2d[] positions = new Pos2d[] { new Pos2d(70, 25), new Pos2d(88, 25), new Pos2d(70, 43), + new Pos2d(88, 43), }; + + for (int i = 0; i < SLOT_NUMBER; i++) { + builder.widget( + new FluidSlotWidget(fluidTanks[i]).setBackground(ModularUITextures.FLUID_SLOT) + .setPos(positions[i])); + } + } +} diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_Output.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_Output.java new file mode 100644 index 0000000000..fd83f6899b --- /dev/null +++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_Output.java @@ -0,0 +1,502 @@ +package gregtech.api.metatileentity.implementations; + +import static gregtech.api.enums.Textures.BlockIcons.FLUID_OUT_SIGN; +import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_PIPE_OUT; + +import java.lang.ref.WeakReference; + +import javax.annotation.Nonnull; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.StatCollector; +import net.minecraftforge.common.util.ForgeDirection; +import net.minecraftforge.fluids.FluidContainerRegistry; +import net.minecraftforge.fluids.FluidRegistry; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.IFluidContainerItem; +import net.minecraftforge.fluids.IFluidHandler; + +import com.gtnewhorizons.modularui.api.math.Alignment; +import com.gtnewhorizons.modularui.api.screen.ModularWindow; +import com.gtnewhorizons.modularui.api.screen.UIBuildContext; +import com.gtnewhorizons.modularui.common.widget.DrawableWidget; +import com.gtnewhorizons.modularui.common.widget.FakeSyncWidget; +import com.gtnewhorizons.modularui.common.widget.TextWidget; + +import gregtech.GT_Mod; +import gregtech.api.gui.modularui.GT_UIInfos; +import gregtech.api.gui.modularui.GT_UITextures; +import gregtech.api.interfaces.ITexture; +import gregtech.api.interfaces.fluid.IFluidStore; +import gregtech.api.interfaces.metatileentity.IFluidLockable; +import gregtech.api.interfaces.modularui.IAddUIWidgets; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.metatileentity.MetaTileEntity; +import gregtech.api.render.TextureFactory; +import gregtech.api.util.GT_ModHandler; +import gregtech.api.util.GT_Utility; +import gregtech.common.gui.modularui.widget.FluidLockWidget; + +public class GT_MetaTileEntity_Hatch_Output extends GT_MetaTileEntity_Hatch + implements IFluidStore, IFluidLockable, IAddUIWidgets { + + private String lockedFluidName = null; + private WeakReference<EntityPlayer> playerThatLockedfluid = null; + public byte mMode = 0; + + public GT_MetaTileEntity_Hatch_Output(int aID, String aName, String aNameRegional, int aTier) { + super( + aID, + aName, + aNameRegional, + aTier, + 4, + new String[] { "Fluid Output for Multiblocks", + "Capacity: " + GT_Utility.formatNumbers(8000L * (1L << aTier)) + "L", + "Right click with screwdriver to restrict output", + "Can be restricted to put out Items and/or Steam/No Steam/1 specific Fluid", + "Restricted Output Hatches are given priority for Multiblock Fluid output" }); + } + + public GT_MetaTileEntity_Hatch_Output(String aName, int aTier, String aDescription, ITexture[][][] aTextures) { + super(aName, aTier, 4, aDescription, aTextures); + } + + public GT_MetaTileEntity_Hatch_Output(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures) { + super(aName, aTier, 4, aDescription, aTextures); + } + + public GT_MetaTileEntity_Hatch_Output(int aID, String aName, String aNameRegional, int aTier, String[] aDescription, + int inventorySize) { + super(aID, aName, aNameRegional, aTier, inventorySize, aDescription); + } + + public GT_MetaTileEntity_Hatch_Output(String name, int tier, int slots, String[] description, + ITexture[][][] textures) { + super(name, tier, slots, description, textures); + } + + @Override + public ITexture[] getTexturesActive(ITexture aBaseTexture) { + return GT_Mod.gregtechproxy.mRenderIndicatorsOnHatch + ? new ITexture[] { aBaseTexture, TextureFactory.of(OVERLAY_PIPE_OUT), TextureFactory.of(FLUID_OUT_SIGN) } + : new ITexture[] { aBaseTexture, TextureFactory.of(OVERLAY_PIPE_OUT) }; + } + + @Override + public ITexture[] getTexturesInactive(ITexture aBaseTexture) { + return GT_Mod.gregtechproxy.mRenderIndicatorsOnHatch + ? new ITexture[] { aBaseTexture, TextureFactory.of(OVERLAY_PIPE_OUT), TextureFactory.of(FLUID_OUT_SIGN) } + : new ITexture[] { aBaseTexture, TextureFactory.of(OVERLAY_PIPE_OUT) }; + } + + @Override + public boolean isSimpleMachine() { + return true; + } + + @Override + public boolean isFacingValid(ForgeDirection facing) { + return true; + } + + @Override + public boolean isAccessAllowed(EntityPlayer aPlayer) { + return true; + } + + @Override + public boolean isLiquidInput(ForgeDirection side) { + return false; + } + + @Override + public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) { + return new GT_MetaTileEntity_Hatch_Output(mName, mTier, mDescriptionArray, mTextures); + } + + @Override + public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer) { + GT_UIInfos.openGTTileEntityUI(aBaseMetaTileEntity, aPlayer); + return true; + } + + @Override + public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) { + super.onPostTick(aBaseMetaTileEntity, aTick); + if (aBaseMetaTileEntity.isServerSide() && aBaseMetaTileEntity.isAllowedToWork() && mFluid != null) { + IFluidHandler tTileEntity = aBaseMetaTileEntity + .getITankContainerAtSide(aBaseMetaTileEntity.getFrontFacing()); + if (tTileEntity != null) { + GT_Utility.moveFluid( + aBaseMetaTileEntity, + tTileEntity, + aBaseMetaTileEntity.getFrontFacing(), + Math.max(1, mFluid.amount), + null); + } + } + } + + @Override + public void saveNBTData(NBTTagCompound aNBT) { + super.saveNBTData(aNBT); + aNBT.setByte("mMode", mMode); + if (isFluidLocked() && lockedFluidName != null && lockedFluidName.length() != 0) + aNBT.setString("lockedFluidName", lockedFluidName); + else aNBT.removeTag("lockedFluidName"); + } + + @Override + public void loadNBTData(NBTTagCompound aNBT) { + super.loadNBTData(aNBT); + mMode = aNBT.getByte("mMode"); + if (isFluidLocked()) { + lockedFluidName = aNBT.getString("lockedFluidName"); + } + lockedFluidName = GT_Utility.isStringInvalid(lockedFluidName) ? null : lockedFluidName; + } + + @Override + public boolean doesFillContainers() { + return true; + } + + @Override + public boolean doesEmptyContainers() { + return false; + } + + @Override + public boolean canTankBeFilled() { + return true; + } + + @Override + public boolean canTankBeEmptied() { + return true; + } + + @Override + public boolean displaysItemStack() { + return true; + } + + @Override + public boolean displaysStackSize() { + return false; + } + + public int getLockedDisplaySlot() { + return 3; + } + + @Override + public boolean isValidSlot(int aIndex) { + // Because getStackDisplaySlot() only allow return one int, this place I only can manually set. + return aIndex != getStackDisplaySlot() && aIndex != getLockedDisplaySlot(); + } + + @Override + public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side, + ItemStack aStack) { + return side == aBaseMetaTileEntity.getFrontFacing() && aIndex == 1; + } + + @Override + public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side, + ItemStack aStack) { + return side == aBaseMetaTileEntity.getFrontFacing() && aIndex == 0; + } + + @Override + public int getCapacity() { + return 8000 * (1 << mTier); + } + + @Override + public void onScrewdriverRightClick(ForgeDirection side, EntityPlayer aPlayer, float aX, float aY, float aZ) { + if (!getBaseMetaTileEntity().getCoverInfoAtSide(side) + .isGUIClickable()) return; + if (aPlayer.isSneaking()) { + mMode = (byte) ((mMode + 9) % 10); + } else { + mMode = (byte) ((mMode + 1) % 10); + } + final String inBrackets; + switch (mMode) { + case 0 -> { + GT_Utility.sendChatToPlayer(aPlayer, GT_Utility.trans("108", "Outputs misc. Fluids, Steam and Items")); + this.setLockedFluidName(null); + } + case 1 -> { + GT_Utility.sendChatToPlayer(aPlayer, GT_Utility.trans("109", "Outputs Steam and Items")); + this.setLockedFluidName(null); + } + case 2 -> { + GT_Utility.sendChatToPlayer(aPlayer, GT_Utility.trans("110", "Outputs Steam and misc. Fluids")); + this.setLockedFluidName(null); + } + case 3 -> { + GT_Utility.sendChatToPlayer(aPlayer, GT_Utility.trans("111", "Outputs Steam")); + this.setLockedFluidName(null); + } + case 4 -> { + GT_Utility.sendChatToPlayer(aPlayer, GT_Utility.trans("112", "Outputs misc. Fluids and Items")); + this.setLockedFluidName(null); + } + case 5 -> { + GT_Utility.sendChatToPlayer(aPlayer, GT_Utility.trans("113", "Outputs only Items")); + this.setLockedFluidName(null); + } + case 6 -> { + GT_Utility.sendChatToPlayer(aPlayer, GT_Utility.trans("114", "Outputs only misc. Fluids")); + this.setLockedFluidName(null); + } + case 7 -> { + GT_Utility.sendChatToPlayer(aPlayer, GT_Utility.trans("115", "Outputs nothing")); + this.setLockedFluidName(null); + } + case 8 -> { + playerThatLockedfluid = new WeakReference<>(aPlayer); + if (mFluid == null) { + this.setLockedFluidName(null); + inBrackets = GT_Utility.trans( + "115.3", + "currently none, will be locked to the next that is put in (or use fluid cell to lock)"); + } else { + this.setLockedFluidName( + this.getDrainableStack() + .getFluid() + .getName()); + inBrackets = this.getDrainableStack() + .getLocalizedName(); + } + GT_Utility.sendChatToPlayer( + aPlayer, + String.format( + "%s (%s)", + GT_Utility.trans("151.1", "Outputs items and 1 specific Fluid"), + inBrackets)); + } + case 9 -> { + playerThatLockedfluid = new WeakReference<>(aPlayer); + if (mFluid == null) { + this.setLockedFluidName(null); + inBrackets = GT_Utility.trans( + "115.3", + "currently none, will be locked to the next that is put in (or use fluid cell to lock)"); + } else { + this.setLockedFluidName( + this.getDrainableStack() + .getFluid() + .getName()); + inBrackets = this.getDrainableStack() + .getLocalizedName(); + } + GT_Utility.sendChatToPlayer( + aPlayer, + String.format("%s (%s)", GT_Utility.trans("151.2", "Outputs 1 specific Fluid"), inBrackets)); + } + } + } + + private boolean tryToLockHatch(EntityPlayer aPlayer, ForgeDirection side) { + if (!getBaseMetaTileEntity().getCoverInfoAtSide(side) + .isGUIClickable()) return false; + if (!isFluidLocked()) return false; + final ItemStack tCurrentItem = aPlayer.inventory.getCurrentItem(); + if (tCurrentItem == null) return false; + FluidStack tFluid = FluidContainerRegistry.getFluidForFilledItem(tCurrentItem); + if (tFluid == null && tCurrentItem.getItem() instanceof IFluidContainerItem) + tFluid = ((IFluidContainerItem) tCurrentItem.getItem()).getFluid(tCurrentItem); + if (tFluid != null) { + if (getLockedFluidName() != null && !getLockedFluidName().equals( + tFluid.getFluid() + .getName())) { + GT_Utility.sendChatToPlayer( + aPlayer, + String.format( + "%s %s", + GT_Utility.trans( + "151.3", + "Hatch is locked to a different fluid. To change the locking, empty it and made it locked to the next fluid with a screwdriver. Currently locked to"), + StatCollector.translateToLocal(getLockedFluidName()))); + } else { + setLockedFluidName( + tFluid.getFluid() + .getName()); + if (mMode == 8) GT_Utility.sendChatToPlayer( + aPlayer, + String.format( + "%s (%s)", + GT_Utility.trans("151.1", "Outputs items and 1 specific Fluid"), + tFluid.getLocalizedName())); + else GT_Utility.sendChatToPlayer( + aPlayer, + String.format( + "%s (%s)", + GT_Utility.trans("151.2", "Outputs 1 specific Fluid"), + tFluid.getLocalizedName())); + } + return true; + } + return false; + } + + public byte getMode() { + return mMode; + } + + @Override + public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer, ForgeDirection side, + float aX, float aY, float aZ) { + if (tryToLockHatch(aPlayer, side)) return true; + return super.onRightclick(aBaseMetaTileEntity, aPlayer, side, aX, aY, aZ); + } + + public boolean outputsSteam() { + return mMode < 4; + } + + public boolean outputsLiquids() { + return mMode % 2 == 0 || mMode == 9; + } + + public boolean outputsItems() { + return mMode % 4 < 2 && mMode != 9; + } + + @Override + public String getLockedFluidName() { + return lockedFluidName; + } + + @Override + public void setLockedFluidName(String lockedFluidName) { + this.lockedFluidName = lockedFluidName; + markDirty(); + } + + @Override + public void lockFluid(boolean lock) { + if (lock) { + if (!isFluidLocked()) { + this.mMode = 9; + markDirty(); + } + } else { + this.mMode = 0; + setLockedFluidName(null); + markDirty(); + } + } + + @Override + public boolean isFluidLocked() { + return mMode == 8 || mMode == 9; + } + + @Override + public boolean acceptsFluidLock(String name) { + return true; + } + + @Override + public boolean isEmptyAndAcceptsAnyFluid() { + return mMode == 0 && getFluidAmount() == 0; + } + + @Override + public boolean canStoreFluid(@Nonnull FluidStack fluidStack) { + if (mFluid != null && !GT_Utility.areFluidsEqual(mFluid, fluidStack)) { + return false; + } + if (isFluidLocked()) { + if (lockedFluidName == null) { + return true; + } + return lockedFluidName.equals( + fluidStack.getFluid() + .getName()); + } + if (GT_ModHandler.isSteam(fluidStack)) { + return outputsSteam(); + } + return outputsLiquids(); + } + + @Override + public int getTankPressure() { + return +100; + } + + @Override + protected void onEmptyingContainerWhenEmpty() { + if (this.lockedFluidName == null && this.mFluid != null && isFluidLocked()) { + this.setLockedFluidName( + this.mFluid.getFluid() + .getName()); + final EntityPlayer player; + if (playerThatLockedfluid == null || (player = playerThatLockedfluid.get()) == null) return; + GT_Utility.sendChatToPlayer( + player, + String.format(GT_Utility.trans("151.4", "Successfully locked Fluid to %s"), mFluid.getLocalizedName())); + playerThatLockedfluid = null; + } + } + + @Override + public boolean isGivingInformation() { + return true; + } + + @Override + public String[] getInfoData() { + return new String[] { EnumChatFormatting.BLUE + "Output Hatch" + EnumChatFormatting.RESET, "Stored Fluid:", + EnumChatFormatting.GOLD + (mFluid == null ? "No Fluid" : mFluid.getLocalizedName()) + + EnumChatFormatting.RESET, + EnumChatFormatting.GREEN + GT_Utility.formatNumbers(mFluid == null ? 0 : mFluid.amount) + + " L" + + EnumChatFormatting.RESET + + " " + + EnumChatFormatting.YELLOW + + GT_Utility.formatNumbers(getCapacity()) + + " L" + + EnumChatFormatting.RESET, + (!isFluidLocked() || lockedFluidName == null) ? "Not Locked" + : ("Locked to " + StatCollector.translateToLocal( + FluidRegistry.getFluidStack(lockedFluidName, 1) + .getUnlocalizedName())) }; + } + + @Override + public boolean useModularUI() { + return true; + } + + @Override + public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) { + super.addUIWidgets(builder, buildContext); + builder.widget( + new DrawableWidget().setDrawable(GT_UITextures.PICTURE_SCREEN_BLACK) + .setPos(98, 16) + .setSize(71, 45)) + .widget(new FluidLockWidget(this).setPos(149, 41)) + .widget( + new TextWidget("Locked Fluid").setDefaultColor(COLOR_TEXT_WHITE.get()) + .setPos(101, 20)) + .widget(TextWidget.dynamicString(() -> { + FluidStack fluidStack = FluidRegistry.getFluidStack(lockedFluidName, 1); + return fluidStack != null ? fluidStack.getLocalizedName() : "None"; + }) + .setDefaultColor(COLOR_TEXT_WHITE.get()) + .setTextAlignment(Alignment.CenterLeft) + .setMaxWidth(65) + .setPos(101, 30)) + .widget(new FakeSyncWidget.ByteSyncer(() -> mMode, val -> mMode = val)); + } +} diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_OutputBus.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_OutputBus.java new file mode 100644 index 0000000000..3bd92c6871 --- /dev/null +++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_OutputBus.java @@ -0,0 +1,202 @@ +package gregtech.api.metatileentity.implementations; + +import static gregtech.api.enums.Textures.BlockIcons.ITEM_OUT_SIGN; +import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_PIPE_OUT; +import static gregtech.api.util.GT_Utility.moveMultipleItemStacks; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.IInventory; +import net.minecraft.item.ItemStack; +import net.minecraftforge.common.util.ForgeDirection; + +import com.gtnewhorizons.modularui.api.screen.ModularWindow; +import com.gtnewhorizons.modularui.api.screen.UIBuildContext; + +import gregtech.GT_Mod; +import gregtech.api.gui.modularui.GT_UIInfos; +import gregtech.api.interfaces.ITexture; +import gregtech.api.interfaces.modularui.IAddUIWidgets; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.metatileentity.MetaTileEntity; +import gregtech.api.render.TextureFactory; +import gregtech.api.util.GT_Utility; +import gregtech.api.util.extensions.ArrayExt; + +public class GT_MetaTileEntity_Hatch_OutputBus extends GT_MetaTileEntity_Hatch implements IAddUIWidgets { + + public GT_MetaTileEntity_Hatch_OutputBus(int aID, String aName, String aNameRegional, int aTier) { + this(aID, aName, aNameRegional, aTier, getSlots(aTier)); + } + + public GT_MetaTileEntity_Hatch_OutputBus(int id, String name, String nameRegional, int tier, int slots) { + super( + id, + name, + nameRegional, + tier, + slots, + ArrayExt.of( + "Item Output for Multiblocks", + "Capacity: " + getSlots(tier) + " stack" + (getSlots(tier) >= 2 ? "s" : ""))); + } + + public GT_MetaTileEntity_Hatch_OutputBus(int aID, String aName, String aNameRegional, int aTier, + String[] aDescription) { + super(aID, aName, aNameRegional, aTier, getSlots(aTier), aDescription); + } + + public GT_MetaTileEntity_Hatch_OutputBus(int aID, String aName, String aNameRegional, int aTier, + String[] aDescription, int inventorySize) { + super(aID, aName, aNameRegional, aTier, inventorySize, aDescription); + } + + @Deprecated + // having too many constructors is bad, don't be so lazy, use GT_MetaTileEntity_Hatch_OutputBus(String, int, + // String[], ITexture[][][]) + public GT_MetaTileEntity_Hatch_OutputBus(String aName, int aTier, String aDescription, ITexture[][][] aTextures) { + this(aName, aTier, getSlots(aTier), ArrayExt.of(aDescription), aTextures); + } + + public GT_MetaTileEntity_Hatch_OutputBus(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures) { + super(aName, aTier, getSlots(aTier), aDescription, aTextures); + } + + public GT_MetaTileEntity_Hatch_OutputBus(String name, int tier, int slots, String[] description, + ITexture[][][] textures) { + super(name, tier, slots, description, textures); + } + + @Override + public ITexture[] getTexturesActive(ITexture aBaseTexture) { + return GT_Mod.gregtechproxy.mRenderIndicatorsOnHatch + ? new ITexture[] { aBaseTexture, TextureFactory.of(OVERLAY_PIPE_OUT), TextureFactory.of(ITEM_OUT_SIGN) } + : new ITexture[] { aBaseTexture, TextureFactory.of(OVERLAY_PIPE_OUT) }; + } + + @Override + public ITexture[] getTexturesInactive(ITexture aBaseTexture) { + return GT_Mod.gregtechproxy.mRenderIndicatorsOnHatch + ? new ITexture[] { aBaseTexture, TextureFactory.of(OVERLAY_PIPE_OUT), TextureFactory.of(ITEM_OUT_SIGN) } + : new ITexture[] { aBaseTexture, TextureFactory.of(OVERLAY_PIPE_OUT) }; + } + + @Override + public boolean isSimpleMachine() { + return true; + } + + @Override + public boolean isFacingValid(ForgeDirection facing) { + return true; + } + + @Override + public boolean isAccessAllowed(EntityPlayer aPlayer) { + return true; + } + + @Override + public boolean isValidSlot(int aIndex) { + return true; + } + + @Override + public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) { + return new GT_MetaTileEntity_Hatch_OutputBus(mName, mTier, mDescriptionArray, mTextures); + } + + @Override + public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer) { + GT_UIInfos.openGTTileEntityUI(aBaseMetaTileEntity, aPlayer); + return true; + } + + /** + * Attempt to store as many items as possible into the internal inventory of this output bus. If you need atomicity + * you should use {@link gregtech.api.interfaces.tileentity.IHasInventory#addStackToSlot(int, ItemStack)} + * + * @param aStack Assume valid. Will be mutated. Take over the ownership. Caller should not retain a reference to + * this stack if the call returns true. + * @return true if stack is fully accepted. false is stack is partially accepted or nothing is accepted + */ + public boolean storeAll(ItemStack aStack) { + markDirty(); + for (int i = 0, mInventoryLength = mInventory.length; i < mInventoryLength && aStack.stackSize > 0; i++) { + ItemStack tSlot = mInventory[i]; + if (GT_Utility.isStackInvalid(tSlot)) { + int tRealStackLimit = Math.min(getInventoryStackLimit(), aStack.getMaxStackSize()); + if (aStack.stackSize <= tRealStackLimit) { + mInventory[i] = aStack; + return true; + } + mInventory[i] = aStack.splitStack(tRealStackLimit); + } else { + int tRealStackLimit = Math.min(getInventoryStackLimit(), tSlot.getMaxStackSize()); + if (tSlot.stackSize < tRealStackLimit && tSlot.isItemEqual(aStack) + && ItemStack.areItemStackTagsEqual(tSlot, aStack)) { + if (aStack.stackSize + tSlot.stackSize <= tRealStackLimit) { + mInventory[i].stackSize += aStack.stackSize; + return true; + } else { + // more to serve + aStack.stackSize -= tRealStackLimit - tSlot.stackSize; + mInventory[i].stackSize = tRealStackLimit; + } + } + } + } + return false; + } + + @Override + public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side, + ItemStack aStack) { + return side == aBaseMetaTileEntity.getFrontFacing(); + } + + @Override + public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side, + ItemStack aStack) { + return false; + } + + @Override + public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) { + super.onPostTick(aBaseMetaTileEntity, aTick); + if (aBaseMetaTileEntity.isServerSide() && aBaseMetaTileEntity.isAllowedToWork() && (aTick & 0x7) == 0) { + final IInventory tTileEntity = aBaseMetaTileEntity + .getIInventoryAtSide(aBaseMetaTileEntity.getFrontFacing()); + if (tTileEntity != null) { + moveMultipleItemStacks( + aBaseMetaTileEntity, + tTileEntity, + aBaseMetaTileEntity.getFrontFacing(), + aBaseMetaTileEntity.getBackFacing(), + null, + false, + (byte) 64, + (byte) 1, + (byte) 64, + (byte) 1, + mInventory.length); + for (int i = 0; i < mInventory.length; i++) + if (mInventory[i] != null && mInventory[i].stackSize <= 0) mInventory[i] = null; + } + } + } + + @Override + public boolean useModularUI() { + return true; + } + + @Override + public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) { + switch (mTier) { + case 0 -> getBaseMetaTileEntity().add1by1Slot(builder); + case 1 -> getBaseMetaTileEntity().add2by2Slots(builder); + case 2 -> getBaseMetaTileEntity().add3by3Slots(builder); + default -> getBaseMetaTileEntity().add4by4Slots(builder); + } + } +} diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_QuadrupleHumongous.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_QuadrupleHumongous.java new file mode 100644 index 0000000000..e0ab7acf95 --- /dev/null +++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Hatch_QuadrupleHumongous.java @@ -0,0 +1,27 @@ +package gregtech.api.metatileentity.implementations; + +import gregtech.api.interfaces.ITexture; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.metatileentity.MetaTileEntity; + +public class GT_MetaTileEntity_Hatch_QuadrupleHumongous extends GT_MetaTileEntity_Hatch_MultiInput { + + public GT_MetaTileEntity_Hatch_QuadrupleHumongous(int aID, int aSlot, String aName, String aNameRegional) { + super(aID, aSlot, aName, aNameRegional, 13); + } + + public GT_MetaTileEntity_Hatch_QuadrupleHumongous(String aName, int aSlot, int aTier, String[] aDescription, + ITexture[][][] aTextures) { + super(aName, aSlot, aTier, aDescription, aTextures); + } + + @Override + public int getCapacityPerTank(int aTier, int aSlot) { + return 500_000_000; + } + + @Override + public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) { + return new GT_MetaTileEntity_Hatch_QuadrupleHumongous(mName, getMaxType(), mTier, mDescriptionArray, mTextures); + } +} diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_MultiBlockBase.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_MultiBlockBase.java new file mode 100644 index 0000000000..32ea708773 --- /dev/null +++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_MultiBlockBase.java @@ -0,0 +1,2540 @@ +package gregtech.api.metatileentity.implementations; + +import static gregtech.api.enums.GT_Values.V; +import static gregtech.api.enums.GT_Values.VN; +import static gregtech.api.util.GT_Utility.filterValidMTEs; +import static gregtech.api.util.GT_Utility.formatNumbers; +import static mcp.mobius.waila.api.SpecialChars.GREEN; +import static mcp.mobius.waila.api.SpecialChars.RED; +import static mcp.mobius.waila.api.SpecialChars.RESET; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.IntConsumer; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import net.minecraft.client.Minecraft; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.inventory.IInventory; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.StatCollector; +import net.minecraft.world.World; +import net.minecraftforge.common.util.Constants; +import net.minecraftforge.common.util.ForgeDirection; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidStack; + +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.TestOnly; +import org.lwjgl.input.Keyboard; + +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.gtnewhorizons.modularui.api.NumberFormatMUI; +import com.gtnewhorizons.modularui.api.math.Alignment; +import com.gtnewhorizons.modularui.api.math.Pos2d; +import com.gtnewhorizons.modularui.api.screen.ModularWindow; +import com.gtnewhorizons.modularui.api.screen.UIBuildContext; +import com.gtnewhorizons.modularui.common.internal.network.NetworkUtils; +import com.gtnewhorizons.modularui.common.widget.DrawableWidget; +import com.gtnewhorizons.modularui.common.widget.DynamicPositionedColumn; +import com.gtnewhorizons.modularui.common.widget.FakeSyncWidget; +import com.gtnewhorizons.modularui.common.widget.SlotWidget; +import com.gtnewhorizons.modularui.common.widget.TextWidget; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import gregtech.GT_Mod; +import gregtech.api.GregTech_API; +import gregtech.api.enums.ConfigCategories; +import gregtech.api.enums.SoundResource; +import gregtech.api.enums.VoidingMode; +import gregtech.api.gui.modularui.GT_UIInfos; +import gregtech.api.gui.modularui.GT_UITextures; +import gregtech.api.interfaces.fluid.IFluidStore; +import gregtech.api.interfaces.metatileentity.IMetaTileEntity; +import gregtech.api.interfaces.modularui.ControllerWithOptionalFeatures; +import gregtech.api.interfaces.modularui.IAddGregtechLogo; +import gregtech.api.interfaces.modularui.IAddUIWidgets; +import gregtech.api.interfaces.modularui.IBindPlayerInventoryUI; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.items.GT_MetaGenerated_Tool; +import gregtech.api.logic.ProcessingLogic; +import gregtech.api.metatileentity.MetaTileEntity; +import gregtech.api.objects.GT_ItemStack; +import gregtech.api.recipe.RecipeMap; +import gregtech.api.recipe.check.CheckRecipeResult; +import gregtech.api.recipe.check.CheckRecipeResultRegistry; +import gregtech.api.recipe.check.SingleRecipeCheck; +import gregtech.api.util.GT_ClientPreference; +import gregtech.api.util.GT_ExoticEnergyInputHelper; +import gregtech.api.util.GT_Log; +import gregtech.api.util.GT_OverclockCalculator; +import gregtech.api.util.GT_ParallelHelper; +import gregtech.api.util.GT_Recipe; +import gregtech.api.util.GT_Utility; +import gregtech.api.util.GT_Waila; +import gregtech.api.util.OutputHatchWrapper; +import gregtech.api.util.VoidProtectionHelper; +import gregtech.api.util.shutdown.ShutDownReason; +import gregtech.api.util.shutdown.ShutDownReasonRegistry; +import gregtech.client.GT_SoundLoop; +import gregtech.common.GT_Pollution; +import gregtech.common.gui.modularui.widget.CheckRecipeResultSyncer; +import gregtech.common.gui.modularui.widget.ShutDownReasonSyncer; +import gregtech.common.items.GT_MetaGenerated_Tool_01; +import gregtech.common.tileentities.machines.GT_MetaTileEntity_Hatch_CraftingInput_ME; +import gregtech.common.tileentities.machines.GT_MetaTileEntity_Hatch_InputBus_ME; +import gregtech.common.tileentities.machines.GT_MetaTileEntity_Hatch_Input_ME; +import gregtech.common.tileentities.machines.GT_MetaTileEntity_Hatch_OutputBus_ME; +import gregtech.common.tileentities.machines.GT_MetaTileEntity_Hatch_Output_ME; +import gregtech.common.tileentities.machines.IDualInputHatch; +import gregtech.common.tileentities.machines.IDualInputInventory; +import gregtech.common.tileentities.machines.IRecipeProcessingAwareHatch; +import gregtech.common.tileentities.machines.multi.GT_MetaTileEntity_LargeTurbine; +import it.unimi.dsi.fastutil.objects.Object2ReferenceOpenHashMap; +import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap; +import mcp.mobius.waila.api.IWailaConfigHandler; +import mcp.mobius.waila.api.IWailaDataAccessor; + +public abstract class GT_MetaTileEntity_MultiBlockBase extends MetaTileEntity + implements ControllerWithOptionalFeatures, IAddGregtechLogo, IAddUIWidgets, IBindPlayerInventoryUI { + + public static boolean disableMaintenance; + public boolean mMachine = false, mWrench = false, mScrewdriver = false, mSoftHammer = false, mHardHammer = false, + mSolderingTool = false, mCrowbar = false, mRunningOnLoad = false; + public boolean mStructureChanged = false; + public int mPollution = 0, mProgresstime = 0, mMaxProgresstime = 0, mEUt = 0, mEfficiencyIncrease = 0, + mStartUpCheck = 100, mRuntime = 0, mEfficiency = 0; + public volatile boolean mUpdated = false; + public int mUpdate = 0; + public ItemStack[] mOutputItems = null; + public FluidStack[] mOutputFluids = null; + public String mNEI; + public int damageFactorLow = 5; + public float damageFactorHigh = 0.6f; + + public boolean mLockedToSingleRecipe = getDefaultRecipeLockingMode(); + protected boolean inputSeparation = getDefaultInputSeparationMode(); + protected VoidingMode voidingMode = getDefaultVoidingMode(); + protected boolean batchMode = getDefaultBatchMode(); + private @Nonnull CheckRecipeResult checkRecipeResult = CheckRecipeResultRegistry.NONE; + + protected static final String INPUT_SEPARATION_NBT_KEY = "inputSeparation"; + protected static final String VOID_EXCESS_NBT_KEY = "voidExcess"; + protected static final String VOIDING_MODE_NBT_KEY = "voidingMode"; + protected static final String BATCH_MODE_NBT_KEY = "batchMode"; + protected SingleRecipeCheck mSingleRecipeCheck = null; + + public ArrayList<GT_MetaTileEntity_Hatch_Input> mInputHatches = new ArrayList<>(); + public ArrayList<GT_MetaTileEntity_Hatch_Output> mOutputHatches = new ArrayList<>(); + public ArrayList<GT_MetaTileEntity_Hatch_InputBus> mInputBusses = new ArrayList<>(); + public ArrayList<GT_MetaTileEntity_Hatch_OutputBus> mOutputBusses = new ArrayList<>(); + public ArrayList<IDualInputHatch> mDualInputHatches = new ArrayList<>(); + public ArrayList<GT_MetaTileEntity_Hatch_Dynamo> mDynamoHatches = new ArrayList<>(); + public ArrayList<GT_MetaTileEntity_Hatch_Muffler> mMufflerHatches = new ArrayList<>(); + public ArrayList<GT_MetaTileEntity_Hatch_Energy> mEnergyHatches = new ArrayList<>(); + public ArrayList<GT_MetaTileEntity_Hatch_Maintenance> mMaintenanceHatches = new ArrayList<>(); + protected List<GT_MetaTileEntity_Hatch> mExoticEnergyHatches = new ArrayList<>(); + protected final ProcessingLogic processingLogic; + @SideOnly(Side.CLIENT) + protected GT_SoundLoop activitySoundLoop; + + private long mLastWorkingTick = 0; + + protected static final byte INTERRUPT_SOUND_INDEX = 8; + protected static final byte PROCESS_START_SOUND_INDEX = 1; + + public GT_MetaTileEntity_MultiBlockBase(int aID, String aName, String aNameRegional) { + super(aID, aName, aNameRegional, 2); + this.processingLogic = null; + GT_MetaTileEntity_MultiBlockBase.disableMaintenance = GregTech_API.sMachineFile + .get(ConfigCategories.machineconfig, "MultiBlockMachines.disableMaintenance", false); + this.damageFactorLow = GregTech_API.sMachineFile + .get(ConfigCategories.machineconfig, "MultiBlockMachines.damageFactorLow", 5); + this.damageFactorHigh = (float) GregTech_API.sMachineFile + .get(ConfigCategories.machineconfig, "MultiBlockMachines.damageFactorHigh", 0.6f); + this.mNEI = ""; + } + + public GT_MetaTileEntity_MultiBlockBase(String aName) { + super(aName, 2); + this.processingLogic = createProcessingLogic(); + GT_MetaTileEntity_MultiBlockBase.disableMaintenance = GregTech_API.sMachineFile + .get(ConfigCategories.machineconfig, "MultiBlockMachines.disableMaintenance", false); + this.damageFactorLow = GregTech_API.sMachineFile + .get(ConfigCategories.machineconfig, "MultiBlockMachines.damageFactorLow", 5); + this.damageFactorHigh = (float) GregTech_API.sMachineFile + .get(ConfigCategories.machineconfig, "MultiBlockMachines.damageFactorHigh", 0.6f); + } + + @Override + public boolean isDisplaySecondaryDescription() { + return Keyboard.isKeyDown(Keyboard.KEY_LSHIFT); + } + + @Override + public boolean allowCoverOnSide(ForgeDirection side, GT_ItemStack aCoverID) { + return side != getBaseMetaTileEntity().getFrontFacing(); + } + + @Override + public void onScrewdriverRightClick(ForgeDirection side, EntityPlayer aPlayer, float aX, float aY, float aZ) { + if (supportsSingleRecipeLocking()) { + mLockedToSingleRecipe = !mLockedToSingleRecipe; + if (mLockedToSingleRecipe) { + GT_Utility.sendChatToPlayer( + aPlayer, + GT_Utility.trans("223", "Single recipe locking enabled. Will lock to next recipe.")); + } else { + GT_Utility.sendChatToPlayer(aPlayer, GT_Utility.trans("220", "Single recipe locking disabled.")); + mSingleRecipeCheck = null; + } + } + } + + @Override + public boolean isSimpleMachine() { + return false; + } + + @Override + public boolean isFacingValid(ForgeDirection facing) { + return true; + } + + @Override + public boolean isAccessAllowed(EntityPlayer aPlayer) { + return true; + } + + @Override + public boolean isValidSlot(int aIndex) { + return aIndex > 0; + } + + @Override + public int getProgresstime() { + return mProgresstime; + } + + @Override + public int maxProgresstime() { + return mMaxProgresstime; + } + + @Override + public int increaseProgress(int aProgress) { + return aProgress; + } + + @Override + public void saveNBTData(NBTTagCompound aNBT) { + aNBT.setInteger("mEUt", mEUt); + aNBT.setInteger("mProgresstime", mProgresstime); + aNBT.setInteger("mMaxProgresstime", mMaxProgresstime); + aNBT.setInteger("mEfficiencyIncrease", mEfficiencyIncrease); + aNBT.setInteger("mEfficiency", mEfficiency); + aNBT.setInteger("mPollution", mPollution); + aNBT.setInteger("mRuntime", mRuntime); + if (supportsSingleRecipeLocking()) { + aNBT.setBoolean("mLockedToSingleRecipe", mLockedToSingleRecipe); + if (mLockedToSingleRecipe && mSingleRecipeCheck != null) + aNBT.setTag("mSingleRecipeCheck", mSingleRecipeCheck.writeToNBT()); + } + + if (mOutputItems != null) { + aNBT.setInteger("mOutputItemsLength", mOutputItems.length); + for (int i = 0; i < mOutputItems.length; i++) if (mOutputItems[i] != null) { + GT_Utility.saveItem(aNBT, "mOutputItem" + i, mOutputItems[i]); + } + } + if (mOutputFluids != null) { + aNBT.setInteger("mOutputFluidsLength", mOutputFluids.length); + for (int i = 0; i < mOutputFluids.length; i++) if (mOutputFluids[i] != null) { + NBTTagCompound tNBT = new NBTTagCompound(); + mOutputFluids[i].writeToNBT(tNBT); + aNBT.setTag("mOutputFluids" + i, tNBT); + } + } + aNBT.setBoolean("mWrench", mWrench); + aNBT.setBoolean("mScrewdriver", mScrewdriver); + aNBT.setBoolean("mSoftHammer", mSoftHammer); + aNBT.setBoolean("mHardHammer", mHardHammer); + aNBT.setBoolean("mSolderingTool", mSolderingTool); + aNBT.setBoolean("mCrowbar", mCrowbar); + aNBT.setBoolean(BATCH_MODE_NBT_KEY, batchMode); + aNBT.setBoolean(INPUT_SEPARATION_NBT_KEY, inputSeparation); + aNBT.setString(VOIDING_MODE_NBT_KEY, voidingMode.name); + } + + @Override + public void loadNBTData(NBTTagCompound aNBT) { + mEUt = aNBT.getInteger("mEUt"); + mProgresstime = aNBT.getInteger("mProgresstime"); + mMaxProgresstime = aNBT.getInteger("mMaxProgresstime"); + if (mMaxProgresstime > 0) mRunningOnLoad = true; + mEfficiencyIncrease = aNBT.getInteger("mEfficiencyIncrease"); + mEfficiency = aNBT.getInteger("mEfficiency"); + mPollution = aNBT.getInteger("mPollution"); + mRuntime = aNBT.getInteger("mRuntime"); + if (supportsSingleRecipeLocking()) { + mLockedToSingleRecipe = aNBT.getBoolean("mLockedToSingleRecipe"); + if (mLockedToSingleRecipe && aNBT.hasKey("mSingleRecipeCheck", Constants.NBT.TAG_COMPOUND)) { + SingleRecipeCheck c = loadSingleRecipeChecker(aNBT.getCompoundTag("mSingleRecipeCheck")); + if (c != null) mSingleRecipeCheck = c; + // the old recipe is gone. we disable the machine to prevent making garbage in case of shared inputs + // maybe use a better way to inform player in the future. + else getBaseMetaTileEntity().disableWorking(); + } + } + batchMode = aNBT.getBoolean(BATCH_MODE_NBT_KEY); + inputSeparation = aNBT.getBoolean(INPUT_SEPARATION_NBT_KEY); + if (aNBT.hasKey(VOIDING_MODE_NBT_KEY, Constants.NBT.TAG_STRING)) { + voidingMode = VoidingMode.fromName(aNBT.getString(VOIDING_MODE_NBT_KEY)); + } else if (aNBT.hasKey(VOID_EXCESS_NBT_KEY)) { + // backward compatibility + voidingMode = aNBT.getBoolean(VOID_EXCESS_NBT_KEY) ? VoidingMode.VOID_ALL : VoidingMode.VOID_NONE; + } + if (!getAllowedVoidingModes().contains(voidingMode)) voidingMode = getDefaultVoidingMode(); + + int aOutputItemsLength = aNBT.getInteger("mOutputItemsLength"); + if (aOutputItemsLength > 0) { + mOutputItems = new ItemStack[aOutputItemsLength]; + for (int i = 0; i < mOutputItems.length; i++) + mOutputItems[i] = GT_Utility.loadItem(aNBT, "mOutputItem" + i); + } + + int aOutputFluidsLength = aNBT.getInteger("mOutputFluidsLength"); + if (aOutputFluidsLength > 0) { + mOutputFluids = new FluidStack[aOutputFluidsLength]; + for (int i = 0; i < mOutputFluids.length; i++) + mOutputFluids[i] = GT_Utility.loadFluid(aNBT, "mOutputFluids" + i); + } + + mWrench = aNBT.getBoolean("mWrench"); + mScrewdriver = aNBT.getBoolean("mScrewdriver"); + mSoftHammer = aNBT.getBoolean("mSoftHammer"); + mHardHammer = aNBT.getBoolean("mHardHammer"); + mSolderingTool = aNBT.getBoolean("mSolderingTool"); + mCrowbar = aNBT.getBoolean("mCrowbar"); + } + + protected SingleRecipeCheck loadSingleRecipeChecker(NBTTagCompound aNBT) { + return SingleRecipeCheck.tryLoad(getRecipeMap(), aNBT); + } + + @Override + public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer) { + GT_UIInfos.openGTTileEntityUI(aBaseMetaTileEntity, aPlayer); + return true; + } + + @Override + public byte getTileEntityBaseType() { + return 2; + } + + /** + * Set the structure as having changed, and trigger an update. + */ + public void onStructureChange() { + mStructureChanged = true; + } + + @Override + public void onMachineBlockUpdate() { + mUpdated = true; + } + + /** + * ClearHatches as a part of structure check. If your multiblock has any hatches that need clearing override this + * method, call super, and clear your own hatches + */ + public void clearHatches() { + mInputHatches.clear(); + mInputBusses.clear(); + mOutputHatches.clear(); + mOutputBusses.clear(); + mDynamoHatches.clear(); + mEnergyHatches.clear(); + setMufflers(false); + mMufflerHatches.clear(); + mMaintenanceHatches.clear(); + mDualInputHatches.clear(); + } + + public boolean checkStructure(boolean aForceReset) { + return checkStructure(aForceReset, getBaseMetaTileEntity()); + } + + public boolean checkStructure(boolean aForceReset, IGregTechTileEntity aBaseMetaTileEntity) { + if (!aBaseMetaTileEntity.isServerSide()) return mMachine; + // Only trigger an update if forced (from onPostTick, generally), or if the structure has changed + if ((mStructureChanged || aForceReset)) { + clearHatches(); + mMachine = checkMachine(aBaseMetaTileEntity, mInventory[1]); + } + mStructureChanged = false; + return mMachine; + } + + @Override + public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) { + if (aBaseMetaTileEntity.isServerSide()) { + if (mEfficiency < 0) mEfficiency = 0; + if (mUpdated) { + // duct tape fix for too many updates on an overloaded server, causing the structure check to not run + if (mUpdate <= 0) mUpdate = 50; + mUpdated = false; + } + if (--mUpdate == 0 || --mStartUpCheck == 0) { + checkStructure(true, aBaseMetaTileEntity); + } + + if (mStartUpCheck < 0) { + if (mMachine) { + checkMaintenance(); + if (getRepairStatus() > 0) { + runMachine(aBaseMetaTileEntity, aTick); + } else if (aBaseMetaTileEntity.isAllowedToWork()) { + stopMachine(ShutDownReasonRegistry.NO_REPAIR); + } + } else if (aBaseMetaTileEntity.isAllowedToWork()) { + stopMachine(ShutDownReasonRegistry.STRUCTURE_INCOMPLETE); + } + } + aBaseMetaTileEntity.setErrorDisplayID( + (aBaseMetaTileEntity.getErrorDisplayID() & ~127) | (mWrench ? 0 : 1) + | (mScrewdriver ? 0 : 2) + | (mSoftHammer ? 0 : 4) + | (mHardHammer ? 0 : 8) + | (mSolderingTool ? 0 : 16) + | (mCrowbar ? 0 : 32) + | (mMachine ? 0 : 64)); + aBaseMetaTileEntity.setActive(mMaxProgresstime > 0); + boolean active = aBaseMetaTileEntity.isActive() && mPollution > 0; + setMufflers(active); + } else { + if (!aBaseMetaTileEntity.hasMufflerUpgrade()) { + doActivitySound(getActivitySoundLoop()); + } + } + } + + @Override + public void onTickFail(IGregTechTileEntity aBaseMetaTileEntity, long aTick) { + super.onTickFail(aBaseMetaTileEntity, aTick); + if (aBaseMetaTileEntity.isServerSide()) { + aBaseMetaTileEntity.disableWorking(); + checkRecipeResult = CheckRecipeResultRegistry.CRASH; + } + } + + private void checkMaintenance() { + if (disableMaintenance) { + mWrench = true; + mScrewdriver = true; + mSoftHammer = true; + mHardHammer = true; + mSolderingTool = true; + mCrowbar = true; + + return; + } + for (GT_MetaTileEntity_Hatch_Maintenance tHatch : filterValidMTEs(mMaintenanceHatches)) { + if (tHatch.mAuto && !(mWrench && mScrewdriver && mSoftHammer && mHardHammer && mSolderingTool && mCrowbar)) + tHatch.autoMaintainance(); + if (tHatch.mWrench) mWrench = true; + if (tHatch.mScrewdriver) mScrewdriver = true; + if (tHatch.mSoftHammer) mSoftHammer = true; + if (tHatch.mHardHammer) mHardHammer = true; + if (tHatch.mSolderingTool) mSolderingTool = true; + if (tHatch.mCrowbar) mCrowbar = true; + + tHatch.mWrench = false; + tHatch.mScrewdriver = false; + tHatch.mSoftHammer = false; + tHatch.mHardHammer = false; + tHatch.mSolderingTool = false; + tHatch.mCrowbar = false; + } + } + + /** + * Starts checking recipe with some operations needed to actually run the check. Overriding this without due care + * may result in dupe of items, hence it's marked as final. + * <p> + * See {@link #createProcessingLogic()} or {@link #checkProcessing()} for what you want to override. + * + * @return If successfully found recipe and/or started processing + */ + protected final boolean checkRecipe() { + startRecipeProcessing(); + CheckRecipeResult result = checkProcessing(); + if (!CheckRecipeResultRegistry.isRegistered(result.getID())) { + throw new RuntimeException(String.format("Result %s is not registered for registry", result.getID())); + } + if (result.wasSuccessful()) { + sendStartMultiBlockSoundLoop(); + } + this.checkRecipeResult = result; + endRecipeProcessing(); + // Don't use `result` here because `endRecipeProcessing()` might mutate `this.checkRecipeResult` + return this.checkRecipeResult.wasSuccessful(); + } + + private boolean shouldCheckRecipeThisTick(long aTick) { + // do a recipe check if any crafting input hatch just got pushed in items + boolean shouldCheck = false; + // check all of them (i.e. do not return early) to reset the state of all of them. + for (IDualInputHatch craftingInputMe : mDualInputHatches) { + shouldCheck |= craftingInputMe.justUpdated(); + } + if (shouldCheck) return true; + + // Perform more frequent recipe change after the machine just shuts down. + long timeElapsed = aTick - mLastWorkingTick; + + if (timeElapsed >= 100) return aTick % 100 == 0; + // Batch mode should be a lot less aggressive at recipe checking + if (!isBatchModeEnabled()) { + return timeElapsed == 5 || timeElapsed == 12 + || timeElapsed == 20 + || timeElapsed == 30 + || timeElapsed == 40 + || timeElapsed == 55 + || timeElapsed == 70 + || timeElapsed == 85; + } + return false; + } + + protected void runMachine(IGregTechTileEntity aBaseMetaTileEntity, long aTick) { + if (mMaxProgresstime > 0 && doRandomMaintenanceDamage()) { + if (onRunningTick(mInventory[1])) { + markDirty(); + if (!polluteEnvironment(getPollutionPerTick(mInventory[1]))) { + stopMachine(ShutDownReasonRegistry.POLLUTION_FAIL); + } + if (mMaxProgresstime > 0 && ++mProgresstime >= mMaxProgresstime) { + if (mOutputItems != null) { + for (ItemStack tStack : mOutputItems) { + if (tStack != null) { + try { + GT_Mod.achievements.issueAchivementHatch( + aBaseMetaTileEntity.getWorld() + .getPlayerEntityByName(aBaseMetaTileEntity.getOwnerName()), + tStack); + } catch (Exception ignored) {} + addOutput(tStack); + } + } + mOutputItems = null; + } + if (mOutputFluids != null) { + addFluidOutputs(mOutputFluids); + if (mOutputFluids.length > 1) { + try { + GT_Mod.achievements.issueAchievement( + aBaseMetaTileEntity.getWorld() + .getPlayerEntityByName(aBaseMetaTileEntity.getOwnerName()), + "oilplant"); + } catch (Exception ignored) {} + } + mOutputFluids = null; + } + mEfficiency = Math.max( + 0, + Math.min( + mEfficiency + mEfficiencyIncrease, + getMaxEfficiency(mInventory[1]) - ((getIdealStatus() - getRepairStatus()) * 1000))); + mOutputItems = null; + mProgresstime = 0; + mMaxProgresstime = 0; + mEfficiencyIncrease = 0; + mLastWorkingTick = aTick; + if (aBaseMetaTileEntity.isAllowedToWork()) { + checkRecipe(); + } + } + } + } else { + if (shouldCheckRecipeThisTick(aTick) || aBaseMetaTileEntity.hasWorkJustBeenEnabled() + || aBaseMetaTileEntity.hasInventoryBeenModified()) { + + if (aBaseMetaTileEntity.isAllowedToWork()) { + if (checkRecipe()) { + markDirty(); + } + } + if (mMaxProgresstime <= 0) mEfficiency = Math.max(0, mEfficiency - 1000); + } + } + } + + public boolean polluteEnvironment(int aPollutionLevel) { + mPollution += aPollutionLevel; + for (GT_MetaTileEntity_Hatch_Muffler tHatch : filterValidMTEs(mMufflerHatches)) { + if (mPollution >= 10000) { + if (tHatch.polluteEnvironment(this)) { + mPollution -= 10000; + } + } else { + break; + } + } + return mPollution < 10000; + } + + protected void sendStartMultiBlockSoundLoop() { + if (getProcessStartSound() != null) { + sendLoopStart(PROCESS_START_SOUND_INDEX); + } + } + + @Override + public void doSound(byte aIndex, double aX, double aY, double aZ) { + super.doSound(aIndex, aX, aY, aZ); + switch (aIndex) { + case PROCESS_START_SOUND_INDEX -> { + if (getProcessStartSound() != null) + GT_Utility.doSoundAtClient(getProcessStartSound(), getTimeBetweenProcessSounds(), 1.0F, aX, aY, aZ); + } + case INTERRUPT_SOUND_INDEX -> GT_Utility + .doSoundAtClient(SoundResource.IC2_MACHINES_INTERRUPT_ONE, 100, 1.0F, aX, aY, aZ); + } + } + + @Override + public void startSoundLoop(byte aIndex, double aX, double aY, double aZ) { + super.startSoundLoop(aIndex, aX, aY, aZ); + if (aIndex == PROCESS_START_SOUND_INDEX) { + if (getProcessStartSound() != null) + GT_Utility.doSoundAtClient(getProcessStartSound(), getTimeBetweenProcessSounds(), 1.0F, aX, aY, aZ); + } + } + + @SideOnly(Side.CLIENT) + protected void doActivitySound(ResourceLocation activitySound) { + if (getBaseMetaTileEntity().isActive() && activitySound != null) { + if (activitySoundLoop == null) { + activitySoundLoop = new GT_SoundLoop(activitySound, getBaseMetaTileEntity(), false, true); + Minecraft.getMinecraft() + .getSoundHandler() + .playSound(activitySoundLoop); + } + } else { + if (activitySoundLoop != null) { + activitySoundLoop = null; + } + } + } + + /** + * @return Time before the start process sound is played again + */ + protected int getTimeBetweenProcessSounds() { + return 100; + } + + /** + * @return Sound that will be played once, when the recipe check was valid + */ + protected SoundResource getProcessStartSound() { + return null; + } + + /** + * @return Sound that will be looped for as long as the machine is doing a recipe + */ + @SideOnly(Side.CLIENT) + protected ResourceLocation getActivitySoundLoop() { + return null; + } + + /** + * Called every tick the Machine runs + */ + public boolean onRunningTick(ItemStack aStack) { + if (mEUt > 0) { + addEnergyOutput(((long) mEUt * mEfficiency) / 10000); + return true; + } + if (mEUt < 0) { + if (!drainEnergyInput(getActualEnergyUsage())) { + stopMachine(ShutDownReasonRegistry.POWER_LOSS); + return false; + } + } + return true; + } + + protected long getActualEnergyUsage() { + return ((long) -mEUt * 10_000) / Math.max(1000, mEfficiency); + } + + /** + * Checks if this is a Correct Machine Part for this kind of Machine (Turbine Rotor for example) + */ + public abstract boolean isCorrectMachinePart(ItemStack aStack); + + /** + * @deprecated Use {@link #createProcessingLogic()} or {@link #checkProcessing()} + */ + @Deprecated + public boolean checkRecipe(ItemStack aStack) { + return false; + } + + /** + * Checks recipe and setup machine if it's successful. + * <p> + * For generic machine working with recipemap, use {@link #createProcessingLogic()} to make use of shared codebase. + */ + @Nonnull + public CheckRecipeResult checkProcessing() { + // If no logic is found, try legacy checkRecipe + if (processingLogic == null) { + return checkRecipe(mInventory[1]) ? CheckRecipeResultRegistry.SUCCESSFUL + : CheckRecipeResultRegistry.NO_RECIPE; + } + + setupProcessingLogic(processingLogic); + + CheckRecipeResult result = doCheckRecipe(); + result = postCheckRecipe(result, processingLogic); + // inputs are consumed at this point + updateSlots(); + if (!result.wasSuccessful()) return result; + + mEfficiency = (10000 - (getIdealStatus() - getRepairStatus()) * 1000); + mEfficiencyIncrease = 10000; + mMaxProgresstime = processingLogic.getDuration(); + setEnergyUsage(processingLogic); + + mOutputItems = processingLogic.getOutputItems(); + mOutputFluids = processingLogic.getOutputFluids(); + + return result; + } + + /** + * @return If controller slot should be considered as inputs for {@link #doCheckRecipe} + */ + protected boolean canUseControllerSlotForRecipe() { + return true; + } + + /** + * Initializes processing logic for use. Unlike {@link #createProcessingLogic}, this method is called + * every time checking for recipes. + */ + protected void setupProcessingLogic(ProcessingLogic logic) { + logic.clear(); + logic.setMachine(this); + logic.setRecipeMapSupplier(this::getRecipeMap); + logic.setVoidProtection(protectsExcessItem(), protectsExcessFluid()); + logic.setBatchSize(isBatchModeEnabled() ? getMaxBatchSize() : 1); + logic.setRecipeLocking(this, isRecipeLockingEnabled()); + setProcessingLogicPower(logic); + } + + /** + * Initializes processing logic for use, specifically for power-related parameters. + * Unlike {@link #createProcessingLogic}, this method is called every time checking for recipes. + */ + protected void setProcessingLogicPower(ProcessingLogic logic) { + logic.setAvailableVoltage(getAverageInputVoltage()); + logic.setAvailableAmperage(getMaxInputAmps()); + logic.setAmperageOC(mEnergyHatches.size() != 1); + } + + protected boolean supportsCraftingMEBuffer() { + return true; + } + + /** + * Iterates over hatches and tries to find recipe. Assume {@link #processingLogic} is already set up for use. + * If return value is successful, inputs are consumed. + */ + @Nonnull + protected CheckRecipeResult doCheckRecipe() { + CheckRecipeResult result = CheckRecipeResultRegistry.NO_RECIPE; + // check crafting input hatches first + if (supportsCraftingMEBuffer()) { + for (IDualInputHatch dualInputHatch : mDualInputHatches) { + for (var it = dualInputHatch.inventories(); it.hasNext();) { + IDualInputInventory slot = it.next(); + processingLogic.setInputItems(slot.getItemInputs()); + processingLogic.setInputFluids(slot.getFluidInputs()); + CheckRecipeResult foundResult = processingLogic.process(); + if (foundResult.wasSuccessful()) { + return foundResult; + } + if (foundResult != CheckRecipeResultRegistry.NO_RECIPE) { + // Recipe failed in interesting way, so remember that and continue searching + result = foundResult; + } + } + } + } + + processingLogic.setInputFluids(getStoredFluids()); + + if (isInputSeparationEnabled()) { + if (mInputBusses.isEmpty()) { + CheckRecipeResult foundResult = processingLogic.process(); + if (foundResult.wasSuccessful()) { + return foundResult; + } + if (foundResult != CheckRecipeResultRegistry.NO_RECIPE) { + // Recipe failed in interesting way, so remember that and continue searching + result = foundResult; + } + } else { + for (GT_MetaTileEntity_Hatch_InputBus bus : mInputBusses) { + if (bus instanceof GT_MetaTileEntity_Hatch_CraftingInput_ME) { + continue; + } + List<ItemStack> inputItems = new ArrayList<>(); + for (int i = bus.getSizeInventory() - 1; i >= 0; i--) { + ItemStack stored = bus.getStackInSlot(i); + if (stored != null) { + inputItems.add(stored); + } + } + if (canUseControllerSlotForRecipe() && getControllerSlot() != null) { + inputItems.add(getControllerSlot()); + } + processingLogic.setInputItems(inputItems.toArray(new ItemStack[0])); + CheckRecipeResult foundResult = processingLogic.process(); + if (foundResult.wasSuccessful()) { + return foundResult; + } + if (foundResult != CheckRecipeResultRegistry.NO_RECIPE) { + // Recipe failed in interesting way, so remember that and continue searching + result = foundResult; + } + } + } + } else { + List<ItemStack> inputItems = getStoredInputs(); + if (canUseControllerSlotForRecipe() && getControllerSlot() != null) { + inputItems.add(getControllerSlot()); + } + processingLogic.setInputItems(inputItems); + CheckRecipeResult foundResult = processingLogic.process(); + if (foundResult.wasSuccessful()) { + return foundResult; + } + if (foundResult != CheckRecipeResultRegistry.NO_RECIPE) { + // Recipe failed in interesting way, so remember that + result = foundResult; + } + } + return result; + } + + /** + * Performs additional check for {@link #processingLogic} after all the calculations are done. + * As many as checks should be done inside of custom {@link ProcessingLogic}, which you can specify with + * {@link #createProcessingLogic()}, because when this method is called, inputs might have been already consumed. + * However, certain checks cannot be done like that; Checking energy overflow should be suppressed for + * long-power machines for example. + * + * @return Modified (or not modified) result + */ + @Nonnull + protected CheckRecipeResult postCheckRecipe(@Nonnull CheckRecipeResult result, + @Nonnull ProcessingLogic processingLogic) { + if (result.wasSuccessful() && processingLogic.getCalculatedEut() > Integer.MAX_VALUE) { + return CheckRecipeResultRegistry.POWER_OVERFLOW; + } + return result; + } + + /** + * Called after {@link #doCheckRecipe} and {@link #postCheckRecipe} being successful. + * Override to set energy usage for this machine. + */ + protected void setEnergyUsage(ProcessingLogic processingLogic) { + // getCalculatedEut() is guaranteed to not exceed int by postCheckRecipe() + mEUt = (int) processingLogic.getCalculatedEut(); + if (mEUt > 0) { + mEUt = (-mEUt); + } + } + + protected int getMaxBatchSize() { + return 128; + } + + /** + * Checks the Machine. You have to assign the MetaTileEntities for the Hatches here. + */ + public abstract boolean checkMachine(IGregTechTileEntity aBaseMetaTileEntity, ItemStack aStack); + + /** + * Gets the maximum Efficiency that spare Part can get (0 - 10000) + */ + public abstract int getMaxEfficiency(ItemStack aStack); + + /** + * Gets the pollution this Device outputs to a Muffler per tick (10000 = one Pullution Block) + */ + public int getPollutionPerTick(ItemStack aStack) { + return getPollutionPerSecond(aStack) / 20; + } + + /** + * Gets the pollution produced per second by this multiblock, default to 0. Override this with its actual value in + * the code of the multiblock. + */ + public int getPollutionPerSecond(ItemStack aStack) { + return 0; + } + + /** + * Gets the damage to the ItemStack, usually 0 or 1. + */ + public abstract int getDamageToComponent(ItemStack aStack); + + /** + * If it explodes when the Component has to be replaced. + */ + public abstract boolean explodesOnComponentBreak(ItemStack aStack); + + /** + * @deprecated Use {@link #stopMachine(ShutDownReason)} + */ + @Deprecated + public void stopMachine() { + stopMachine(ShutDownReasonRegistry.NONE); + } + + /** + * @deprecated Use {@link #stopMachine(ShutDownReason)} + */ + @Deprecated + public void criticalStopMachine() { + stopMachine(ShutDownReasonRegistry.CRITICAL_NONE); + } + + public void stopMachine(@Nonnull ShutDownReason reason) { + if (!ShutDownReasonRegistry.isRegistered(reason.getID())) { + throw new RuntimeException(String.format("Reason %s is not registered for registry", reason.getID())); + } + mOutputItems = null; + mOutputFluids = null; + mEUt = 0; + mEfficiency = 0; + mProgresstime = 0; + mMaxProgresstime = 0; + mEfficiencyIncrease = 0; + getBaseMetaTileEntity().disableWorking(); + getBaseMetaTileEntity().setShutDownReason(reason); + getBaseMetaTileEntity().setShutdownStatus(true); + if (reason.wasCritical()) { + sendSound(INTERRUPT_SOUND_INDEX); + } + } + + public int getRepairStatus() { + return (mWrench ? 1 : 0) + (mScrewdriver ? 1 : 0) + + (mSoftHammer ? 1 : 0) + + (mHardHammer ? 1 : 0) + + (mSolderingTool ? 1 : 0) + + (mCrowbar ? 1 : 0); + } + + public int getIdealStatus() { + return 6; + } + + public int getCurrentEfficiency(ItemStack itemStack) { + int maxEff = getMaxEfficiency(itemStack); + return maxEff - (getIdealStatus() - getRepairStatus()) * maxEff / 10; + } + + public boolean doRandomMaintenanceDamage() { + if (!isCorrectMachinePart(mInventory[1])) { + stopMachine(ShutDownReasonRegistry.NO_MACHINE_PART); + return false; + } + if (getRepairStatus() == 0) { + stopMachine(ShutDownReasonRegistry.NO_REPAIR); + return false; + } + if (mRuntime++ > 1000) { + mRuntime = 0; + if (getBaseMetaTileEntity().getRandomNumber(6000) == 0) { + switch (getBaseMetaTileEntity().getRandomNumber(6)) { + case 0 -> mWrench = false; + case 1 -> mScrewdriver = false; + case 2 -> mSoftHammer = false; + case 3 -> mHardHammer = false; + case 4 -> mSolderingTool = false; + case 5 -> mCrowbar = false; + } + } + if (mInventory[1] != null && getBaseMetaTileEntity().getRandomNumber(2) == 0 + && !mInventory[1].getUnlocalizedName() + .startsWith("gt.blockmachines.basicmachine.")) { + if (mInventory[1].getItem() instanceof GT_MetaGenerated_Tool_01) { + NBTTagCompound tNBT = mInventory[1].getTagCompound(); + ((GT_MetaGenerated_Tool) mInventory[1].getItem()).doDamage( + mInventory[1], + (long) getDamageToComponent(mInventory[1]) + * (long) Math.min(mEUt / this.damageFactorLow, Math.pow(mEUt, this.damageFactorHigh))); + if (mInventory[1].stackSize == 0) mInventory[1] = null; + } + } + } + return true; + } + + public void explodeMultiblock() { + + GT_Log.exp.println( + "MultiBlockExplosion at: " + this.getBaseMetaTileEntity() + .getXCoord() + + " | " + + this.getBaseMetaTileEntity() + .getYCoord() + + " | " + + this.getBaseMetaTileEntity() + .getZCoord() + + " DIMID: " + + this.getBaseMetaTileEntity() + .getWorld().provider.dimensionId + + "."); + + GT_Pollution.addPollution(getBaseMetaTileEntity(), GT_Mod.gregtechproxy.mPollutionOnExplosion); + mInventory[1] = null; + // noinspection unchecked // In this case, the inspection only indicates that the array can be abused in runtime + Iterable<MetaTileEntity> allHatches = Iterables.concat( + mInputBusses, + mOutputBusses, + mInputHatches, + mOutputHatches, + mDynamoHatches, + mMufflerHatches, + mEnergyHatches, + mMaintenanceHatches); + for (MetaTileEntity tTileEntity : allHatches) { + if (tTileEntity != null && tTileEntity.getBaseMetaTileEntity() != null) { + tTileEntity.getBaseMetaTileEntity() + .doExplosion(V[8]); + } + } + getBaseMetaTileEntity().doExplosion(V[8]); + } + + public boolean addEnergyOutput(long aEU) { + if (aEU <= 0) { + return true; + } + if (mDynamoHatches.size() > 0) { + return addEnergyOutputMultipleDynamos(aEU, true); + } + return false; + } + + public boolean addEnergyOutputMultipleDynamos(long aEU, boolean aAllowMixedVoltageDynamos) { + int injected = 0; + long totalOutput = 0; + long aFirstVoltageFound = -1; + boolean aFoundMixedDynamos = false; + for (GT_MetaTileEntity_Hatch_Dynamo aDynamo : filterValidMTEs(mDynamoHatches)) { + long aVoltage = aDynamo.maxEUOutput(); + long aTotal = aDynamo.maxAmperesOut() * aVoltage; + // Check against voltage to check when hatch mixing + if (aFirstVoltageFound == -1) { + aFirstVoltageFound = aVoltage; + } else { + if (aFirstVoltageFound != aVoltage) { + aFoundMixedDynamos = true; + } + } + totalOutput += aTotal; + } + + if (totalOutput < aEU || (aFoundMixedDynamos && !aAllowMixedVoltageDynamos)) { + explodeMultiblock(); + return false; + } + + long leftToInject; + long aVoltage; + int aAmpsToInject; + int aRemainder; + int ampsOnCurrentHatch; + for (GT_MetaTileEntity_Hatch_Dynamo aDynamo : filterValidMTEs(mDynamoHatches)) { + leftToInject = aEU - injected; + aVoltage = aDynamo.maxEUOutput(); + aAmpsToInject = (int) (leftToInject / aVoltage); + aRemainder = (int) (leftToInject - (aAmpsToInject * aVoltage)); + ampsOnCurrentHatch = (int) Math.min(aDynamo.maxAmperesOut(), aAmpsToInject); + for (int i = 0; i < ampsOnCurrentHatch; i++) { + aDynamo.getBaseMetaTileEntity() + .increaseStoredEnergyUnits(aVoltage, false); + } + injected += aVoltage * ampsOnCurrentHatch; + if (aRemainder > 0 && ampsOnCurrentHatch < aDynamo.maxAmperesOut()) { + aDynamo.getBaseMetaTileEntity() + .increaseStoredEnergyUnits(aRemainder, false); + injected += aRemainder; + } + } + return injected > 0; + } + + /** + * Sums up voltage of energy hatches. Amperage does not matter. + */ + public long getMaxInputVoltage() { + long rVoltage = 0; + for (GT_MetaTileEntity_Hatch_Energy tHatch : filterValidMTEs(mEnergyHatches)) + rVoltage += tHatch.getBaseMetaTileEntity() + .getInputVoltage(); + return rVoltage; + } + + public long getAverageInputVoltage() { + return GT_ExoticEnergyInputHelper.getAverageInputVoltageMulti(mEnergyHatches); + } + + public long getMaxInputAmps() { + return GT_ExoticEnergyInputHelper.getMaxWorkingInputAmpsMulti(mEnergyHatches); + } + + public long getMaxInputEu() { + return GT_ExoticEnergyInputHelper.getTotalEuMulti(mEnergyHatches); + } + + /** + * Sums up max input EU/t of energy hatches, amperage included. + */ + public long getMaxInputPower() { + long eut = 0; + for (GT_MetaTileEntity_Hatch_Energy tHatch : filterValidMTEs(mEnergyHatches)) { + IGregTechTileEntity baseTile = tHatch.getBaseMetaTileEntity(); + eut += baseTile.getInputVoltage() * baseTile.getInputAmperage(); + } + return eut; + } + + /** + * Returns voltage tier of energy hatches. If multiple tiers are found, returns 0. + */ + public long getInputVoltageTier() { + long rTier = 0; + if (mEnergyHatches.size() > 0) { + rTier = mEnergyHatches.get(0) + .getInputTier(); + for (int i = 1; i < mEnergyHatches.size(); i++) { + if (mEnergyHatches.get(i) + .getInputTier() != rTier) return 0; + } + } + + return rTier; + } + + /** + * Calcualtes the overclockedness using long integers + * + * @param aEUt - recipe EUt + * @param aDuration - recipe Duration + * @param mAmperage - should be 1 ? + * @param maxInputVoltage - Multiblock Max input voltage. Voltage is rounded up to higher tier voltage. + * @param perfectOC - If the Multiblock OCs perfectly, i.e. the large Chemical Reactor + */ + protected void calculateOverclockedNessMultiInternal(long aEUt, int aDuration, int mAmperage, long maxInputVoltage, + boolean perfectOC) { + byte tier = (byte) Math.max(0, GT_Utility.getTier(maxInputVoltage)); + GT_OverclockCalculator calculator = new GT_OverclockCalculator().setRecipeEUt(aEUt) + .setEUt(V[tier] * mAmperage) + .setDuration(aDuration) + .setDurationDecreasePerOC(perfectOC ? 2 : 1) + .calculate(); + mEUt = (int) calculator.getConsumption(); + mMaxProgresstime = calculator.getDuration(); + } + + @Deprecated + protected void calculateOverclockedNessMulti(int aEUt, int aDuration, int mAmperage, long maxInputVoltage) { + calculateOverclockedNessMultiInternal(aEUt, aDuration, mAmperage, maxInputVoltage, false); + } + + protected void calculateOverclockedNessMulti(long aEUt, int aDuration, int mAmperage, long maxInputVoltage) { + calculateOverclockedNessMultiInternal(aEUt, aDuration, mAmperage, maxInputVoltage, false); + } + + @Deprecated + protected void calculatePerfectOverclockedNessMulti(int aEUt, int aDuration, int mAmperage, long maxInputVoltage) { + calculateOverclockedNessMultiInternal(aEUt, aDuration, mAmperage, maxInputVoltage, true); + } + + protected void calculatePerfectOverclockedNessMulti(long aEUt, int aDuration, int mAmperage, long maxInputVoltage) { + calculateOverclockedNessMultiInternal(aEUt, aDuration, mAmperage, maxInputVoltage, true); + } + + public boolean drainEnergyInput(long aEU) { + if (aEU <= 0) return true; + for (GT_MetaTileEntity_Hatch_Energy tHatch : filterValidMTEs(mEnergyHatches)) { + if (tHatch.getBaseMetaTileEntity() + .decreaseStoredEnergyUnits(aEU, false)) return true; + } + return false; + } + + protected static boolean dumpFluid(List<GT_MetaTileEntity_Hatch_Output> aOutputHatches, FluidStack copiedFluidStack, + boolean restrictiveHatchesOnly) { + for (GT_MetaTileEntity_Hatch_Output tHatch : filterValidMTEs(aOutputHatches)) { + if (restrictiveHatchesOnly && tHatch.mMode == 0) { + continue; + } + if (!tHatch.canStoreFluid(copiedFluidStack)) continue; + int tAmount = tHatch.fill(copiedFluidStack, false); + if (tAmount >= copiedFluidStack.amount) { + boolean filled = tHatch.fill(copiedFluidStack, true) >= copiedFluidStack.amount; + tHatch.onEmptyingContainerWhenEmpty(); + return filled; + } else if (tAmount > 0) { + copiedFluidStack.amount = copiedFluidStack.amount - tHatch.fill(copiedFluidStack, true); + tHatch.onEmptyingContainerWhenEmpty(); + } + } + return false; + } + + public boolean addOutput(FluidStack aLiquid) { + if (aLiquid == null) return false; + FluidStack copiedFluidStack = aLiquid.copy(); + if (!dumpFluid(mOutputHatches, copiedFluidStack, true)) { + dumpFluid(mOutputHatches, copiedFluidStack, false); + } + return false; + } + + protected void addFluidOutputs(FluidStack[] mOutputFluids2) { + for (FluidStack outputFluidStack : mOutputFluids2) { + addOutput(outputFluidStack); + } + } + + public boolean depleteInput(FluidStack aLiquid) { + return depleteInput(aLiquid, false); + } + + public boolean depleteInput(FluidStack aLiquid, boolean simulate) { + if (aLiquid == null) return false; + for (GT_MetaTileEntity_Hatch_Input tHatch : filterValidMTEs(mInputHatches)) { + setHatchRecipeMap(tHatch); + FluidStack tLiquid = tHatch.drain(ForgeDirection.UNKNOWN, aLiquid, false); + if (tLiquid != null && tLiquid.amount >= aLiquid.amount) { + if (simulate) { + return true; + } + tLiquid = tHatch.drain(ForgeDirection.UNKNOWN, aLiquid, true); + return tLiquid != null && tLiquid.amount >= aLiquid.amount; + } + } + return false; + } + + public boolean addOutput(ItemStack aStack) { + if (GT_Utility.isStackInvalid(aStack)) return false; + aStack = GT_Utility.copyOrNull(aStack); + for (GT_MetaTileEntity_Hatch_OutputBus tHatch : filterValidMTEs(mOutputBusses)) { + if (tHatch.storeAll(aStack)) { + return true; + } + } + boolean outputSuccess = true; + while (outputSuccess && aStack.stackSize > 0) { + outputSuccess = false; + ItemStack single = aStack.splitStack(1); + for (GT_MetaTileEntity_Hatch_Output tHatch : filterValidMTEs(mOutputHatches)) { + if (!outputSuccess && tHatch.outputsItems()) { + if (tHatch.getBaseMetaTileEntity() + .addStackToSlot(1, single)) outputSuccess = true; + } + } + } + return outputSuccess; + } + + public boolean depleteInput(ItemStack aStack) { + if (GT_Utility.isStackInvalid(aStack)) return false; + FluidStack aLiquid = GT_Utility.getFluidForFilledItem(aStack, true); + if (aLiquid != null) return depleteInput(aLiquid); + for (GT_MetaTileEntity_Hatch_Input tHatch : filterValidMTEs(mInputHatches)) { + setHatchRecipeMap(tHatch); + if (GT_Utility.areStacksEqual( + aStack, + tHatch.getBaseMetaTileEntity() + .getStackInSlot(0))) { + if (tHatch.getBaseMetaTileEntity() + .getStackInSlot(0).stackSize >= aStack.stackSize) { + tHatch.getBaseMetaTileEntity() + .decrStackSize(0, aStack.stackSize); + return true; + } + } + } + for (GT_MetaTileEntity_Hatch_InputBus tHatch : filterValidMTEs(mInputBusses)) { + tHatch.mRecipeMap = getRecipeMap(); + for (int i = tHatch.getBaseMetaTileEntity() + .getSizeInventory() - 1; i >= 0; i--) { + if (GT_Utility.areStacksEqual( + aStack, + tHatch.getBaseMetaTileEntity() + .getStackInSlot(i))) { + if (tHatch.getBaseMetaTileEntity() + .getStackInSlot(i).stackSize >= aStack.stackSize) { + tHatch.getBaseMetaTileEntity() + .decrStackSize(i, aStack.stackSize); + return true; + } + } + } + } + return false; + } + + public ArrayList<ItemStack> getStoredOutputs() { + ArrayList<ItemStack> rList = new ArrayList<>(); + for (GT_MetaTileEntity_Hatch_OutputBus tHatch : filterValidMTEs(mOutputBusses)) { + for (int i = tHatch.getBaseMetaTileEntity() + .getSizeInventory() - 1; i >= 0; i--) { + rList.add( + tHatch.getBaseMetaTileEntity() + .getStackInSlot(i)); + } + } + return rList; + } + + public ArrayList<FluidStack> getStoredFluids() { + ArrayList<FluidStack> rList = new ArrayList<>(); + Map<Fluid, FluidStack> inputsFromME = new HashMap<>(); + for (GT_MetaTileEntity_Hatch_Input tHatch : filterValidMTEs(mInputHatches)) { + setHatchRecipeMap(tHatch); + if (tHatch instanceof GT_MetaTileEntity_Hatch_MultiInput multiInputHatch) { + for (FluidStack tFluid : multiInputHatch.getStoredFluid()) { + if (tFluid != null) { + rList.add(tFluid); + } + } + } else if (tHatch instanceof GT_MetaTileEntity_Hatch_Input_ME meHatch) { + for (FluidStack fluidStack : meHatch.getStoredFluids()) { + if (fluidStack != null) { + // Prevent the same fluid from different ME hatches from being recognized + inputsFromME.put(fluidStack.getFluid(), fluidStack); + } + } + } else { + if (tHatch.getFillableStack() != null) { + rList.add(tHatch.getFillableStack()); + } + } + } + + if (!inputsFromME.isEmpty()) { + rList.addAll(inputsFromME.values()); + } + return rList; + } + + /** + * Drains fluid from the given hatch, including {@link IDualInputHatch}. Should never be used during recipe check! + * + * @param doDrain If false, fluid will not actually be consumed + * @return Whether the hatch contains enough fluid to drain + */ + public boolean drain(GT_MetaTileEntity_Hatch hatch, FluidStack fluid, boolean doDrain) { + if (fluid == null || hatch == null) return false; + if (supportsCraftingMEBuffer() && hatch instanceof IDualInputHatch tHatch && tHatch.supportsFluids()) { + Optional<IDualInputInventory> inventory = tHatch.getFirstNonEmptyInventory(); + if (inventory.isPresent()) { + for (FluidStack storedFluid : Lists.newArrayList( + inventory.get() + .getFluidInputs())) { + if (fluid.isFluidEqual(storedFluid)) { + if (doDrain) storedFluid.amount = Math.max(storedFluid.amount - fluid.amount, 0); + return storedFluid.amount >= fluid.amount; + } + } + } + } + + if (hatch instanceof GT_MetaTileEntity_Hatch_Input tHatch && tHatch.isValid()) { + if (tHatch instanceof GT_MetaTileEntity_Hatch_Input_ME meHatch) { + meHatch.startRecipeProcessing(); + FluidStack tFluid = meHatch.drain(ForgeDirection.UNKNOWN, fluid, doDrain); + meHatch.endRecipeProcessing(this); + return tFluid != null && tFluid.amount >= fluid.amount; + } else { + FluidStack tFluid = tHatch.drain(ForgeDirection.UNKNOWN, fluid, doDrain); + return tFluid != null && tFluid.amount >= fluid.amount; + } + } + + return false; + } + + public ArrayList<ItemStack> getStoredInputs() { + ArrayList<ItemStack> rList = new ArrayList<>(); + Map<GT_Utility.ItemId, ItemStack> inputsFromME = new HashMap<>(); + for (GT_MetaTileEntity_Hatch_InputBus tHatch : filterValidMTEs(mInputBusses)) { + if (tHatch instanceof GT_MetaTileEntity_Hatch_CraftingInput_ME) { + continue; + } + tHatch.mRecipeMap = getRecipeMap(); + IGregTechTileEntity tileEntity = tHatch.getBaseMetaTileEntity(); + boolean isMEBus = tHatch instanceof GT_MetaTileEntity_Hatch_InputBus_ME; + for (int i = tileEntity.getSizeInventory() - 1; i >= 0; i--) { + ItemStack itemStack = tileEntity.getStackInSlot(i); + if (itemStack != null) { + if (isMEBus) { + // Prevent the same item from different ME buses from being recognized + inputsFromME.put(GT_Utility.ItemId.createNoCopy(itemStack), itemStack); + } else { + rList.add(itemStack); + } + } + } + } + + if (getStackInSlot(1) != null && getStackInSlot(1).getUnlocalizedName() + .startsWith("gt.integrated_circuit")) rList.add(getStackInSlot(1)); + if (!inputsFromME.isEmpty()) { + rList.addAll(inputsFromME.values()); + } + return rList; + } + + /** + * Anything that is usually separated off in {@link #getStoredInputs()} (like crafting input bus/buffer) is also + * included here. + */ + public ArrayList<ItemStack> getAllStoredInputs() { + ArrayList<ItemStack> rList = new ArrayList<>(); + + if (supportsCraftingMEBuffer()) { + for (IDualInputHatch dualInputHatch : mDualInputHatches) { + Iterator<? extends IDualInputInventory> inventoryIterator = dualInputHatch.inventories(); + while (inventoryIterator.hasNext()) { + ItemStack[] items = inventoryIterator.next() + .getItemInputs(); + if (items == null) { + continue; + } + for (int i = 0; i < items.length; i++) { + ItemStack item = items[i]; + if (item != null) { + rList.add(item); + } + } + } + } + } + + Map<GT_Utility.ItemId, ItemStack> inputsFromME = new HashMap<>(); + for (GT_MetaTileEntity_Hatch_InputBus tHatch : filterValidMTEs(mInputBusses)) { + if (tHatch instanceof GT_MetaTileEntity_Hatch_CraftingInput_ME) { + continue; + } + tHatch.mRecipeMap = getRecipeMap(); + IGregTechTileEntity tileEntity = tHatch.getBaseMetaTileEntity(); + boolean isMEBus = tHatch instanceof GT_MetaTileEntity_Hatch_InputBus_ME; + for (int i = tileEntity.getSizeInventory() - 1; i >= 0; i--) { + ItemStack itemStack = tileEntity.getStackInSlot(i); + if (itemStack != null) { + if (isMEBus) { + // Prevent the same item from different ME buses from being recognized + inputsFromME.put(GT_Utility.ItemId.createNoCopy(itemStack), itemStack); + } else { + rList.add(itemStack); + } + } + } + } + + if (getStackInSlot(1) != null && getStackInSlot(1).getUnlocalizedName() + .startsWith("gt.integrated_circuit")) rList.add(getStackInSlot(1)); + if (!inputsFromME.isEmpty()) { + rList.addAll(inputsFromME.values()); + } + return rList; + } + + public Map<GT_Utility.ItemId, ItemStack> getStoredInputsFromME() { + Map<GT_Utility.ItemId, ItemStack> inputsFromME = new Object2ReferenceOpenHashMap<>(); + for (GT_MetaTileEntity_Hatch_InputBus tHatch : filterValidMTEs(mInputBusses)) { + if (tHatch instanceof GT_MetaTileEntity_Hatch_InputBus_ME meBus) { + for (int i = meBus.getSizeInventory() - 1; i >= 0; i--) { + ItemStack itemStack = meBus.getStackInSlot(i); + if (itemStack != null) { + // Prevent the same item from different ME buses from being recognized + inputsFromME.put(GT_Utility.ItemId.createNoCopy(itemStack), itemStack); + } + } + } + } + return inputsFromME; + } + + public Map<Fluid, FluidStack> getStoredFluidsFromME() { + Map<Fluid, FluidStack> fluidsFromME = new Reference2ReferenceOpenHashMap<>(); + for (GT_MetaTileEntity_Hatch_Input tHatch : filterValidMTEs(mInputHatches)) { + if (tHatch instanceof GT_MetaTileEntity_Hatch_Input_ME meHatch) { + for (FluidStack fluid : meHatch.getStoredFluids()) { + if (fluid != null) { + // Prevent the same fluid from different ME hatches from being recognized + fluidsFromME.put(fluid.getFluid(), fluid); + } + } + } + } + return fluidsFromME; + } + + @Override + public RecipeMap<?> getRecipeMap() { + return null; + } + + /** + * Creates logic to run recipe check based on recipemap. This runs only once, on class instantiation. + * <p> + * If this machine doesn't use recipemap or does some complex things, override {@link #checkProcessing()}. + */ + @ApiStatus.OverrideOnly + protected ProcessingLogic createProcessingLogic() { + return null; + } + + public void updateSlots() { + for (GT_MetaTileEntity_Hatch_Input tHatch : filterValidMTEs(mInputHatches)) tHatch.updateSlots(); + for (GT_MetaTileEntity_Hatch_InputBus tHatch : filterValidMTEs(mInputBusses)) tHatch.updateSlots(); + } + + protected void startRecipeProcessing() { + for (GT_MetaTileEntity_Hatch_InputBus hatch : filterValidMTEs(mInputBusses)) { + if (hatch instanceof IRecipeProcessingAwareHatch aware) { + aware.startRecipeProcessing(); + } + } + for (GT_MetaTileEntity_Hatch_Input hatch : filterValidMTEs(mInputHatches)) { + if (hatch instanceof IRecipeProcessingAwareHatch aware) { + aware.startRecipeProcessing(); + } + } + } + + public void setResultIfFailure(CheckRecipeResult result) { + if (!result.wasSuccessful()) { + this.checkRecipeResult = result; + } + } + + protected void endRecipeProcessing() { + for (GT_MetaTileEntity_Hatch_InputBus hatch : filterValidMTEs(mInputBusses)) { + if (hatch instanceof IRecipeProcessingAwareHatch aware) { + setResultIfFailure(aware.endRecipeProcessing(this)); + } + } + for (GT_MetaTileEntity_Hatch_Input hatch : filterValidMTEs(mInputHatches)) { + if (hatch instanceof IRecipeProcessingAwareHatch aware) { + setResultIfFailure(aware.endRecipeProcessing(this)); + } + } + } + + public boolean addToMachineList(IGregTechTileEntity aTileEntity, int aBaseCasingIndex) { + if (aTileEntity == null) return false; + IMetaTileEntity aMetaTileEntity = aTileEntity.getMetaTileEntity(); + if (aMetaTileEntity == null) return false; + if (aMetaTileEntity instanceof GT_MetaTileEntity_Hatch hatch) { + hatch.updateTexture(aBaseCasingIndex); + hatch.updateCraftingIcon(this.getMachineCraftingIcon()); + } + if (aMetaTileEntity instanceof IDualInputHatch hatch) { + hatch.updateCraftingIcon(this.getMachineCraftingIcon()); + return mDualInputHatches.add(hatch); + } + if (aMetaTileEntity instanceof GT_MetaTileEntity_Hatch_Input) { + setHatchRecipeMap((GT_MetaTileEntity_Hatch_Input) aMetaTileEntity); + return mInputHatches.add((GT_MetaTileEntity_Hatch_Input) aMetaTileEntity); + } + if (aMetaTileEntity instanceof GT_MetaTileEntity_Hatch_InputBus) { + ((GT_MetaTileEntity_Hatch_InputBus) aMetaTileEntity).mRecipeMap = getRecipeMap(); + return mInputBusses.add((GT_MetaTileEntity_Hatch_InputBus) aMetaTileEntity); + } + if (aMetaTileEntity instanceof GT_MetaTileEntity_Hatch_Output) + return mOutputHatches.add((GT_MetaTileEntity_Hatch_Output) aMetaTileEntity); + if (aMetaTileEntity instanceof GT_MetaTileEntity_Hatch_OutputBus) + return mOutputBusses.add((GT_MetaTileEntity_Hatch_OutputBus) aMetaTileEntity); + if (aMetaTileEntity instanceof GT_MetaTileEntity_Hatch_Energy) + return mEnergyHatches.add((GT_MetaTileEntity_Hatch_Energy) aMetaTileEntity); + if (aMetaTileEntity instanceof GT_MetaTileEntity_Hatch_Dynamo) + return mDynamoHatches.add((GT_MetaTileEntity_Hatch_Dynamo) aMetaTileEntity); + if (aMetaTileEntity instanceof GT_MetaTileEntity_Hatch_Maintenance) + return mMaintenanceHatches.add((GT_MetaTileEntity_Hatch_Maintenance) aMetaTileEntity); + if (aMetaTileEntity instanceof GT_MetaTileEntity_Hatch_Muffler) + return mMufflerHatches.add((GT_MetaTileEntity_Hatch_Muffler) aMetaTileEntity); + return false; + } + + public boolean addMaintenanceToMachineList(IGregTechTileEntity aTileEntity, int aBaseCasingIndex) { + if (aTileEntity == null) return false; + IMetaTileEntity aMetaTileEntity = aTileEntity.getMetaTileEntity(); + if (aMetaTileEntity == null) return false; + if (aMetaTileEntity instanceof GT_MetaTileEntity_Hatch_Maintenance hatch) { + hatch.updateTexture(aBaseCasingIndex); + hatch.updateCraftingIcon(this.getMachineCraftingIcon()); + return mMaintenanceHatches.add(hatch); + } + return false; + } + + public boolean addEnergyInputToMachineList(IGregTechTileEntity aTileEntity, int aBaseCasingIndex) { + if (aTileEntity == null) { + return false; + } + IMetaTileEntity aMetaTileEntity = aTileEntity.getMetaTileEntity(); + if (aMetaTileEntity == null) return false; + if (aMetaTileEntity instanceof GT_MetaTileEntity_Hatch_Energy hatch) { + hatch.updateTexture(aBaseCasingIndex); + hatch.updateCraftingIcon(this.getMachineCraftingIcon()); + return mEnergyHatches.add(hatch); + } + return false; + } + + public boolean addExoticEnergyInputToMachineList(IGregTechTileEntity aTileEntity, int aBaseCasingIndex) { + if (aTileEntity == null) return false; + IMetaTileEntity aMetaTileEntity = aTileEntity.getMetaTileEntity(); + if (aMetaTileEntity == null) return false; + if (aMetaTileEntity instanceof GT_MetaTileEntity_Hatch hatch + && GT_ExoticEnergyInputHelper.isExoticEnergyInput(aMetaTileEntity)) { + hatch.updateTexture(aBaseCasingIndex); + hatch.updateCraftingIcon(this.getMachineCraftingIcon()); + return mExoticEnergyHatches.add(hatch); + } + return false; + } + + public boolean addDynamoToMachineList(IGregTechTileEntity aTileEntity, int aBaseCasingIndex) { + if (aTileEntity == null) return false; + IMetaTileEntity aMetaTileEntity = aTileEntity.getMetaTileEntity(); + if (aMetaTileEntity == null) return false; + if (aMetaTileEntity instanceof GT_MetaTileEntity_Hatch_Dynamo hatch) { + hatch.updateTexture(aBaseCasingIndex); + hatch.updateCraftingIcon(this.getMachineCraftingIcon()); + return mDynamoHatches.add(hatch); + } + return false; + } + + public boolean addMufflerToMachineList(IGregTechTileEntity aTileEntity, int aBaseCasingIndex) { + if (aTileEntity == null) return false; + IMetaTileEntity aMetaTileEntity = aTileEntity.getMetaTileEntity(); + if (aMetaTileEntity == null) return false; + if (aMetaTileEntity instanceof GT_MetaTileEntity_Hatch_Muffler hatch) { + hatch.updateTexture(aBaseCasingIndex); + hatch.updateCraftingIcon(this.getMachineCraftingIcon()); + return mMufflerHatches.add(hatch); + } + return false; + } + + public boolean addInputToMachineList(IGregTechTileEntity aTileEntity, int aBaseCasingIndex) { + return addInputBusToMachineList(aTileEntity, aBaseCasingIndex) + || addInputHatchToMachineList(aTileEntity, aBaseCasingIndex); + } + + public boolean addOutputToMachineList(IGregTechTileEntity aTileEntity, int aBaseCasingIndex) { + return addOutputBusToMachineList(aTileEntity, aBaseCasingIndex) + || addOutputHatchToMachineList(aTileEntity, aBaseCasingIndex); + } + + public boolean addInputBusToMachineList(IGregTechTileEntity aTileEntity, int aBaseCasingIndex) { + if (aTileEntity == null) return false; + IMetaTileEntity aMetaTileEntity = aTileEntity.getMetaTileEntity(); + if (aMetaTileEntity == null) return false; + if (aMetaTileEntity instanceof IDualInputHatch hatch) { + if (!supportsCraftingMEBuffer()) return false; + hatch.updateTexture(aBaseCasingIndex); + hatch.updateCraftingIcon(this.getMachineCraftingIcon()); + return mDualInputHatches.add(hatch); + } + + if (aMetaTileEntity instanceof GT_MetaTileEntity_Hatch_InputBus hatch) { + hatch.updateTexture(aBaseCasingIndex); + hatch.updateCraftingIcon(this.getMachineCraftingIcon()); + hatch.mRecipeMap = getRecipeMap(); + return mInputBusses.add(hatch); + } + return false; + } + + public boolean addOutputBusToMachineList(IGregTechTileEntity aTileEntity, int aBaseCasingIndex) { + if (aTileEntity == null) return false; + IMetaTileEntity aMetaTileEntity = aTileEntity.getMetaTileEntity(); + if (aMetaTileEntity == null) return false; + if (aMetaTileEntity instanceof GT_MetaTileEntity_Hatch_OutputBus hatch) { + hatch.updateTexture(aBaseCasingIndex); + hatch.updateCraftingIcon(this.getMachineCraftingIcon()); + return mOutputBusses.add(hatch); + } + return false; + } + + public boolean addInputHatchToMachineList(IGregTechTileEntity aTileEntity, int aBaseCasingIndex) { + if (aTileEntity == null) return false; + IMetaTileEntity aMetaTileEntity = aTileEntity.getMetaTileEntity(); + if (aMetaTileEntity == null) return false; + if (aMetaTileEntity instanceof GT_MetaTileEntity_Hatch_Input hatch) { + hatch.updateTexture(aBaseCasingIndex); + hatch.updateCraftingIcon(this.getMachineCraftingIcon()); + setHatchRecipeMap(hatch); + return mInputHatches.add(hatch); + } + return false; + } + + public boolean addOutputHatchToMachineList(IGregTechTileEntity aTileEntity, int aBaseCasingIndex) { + if (aTileEntity == null) return false; + IMetaTileEntity aMetaTileEntity = aTileEntity.getMetaTileEntity(); + if (aMetaTileEntity == null) return false; + if (aMetaTileEntity instanceof GT_MetaTileEntity_Hatch_Output hatch) { + hatch.updateTexture(aBaseCasingIndex); + hatch.updateCraftingIcon(this.getMachineCraftingIcon()); + return mOutputHatches.add(hatch); + } + return false; + } + + protected void setHatchRecipeMap(GT_MetaTileEntity_Hatch_Input hatch) { + if (filtersFluid()) { + hatch.mRecipeMap = getRecipeMap(); + } + } + + /** + * @return If this multi filters fluid input for hatches based on recipemap. + */ + protected boolean filtersFluid() { + return true; + } + + @Override + public String[] getInfoData() { + int mPollutionReduction = 0; + for (GT_MetaTileEntity_Hatch_Muffler tHatch : filterValidMTEs(mMufflerHatches)) { + mPollutionReduction = Math.max(tHatch.calculatePollutionReduction(100), mPollutionReduction); + } + + long storedEnergy = 0; + long maxEnergy = 0; + for (GT_MetaTileEntity_Hatch_Energy tHatch : filterValidMTEs(mEnergyHatches)) { + storedEnergy += tHatch.getBaseMetaTileEntity() + .getStoredEU(); + maxEnergy += tHatch.getBaseMetaTileEntity() + .getEUCapacity(); + } + + return new String[] { + /* 1 */ StatCollector.translateToLocal("GT5U.multiblock.Progress") + ": " + + EnumChatFormatting.GREEN + + formatNumbers(mProgresstime / 20) + + EnumChatFormatting.RESET + + " s / " + + EnumChatFormatting.YELLOW + + formatNumbers(mMaxProgresstime / 20) + + EnumChatFormatting.RESET + + " s", + /* 2 */ StatCollector.translateToLocal("GT5U.multiblock.energy") + ": " + + EnumChatFormatting.GREEN + + formatNumbers(storedEnergy) + + EnumChatFormatting.RESET + + " EU / " + + EnumChatFormatting.YELLOW + + formatNumbers(maxEnergy) + + EnumChatFormatting.RESET + + " EU", + /* 3 */ StatCollector.translateToLocal("GT5U.multiblock.usage") + ": " + + EnumChatFormatting.RED + + formatNumbers(getActualEnergyUsage()) + + EnumChatFormatting.RESET + + " EU/t", + /* 4 */ StatCollector.translateToLocal("GT5U.multiblock.mei") + ": " + + EnumChatFormatting.YELLOW + + formatNumbers(getMaxInputVoltage()) + + EnumChatFormatting.RESET + + " EU/t(*2A) " + + StatCollector.translateToLocal("GT5U.machines.tier") + + ": " + + EnumChatFormatting.YELLOW + + VN[GT_Utility.getTier(getMaxInputVoltage())] + + EnumChatFormatting.RESET, + /* 5 */ StatCollector.translateToLocal("GT5U.multiblock.problems") + ": " + + EnumChatFormatting.RED + + (getIdealStatus() - getRepairStatus()) + + EnumChatFormatting.RESET + + " " + + StatCollector.translateToLocal("GT5U.multiblock.efficiency") + + ": " + + EnumChatFormatting.YELLOW + + mEfficiency / 100.0F + + EnumChatFormatting.RESET + + " %", + /* 6 */ StatCollector.translateToLocal("GT5U.multiblock.pollution") + ": " + + EnumChatFormatting.GREEN + + mPollutionReduction + + EnumChatFormatting.RESET + + " %" }; + } + + @Override + public boolean isGivingInformation() { + return true; + } + + @Override + public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side, + ItemStack aStack) { + return false; + } + + @Override + public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side, + ItemStack aStack) { + return false; + } + + protected ItemStack[] getCompactedInputs() { + // TODO: repalce method with a cleaner one + ArrayList<ItemStack> tInputList = getStoredInputs(); + int tInputList_sS = tInputList.size(); + for (int i = 0; i < tInputList_sS - 1; i++) { + for (int j = i + 1; j < tInputList_sS; j++) { + if (!GT_Utility.areStacksEqual(tInputList.get(i), tInputList.get(j))) continue; + if (tInputList.get(i).stackSize >= tInputList.get(j).stackSize) { + tInputList.remove(j--); + tInputList_sS = tInputList.size(); + } else { + tInputList.remove(i--); + tInputList_sS = tInputList.size(); + break; + } + } + } + return tInputList.toArray(new ItemStack[0]); + } + + protected FluidStack[] getCompactedFluids() { + // TODO: repalce method with a cleaner one + ArrayList<FluidStack> tFluidList = getStoredFluids(); + int tFluidList_sS = tFluidList.size(); + for (int i = 0; i < tFluidList_sS - 1; i++) { + for (int j = i + 1; j < tFluidList_sS; j++) { + if (!GT_Utility.areFluidsEqual(tFluidList.get(i), tFluidList.get(j))) continue; + + if (tFluidList.get(i).amount >= tFluidList.get(j).amount) { + tFluidList.remove(j--); + tFluidList_sS = tFluidList.size(); + } else { + tFluidList.remove(i--); + tFluidList_sS = tFluidList.size(); + break; + } + } + } + return tFluidList.toArray(new FluidStack[0]); + } + + @Override + public void getWailaBody(ItemStack itemStack, List<String> currentTip, IWailaDataAccessor accessor, + IWailaConfigHandler config) { + final NBTTagCompound tag = accessor.getNBTData(); + + if (tag.getBoolean("incompleteStructure")) { + currentTip.add(RED + "** INCOMPLETE STRUCTURE **" + RESET); + } + currentTip.add( + (tag.getBoolean("hasProblems") ? (RED + "** HAS PROBLEMS **") : GREEN + "Running Fine") + RESET + + " Efficiency: " + + tag.getFloat("efficiency") + + "%"); + + boolean isActive = tag.getBoolean("isActive"); + if (isActive) { + long energyTier = tag.getLong("energyTier"); + long actualEnergyUsage = tag.getLong("energyUsage"); + if (energyTier > 0) { + if (actualEnergyUsage > 0) { + currentTip.add( + StatCollector.translateToLocalFormatted( + "GT5U.waila.energy.use_with_amperage", + formatNumbers(actualEnergyUsage), + GT_Utility.getAmperageForTier(actualEnergyUsage, (byte) energyTier), + GT_Utility.getColoredTierNameFromTier((byte) energyTier))); + } else if (actualEnergyUsage < 0) { + currentTip.add( + StatCollector.translateToLocalFormatted( + "GT5U.waila.energy.produce_with_amperage", + formatNumbers(-actualEnergyUsage), + GT_Utility.getAmperageForTier(-actualEnergyUsage, (byte) energyTier), + GT_Utility.getColoredTierNameFromTier((byte) energyTier))); + } + } else { + if (actualEnergyUsage > 0) { + currentTip.add( + StatCollector.translateToLocalFormatted( + "GT5U.waila.energy.use", + formatNumbers(actualEnergyUsage), + GT_Utility.getColoredTierNameFromVoltage(actualEnergyUsage))); + } else if (actualEnergyUsage < 0) { + currentTip.add( + StatCollector.translateToLocalFormatted( + "GT5U.waila.energy.produce", + formatNumbers(-actualEnergyUsage), + GT_Utility.getColoredTierNameFromVoltage(-actualEnergyUsage))); + } + } + } + currentTip.add( + GT_Waila.getMachineProgressString(isActive, tag.getInteger("maxProgress"), tag.getInteger("progress"))); + // Show ns on the tooltip + if (GT_Mod.gregtechproxy.wailaAverageNS && tag.hasKey("averageNS")) { + int tAverageTime = tag.getInteger("averageNS"); + currentTip.add("Average CPU load of ~" + formatNumbers(tAverageTime) + " ns"); + } + super.getWailaBody(itemStack, currentTip, accessor, config); + } + + public final void getMTEWailaBody(ItemStack itemStack, List<String> currentTip, IWailaDataAccessor accessor, + IWailaConfigHandler config) { + super.getWailaBody(itemStack, currentTip, accessor, config); + } + + @Override + public void getWailaNBTData(EntityPlayerMP player, TileEntity tile, NBTTagCompound tag, World world, int x, int y, + int z) { + super.getWailaNBTData(player, tile, tag, world, x, y, z); + + tag.setBoolean("hasProblems", (getIdealStatus() - getRepairStatus()) > 0); + tag.setFloat("efficiency", mEfficiency / 100.0F); + tag.setInteger("progress", mProgresstime); + tag.setInteger("maxProgress", mMaxProgresstime); + tag.setBoolean("incompleteStructure", (getBaseMetaTileEntity().getErrorDisplayID() & 64) != 0); + + final IGregTechTileEntity tileEntity = getBaseMetaTileEntity(); + if (tileEntity != null) { + tag.setBoolean("isActive", tileEntity.isActive()); + if (tileEntity.isActive()) { + if (mEUt < 0) tag.setLong("energyUsage", getActualEnergyUsage()); + else tag.setLong("energyUsage", (long) -mEUt * mEfficiency / 10000); + tag.setLong("energyTier", getInputVoltageTier()); + } + } + + final GT_ClientPreference preference = GT_Mod.gregtechproxy.getClientPreference(player.getUniqueID()); + if (preference != null && preference.isWailaAverageNSEnabled()) { + getBaseMetaTileEntity().startTimeStatistics(); + int tAverageTime = 0; + int amountOfZero = 0; + for (int tTime : this.getBaseMetaTileEntity() + .getTimeStatistics()) { + tAverageTime += tTime; + if (tTime == 0) { + amountOfZero += 1; + } + } + + // tick time zero means it has not been updated yet + int samples = getBaseMetaTileEntity().getTimeStatistics().length - amountOfZero; + if (samples > 0) { + tag.setInteger("averageNS", tAverageTime / samples); + } + } + } + + protected void setMufflers(boolean state) { + for (GT_MetaTileEntity_Hatch_Muffler aMuffler : mMufflerHatches) { + final IGregTechTileEntity iGTTileEntity = aMuffler.getBaseMetaTileEntity(); + if (iGTTileEntity != null && !iGTTileEntity.isDead()) { + iGTTileEntity.setActive(state); + } + } + } + + @Override + public void onRemoval() { + super.onRemoval(); + // Deactivate mufflers + setMufflers(false); + } + + public List<GT_MetaTileEntity_Hatch> getExoticEnergyHatches() { + return mExoticEnergyHatches; + } + + /** + * @return Returns true if there is 1 TT Energy Hatch OR up to 2 Energy Hatches + */ + public boolean checkExoticAndNormalEnergyHatches() { + if (mExoticEnergyHatches.isEmpty() && mEnergyHatches.isEmpty()) { + return false; + } + + if (!mExoticEnergyHatches.isEmpty()) { + if (!mEnergyHatches.isEmpty()) { + return false; + } + + if (mExoticEnergyHatches.size() != 1) { + return false; + } + } + + return mEnergyHatches.size() <= 2; + } + + /** + * Checks if all the item / fluid outputs of the recipe can be outputted to the buses / hatches. + * If void protection is enabled, it also checks for {@link #protectsExcessItem()} and + * {@link #protectsExcessFluid()}, so you don't need to call them along with this method. + * <p> + * If you're using {@link GT_ParallelHelper}, it will handle void protection and return 0 parallel + * if all the output cannot be dumped into buses / hatches. In that case you won't use this method. + */ + protected boolean canOutputAll(@Nonnull GT_Recipe recipe) { + return canOutputAll(recipe.mOutputs, recipe.mFluidOutputs); + } + + /** + * Checks if all the items can be outputted to the output buses. + * If void protection is enabled, it also checks for {@link #protectsExcessItem()}, + * so you don't need to call it along with this method. + */ + @SuppressWarnings("BooleanMethodIsAlwaysInverted") + protected boolean canOutputAll(ItemStack[] items) { + return canOutputAll(items, null); + } + + /** + * Checks if all the fluids can be outputted to the output hatches. + * If void protection is enabled, it also checks for {@link #protectsExcessFluid()}, + * so you don't need to call it along with this method. + */ + protected boolean canOutputAll(FluidStack[] fluids) { + return canOutputAll(null, fluids); + } + + /** + * Checks if all the items / fluids can be outputted to output buses / hatches. + * If void protection is enabled, it also checks for {@link #protectsExcessItem()} and + * {@link #protectsExcessFluid()}, so you don't need to call them along with this method. + */ + protected boolean canOutputAll(@Nullable ItemStack[] items, @Nullable FluidStack[] fluids) { + if (!protectsExcessItem() && !protectsExcessFluid()) { + return true; + } + + VoidProtectionHelper voidProtectionHelper = new VoidProtectionHelper().setMachine(this) + .setItemOutputs(items) + .setFluidOutputs(fluids) + .build(); + return voidProtectionHelper.getMaxParallel() > 0; + } + + @Override + public boolean isAllowedToWork() { + return getBaseMetaTileEntity() != null && getBaseMetaTileEntity().isAllowedToWork(); + } + + @Override + public void disableWorking() { + if (getBaseMetaTileEntity() != null) { + getBaseMetaTileEntity().disableWorking(); + } + } + + @Override + public void enableWorking() { + if (getBaseMetaTileEntity() != null) { + getBaseMetaTileEntity().enableWorking(); + } + } + + public ItemStack getControllerSlot() { + return mInventory[1]; + } + + @Override + public Pos2d getPowerSwitchButtonPos() { + return new Pos2d(174, 148); + } + + @Override + public boolean supportsVoidProtection() { + return false; + } + + @Override + public VoidingMode getVoidingMode() { + return voidingMode; + } + + @Override + public void setVoidingMode(VoidingMode mode) { + this.voidingMode = mode; + } + + @Override + public List<ItemStack> getItemOutputSlots(ItemStack[] toOutput) { + List<ItemStack> ret = new ArrayList<>(); + for (final GT_MetaTileEntity_Hatch tBus : filterValidMTEs(mOutputBusses)) { + final IInventory tBusInv = tBus.getBaseMetaTileEntity(); + for (int i = 0; i < tBusInv.getSizeInventory(); i++) { + ret.add(tBus.getStackInSlot(i)); + } + } + return ret; + } + + @Override + public List<? extends IFluidStore> getFluidOutputSlots(FluidStack[] toOutput) { + return filterValidMTEs(mOutputHatches); + } + + /** + * Util method for DT-like structure to collect list of output hatches. + */ + protected <T extends GT_MetaTileEntity_Hatch_Output> List<? extends IFluidStore> getFluidOutputSlotsByLayer( + FluidStack[] toOutput, List<List<T>> hatchesByLayer) { + List<IFluidStore> ret = new ArrayList<>(); + for (int i = 0; i < toOutput.length; i++) { + if (i >= hatchesByLayer.size()) { + break; + } + FluidStack fluidOutputForLayer = toOutput[i]; + for (GT_MetaTileEntity_Hatch_Output hatch : hatchesByLayer.get(i)) { + if (!hatch.isValid()) continue; + if (fluidOutputForLayer != null) { + ret.add(new OutputHatchWrapper(hatch, f -> GT_Utility.areFluidsEqual(f, fluidOutputForLayer))); + } else { + ret.add(hatch); + } + } + } + return ret; + } + + @Override + public boolean canDumpItemToME() { + for (GT_MetaTileEntity_Hatch tHatch : filterValidMTEs(mOutputBusses)) { + if (tHatch instanceof GT_MetaTileEntity_Hatch_OutputBus_ME) { + return true; + } + } + return false; + } + + @Override + public boolean canDumpFluidToME() { + for (IFluidStore tHatch : getFluidOutputSlots(new FluidStack[0])) { + if (tHatch instanceof GT_MetaTileEntity_Hatch_Output_ME) { + return true; + } + } + return false; + } + + @Override + public Pos2d getVoidingModeButtonPos() { + return new Pos2d(8, 91); + } + + @Override + public boolean supportsInputSeparation() { + return false; + } + + @Override + public boolean isInputSeparationEnabled() { + return inputSeparation; + } + + @Override + public void setInputSeparation(boolean enabled) { + this.inputSeparation = enabled; + } + + @Override + public Pos2d getInputSeparationButtonPos() { + return new Pos2d(26, 91); + } + + @Override + public boolean supportsBatchMode() { + return false; + } + + @Override + public boolean isBatchModeEnabled() { + return batchMode; + } + + @Override + public void setBatchMode(boolean enabled) { + this.batchMode = enabled; + } + + @Override + public Pos2d getBatchModeButtonPos() { + return new Pos2d(44, 91); + } + + @Override + public boolean supportsSingleRecipeLocking() { + return false; + } + + @Override + public boolean isRecipeLockingEnabled() { + return mLockedToSingleRecipe; + } + + @Override + public void setRecipeLocking(boolean enabled) { + mLockedToSingleRecipe = enabled; + if (!enabled) { + setSingleRecipeCheck(null); + } + } + + @Override + public void setSingleRecipeCheck(SingleRecipeCheck recipeCheck) { + mSingleRecipeCheck = recipeCheck; + } + + @Override + public SingleRecipeCheck getSingleRecipeCheck() { + return mSingleRecipeCheck; + } + + @Override + public Pos2d getRecipeLockingButtonPos() { + return new Pos2d(62, 91); + } + + @Override + public boolean useModularUI() { + return true; + } + + @Override + public int getGUIWidth() { + return 198; + } + + @Override + public int getGUIHeight() { + return 192; + } + + @Override + public void bindPlayerInventoryUI(ModularWindow.Builder builder, UIBuildContext buildContext) { + builder.bindPlayerInventory(buildContext.getPlayer(), new Pos2d(7, 109), getGUITextureSet().getItemSlot()); + } + + @Override + public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) { + builder.widget( + new DrawableWidget().setDrawable(GT_UITextures.PICTURE_SCREEN_BLACK) + .setPos(4, 4) + .setSize(190, 85)); + final SlotWidget inventorySlot = new SlotWidget(inventoryHandler, 1); + builder.widget( + inventorySlot.setPos(173, 167) + .setBackground(GT_UITextures.SLOT_DARK_GRAY)); + + final DynamicPositionedColumn screenElements = new DynamicPositionedColumn(); + drawTexts(screenElements, inventorySlot); + builder.widget(screenElements); + + builder.widget(createPowerSwitchButton(builder)) + .widget(createVoidExcessButton(builder)) + .widget(createInputSeparationButton(builder)) + .widget(createBatchModeButton(builder)) + .widget(createLockToSingleRecipeButton(builder)); + } + + @Override + public void addGregTechLogo(ModularWindow.Builder builder) {} + + protected boolean shouldDisplayCheckRecipeResult() { + return true; + } + + public boolean shouldDisplayShutDownReason() { + return true; + } + + protected final NumberFormatMUI numberFormat = new NumberFormatMUI(); + + protected String generateCurrentRecipeInfoString() { + StringBuffer ret = new StringBuffer(EnumChatFormatting.WHITE + "Progress: "); + + numberFormat.setMinimumFractionDigits(2); + numberFormat.setMaximumFractionDigits(2); + numberFormat.format((double) mProgresstime / 20, ret); + ret.append("s / "); + numberFormat.format((double) mMaxProgresstime / 20, ret); + ret.append("s ("); + numberFormat.setMinimumFractionDigits(1); + numberFormat.setMaximumFractionDigits(1); + numberFormat.format((double) mProgresstime / mMaxProgresstime * 100, ret); + ret.append("%)\n"); + numberFormat.setMinimumFractionDigits(0); + numberFormat.setMaximumFractionDigits(2); + + IntConsumer appendRate = (amount) -> { + double processPerTick = (double) amount / mMaxProgresstime * 20; + if (processPerTick > 1) { + ret.append(" ("); + numberFormat.format(Math.round(processPerTick * 10) / 10.0, ret); + ret.append("/s)"); + } else { + ret.append(" ("); + numberFormat.format(Math.round(1 / processPerTick * 10) / 10.0, ret); + ret.append("s/ea)"); + } + }; + + int lines = 0; + int MAX_LINES = 5; + + if (mOutputItems != null) { + for (var item : mOutputItems) { + if (item == null) continue; + if (lines >= MAX_LINES) { + ret.append("..."); + return ret.toString(); + } + lines++; + ret.append(EnumChatFormatting.AQUA) + .append(item.getDisplayName()) + .append(EnumChatFormatting.WHITE) + .append(" x ") + .append(EnumChatFormatting.GOLD); + numberFormat.format(item.stackSize, ret); + ret.append(EnumChatFormatting.WHITE); + appendRate.accept(item.stackSize); + ret.append('\n'); + } + } + if (mOutputFluids != null) { + for (var fluid : mOutputFluids) { + if (fluid == null) continue; + if (lines >= MAX_LINES) { + ret.append("..."); + return ret.toString(); + } + lines++; + ret.append(EnumChatFormatting.AQUA) + .append(fluid.getLocalizedName()) + .append(EnumChatFormatting.WHITE) + .append(" x ") + .append(EnumChatFormatting.GOLD); + numberFormat.format(fluid.amount, ret); + ret.append("L") + .append(EnumChatFormatting.WHITE); + appendRate.accept(fluid.amount); + ret.append('\n'); + } + } + return ret.toString(); + } + + protected void drawTexts(DynamicPositionedColumn screenElements, SlotWidget inventorySlot) { + screenElements.setSynced(false) + .setSpace(0) + .setPos(10, 7); + + screenElements + .widget( + new TextWidget(GT_Utility.trans("132", "Pipe is loose.")).setDefaultColor(COLOR_TEXT_WHITE.get()) + .setEnabled(widget -> !mWrench)) + .widget(new FakeSyncWidget.BooleanSyncer(() -> mWrench, val -> mWrench = val)); + screenElements + .widget( + new TextWidget(GT_Utility.trans("133", "Screws are loose.")).setDefaultColor(COLOR_TEXT_WHITE.get()) + .setEnabled(widget -> !mScrewdriver)) + .widget(new FakeSyncWidget.BooleanSyncer(() -> mScrewdriver, val -> mScrewdriver = val)); + screenElements + .widget( + new TextWidget(GT_Utility.trans("134", "Something is stuck.")).setDefaultColor(COLOR_TEXT_WHITE.get()) + .setEnabled(widget -> !mSoftHammer)) + .widget(new FakeSyncWidget.BooleanSyncer(() -> mSoftHammer, val -> mSoftHammer = val)); + screenElements + .widget( + new TextWidget(GT_Utility.trans("135", "Platings are dented.")).setDefaultColor(COLOR_TEXT_WHITE.get()) + .setEnabled(widget -> !mHardHammer)) + .widget(new FakeSyncWidget.BooleanSyncer(() -> mHardHammer, val -> mHardHammer = val)); + screenElements + .widget( + new TextWidget(GT_Utility.trans("136", "Circuitry burned out.")).setDefaultColor(COLOR_TEXT_WHITE.get()) + .setEnabled(widget -> !mSolderingTool)) + .widget(new FakeSyncWidget.BooleanSyncer(() -> mSolderingTool, val -> mSolderingTool = val)); + screenElements + .widget( + new TextWidget(GT_Utility.trans("137", "That doesn't belong there.")) + .setDefaultColor(COLOR_TEXT_WHITE.get()) + .setEnabled(widget -> !mCrowbar)) + .widget(new FakeSyncWidget.BooleanSyncer(() -> mCrowbar, val -> mCrowbar = val)); + screenElements + .widget( + new TextWidget(GT_Utility.trans("138", "Incomplete Structure.")).setDefaultColor(COLOR_TEXT_WHITE.get()) + .setEnabled(widget -> !mMachine)) + .widget(new FakeSyncWidget.BooleanSyncer(() -> mMachine, val -> mMachine = val)); + screenElements.widget( + new TextWidget("Too Uncertain.").setDefaultColor(COLOR_TEXT_WHITE.get()) + .setEnabled(widget -> (getBaseMetaTileEntity().getErrorDisplayID() & 128) != 0)); + screenElements.widget( + new TextWidget("Invalid Parameters.").setDefaultColor(COLOR_TEXT_WHITE.get()) + .setEnabled(widget -> (getBaseMetaTileEntity().getErrorDisplayID() & 256) != 0)); + + screenElements.widget( + new TextWidget(GT_Utility.trans("139", "Hit with Soft Mallet")).setDefaultColor(COLOR_TEXT_WHITE.get()) + .setEnabled( + widget -> getBaseMetaTileEntity().getErrorDisplayID() == 0 && !getBaseMetaTileEntity().isActive())) + .widget( + new FakeSyncWidget.IntegerSyncer( + () -> getBaseMetaTileEntity().getErrorDisplayID(), + val -> getBaseMetaTileEntity().setErrorDisplayID(val))) + .widget( + new FakeSyncWidget.BooleanSyncer( + () -> getBaseMetaTileEntity().isActive(), + val -> getBaseMetaTileEntity().setActive(val))); + screenElements.widget( + new TextWidget(GT_Utility.trans("140", "to (re-)start the Machine")).setDefaultColor(COLOR_TEXT_WHITE.get()) + .setEnabled( + widget -> getBaseMetaTileEntity().getErrorDisplayID() == 0 && !getBaseMetaTileEntity().isActive())); + screenElements.widget( + new TextWidget(GT_Utility.trans("141", "if it doesn't start.")).setDefaultColor(COLOR_TEXT_WHITE.get()) + .setEnabled( + widget -> getBaseMetaTileEntity().getErrorDisplayID() == 0 && !getBaseMetaTileEntity().isActive())); + screenElements.widget( + new TextWidget(GT_Utility.trans("142", "Running perfectly.")).setDefaultColor(COLOR_TEXT_WHITE.get()) + .setEnabled( + widget -> getBaseMetaTileEntity().getErrorDisplayID() == 0 && getBaseMetaTileEntity().isActive())); + + screenElements.widget( + TextWidget.dynamicString( + () -> getBaseMetaTileEntity().getLastShutDownReason() + .getDisplayString()) + .setSynced(false) + .setTextAlignment(Alignment.CenterLeft) + .setEnabled( + widget -> shouldDisplayShutDownReason() && !getBaseMetaTileEntity().isActive() + && GT_Utility.isStringValid( + getBaseMetaTileEntity().getLastShutDownReason() + .getDisplayString()) + && getBaseMetaTileEntity().wasShutdown())) + .widget( + new ShutDownReasonSyncer( + () -> getBaseMetaTileEntity().getLastShutDownReason(), + reason -> getBaseMetaTileEntity().setShutDownReason(reason))) + .widget( + new FakeSyncWidget.BooleanSyncer( + () -> getBaseMetaTileEntity().wasShutdown(), + wasShutDown -> getBaseMetaTileEntity().setShutdownStatus(wasShutDown))); + + screenElements.widget( + TextWidget.dynamicString(() -> checkRecipeResult.getDisplayString()) + .setSynced(false) + .setTextAlignment(Alignment.CenterLeft) + .setEnabled( + widget -> shouldDisplayCheckRecipeResult() + && GT_Utility.isStringValid(checkRecipeResult.getDisplayString()) + && (isAllowedToWork() || getBaseMetaTileEntity().isActive() + || checkRecipeResult.persistsOnShutdown()))) + .widget(new CheckRecipeResultSyncer(() -> checkRecipeResult, (result) -> checkRecipeResult = result)); + + if (showRecipeTextInGUI()) { + // Display current recipe + screenElements.widget( + TextWidget.dynamicString(this::generateCurrentRecipeInfoString) + .setSynced(false) + .setTextAlignment(Alignment.CenterLeft) + .setEnabled( + widget -> (mOutputFluids != null && mOutputFluids.length > 0) + || (mOutputItems != null && mOutputItems.length > 0))) + .widget( + new FakeSyncWidget.ListSyncer<>( + () -> mOutputFluids != null ? Arrays.asList(mOutputFluids) : Collections.emptyList(), + val -> mOutputFluids = val.toArray(new FluidStack[0]), + NetworkUtils::writeFluidStack, + NetworkUtils::readFluidStack)) + .widget( + new FakeSyncWidget.ListSyncer<>( + () -> mOutputItems != null ? Arrays.asList(mOutputItems) : Collections.emptyList(), + val -> mOutputItems = val.toArray(new ItemStack[0]), + NetworkUtils::writeItemStack, + NetworkUtils::readItemStack)) + .widget(new FakeSyncWidget.IntegerSyncer(() -> mProgresstime, val -> mProgresstime = val)) + .widget(new FakeSyncWidget.IntegerSyncer(() -> mMaxProgresstime, val -> mMaxProgresstime = val)); + } + + screenElements.widget( + new TextWidget(GT_Utility.trans("144", "Missing Turbine Rotor")).setDefaultColor(COLOR_TEXT_WHITE.get()) + .setEnabled(widget -> { + if (getBaseMetaTileEntity().isAllowedToWork()) return false; + if (getBaseMetaTileEntity().getErrorDisplayID() == 0 + && this instanceof GT_MetaTileEntity_LargeTurbine) { + final ItemStack tItem = inventorySlot.getMcSlot() + .getStack(); + return tItem == null + || !(tItem.getItem() == GT_MetaGenerated_Tool_01.INSTANCE && tItem.getItemDamage() >= 170 + && tItem.getItemDamage() <= 177); + } + return false; + })); + } + + protected boolean showRecipeTextInGUI() { + return true; + } + + @TestOnly + protected void setEnergyHatches(ArrayList<GT_MetaTileEntity_Hatch_Energy> EnergyHatches) { + this.mEnergyHatches = EnergyHatches; + } + + @TestOnly + protected void setExoticEnergyHatches(List<GT_MetaTileEntity_Hatch> ExoticEnergyHatches) { + this.mExoticEnergyHatches = ExoticEnergyHatches; + } +} diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_SpecialFilter.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_SpecialFilter.java new file mode 100644 index 0000000000..1a71f17ec8 --- /dev/null +++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_SpecialFilter.java @@ -0,0 +1,134 @@ +package gregtech.api.metatileentity.implementations; + +import java.util.List; +import java.util.function.Function; + +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraftforge.common.util.ForgeDirection; + +import com.gtnewhorizons.modularui.api.drawable.Text; +import com.gtnewhorizons.modularui.api.screen.ModularWindow; +import com.gtnewhorizons.modularui.api.screen.UIBuildContext; +import com.gtnewhorizons.modularui.common.internal.wrapper.BaseSlot; +import com.gtnewhorizons.modularui.common.widget.DrawableWidget; +import com.gtnewhorizons.modularui.common.widget.SlotGroup; +import com.gtnewhorizons.modularui.common.widget.SlotWidget; + +import gregtech.api.gui.modularui.GT_UITextures; +import gregtech.api.interfaces.ITexture; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; + +public abstract class GT_MetaTileEntity_SpecialFilter extends GT_MetaTileEntity_FilterBase { + + private static final String ALLOW_NBT_TOOLTIP = "GT5U.machines.allow_nbt.tooltip"; + private boolean allowNbt = false; + + public GT_MetaTileEntity_SpecialFilter(int aID, String aName, String aNameRegional, int aTier, + String[] aDescription) { + // 9 buffer slot, 1 representation slot, 1 holo slot. last seems not needed... + super(aID, aName, aNameRegional, aTier, 11, aDescription); + } + + public GT_MetaTileEntity_SpecialFilter(String aName, int aTier, int aInvSlotCount, String aDescription, + ITexture[][][] aTextures) { + super(aName, aTier, aInvSlotCount, aDescription, aTextures); + } + + public GT_MetaTileEntity_SpecialFilter(String aName, int aTier, int aInvSlotCount, String[] aDescription, + ITexture[][][] aTextures) { + super(aName, aTier, aInvSlotCount, aDescription, aTextures); + } + + @Override + public void saveNBTData(NBTTagCompound aNBT) { + super.saveNBTData(aNBT); + aNBT.setBoolean("bNBTAllowed", this.allowNbt); + } + + @Override + public void loadNBTData(NBTTagCompound aNBT) { + super.loadNBTData(aNBT); + this.allowNbt = aNBT.getBoolean("bNBTAllowed"); + } + + @Override + public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side, + ItemStack aStack) { + return (super.allowPutStack(aBaseMetaTileEntity, aIndex, side, aStack)) + && ((this.allowNbt) || (!aStack.hasTagCompound())) + && (this.isStackAllowed(aStack) != this.invertFilter); + } + + protected abstract boolean isStackAllowed(ItemStack aStack); + + protected List<Text> getEmptySlotTooltip() { + return null; + } + + protected Function<List<String>, List<String>> getItemStackReplacementTooltip() { + return list -> list; + } + + @Override + public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) { + super.addUIWidgets(builder, buildContext); + addAllowNbtButton(builder); + builder.widget( + new DrawableWidget().setDrawable(GT_UITextures.PICTURE_ARROW_24_WHITE.apply(27, false)) + .setPos(6, 19) + .setSize(27, 24)) + .widget( + new DrawableWidget().setDrawable(GT_UITextures.PICTURE_ARROW_24_BLUE.apply(42, true)) + .setPos(53, 19) + .setSize(42, 24)) + .widget( + new DrawableWidget().setDrawable(GT_UITextures.PICTURE_ARROW_24_RED.apply(19, true)) + .setPos(152, 19) + .setSize(19, 24)) + .widget( + createFilterIconSlot(BaseSlot.phantom(inventoryHandler, 9)).disableShiftInsert() + .setPos(34, 22) + .setBackground(GT_UITextures.BUTTON_STANDARD)) + .widget( + SlotGroup.ofItemHandler(inventoryHandler, 3) + .endAtSlot(8) + .build() + .setPos(97, 4)); + } + + private void addAllowNbtButton(ModularWindow.Builder builder) { + builder.widget( + createToggleButton( + () -> allowNbt, + val -> allowNbt = val, + GT_UITextures.OVERLAY_BUTTON_NBT, + () -> mTooltipCache.getData(ALLOW_NBT_TOOLTIP))); + } + + protected abstract SlotWidget createFilterIconSlot(BaseSlot slot); + + protected abstract class FilterIconSlotWidget extends SlotWidget { + + public FilterIconSlotWidget(BaseSlot slot) { + super(slot); + } + + @Override + protected abstract void phantomClick(ClickData clickData, ItemStack cursorStack); + + @Override + public void buildTooltip(List<Text> tooltip) { + super.buildTooltip(tooltip); + List<Text> emptySlotTooltip = getEmptySlotTooltip(); + if (emptySlotTooltip != null) { + tooltip.addAll(emptySlotTooltip); + } + } + + @Override + public Function<List<String>, List<String>> getOverwriteItemStackTooltip() { + return getItemStackReplacementTooltip(); + } + } +} diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_TieredMachineBlock.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_TieredMachineBlock.java new file mode 100644 index 0000000000..dd21d6f412 --- /dev/null +++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_TieredMachineBlock.java @@ -0,0 +1,126 @@ +package gregtech.api.metatileentity.implementations; + +import static gregtech.api.enums.GT_Values.GT; +import static gregtech.api.metatileentity.BaseTileEntity.BATTERY_SLOT_TOOLTIP; +import static gregtech.api.metatileentity.BaseTileEntity.BATTERY_SLOT_TOOLTIP_ALT; +import static gregtech.api.metatileentity.BaseTileEntity.TOOLTIP_DELAY; + +import com.gtnewhorizons.modularui.common.widget.SlotWidget; + +import gregtech.api.enums.GT_Values; +import gregtech.api.gui.modularui.GT_UITextures; +import gregtech.api.interfaces.ITexture; +import gregtech.api.metatileentity.MetaTileEntity; +import gregtech.api.util.GT_Utility; + +public abstract class GT_MetaTileEntity_TieredMachineBlock extends MetaTileEntity { + + /** + * Value between [0 - 9] to describe the Tier of this Machine. PLZ [0-15] works - READ! GT_Values class. + */ + public final byte mTier; + + @Deprecated + public final String mDescription; + + /** + * A simple Description. + */ + public final String[] mDescriptionArray; + + /** + * Contains all Textures used by this Block. + */ + public final ITexture[][][] mTextures; + + public GT_MetaTileEntity_TieredMachineBlock(int aID, String aName, String aNameRegional, int aTier, + int aInvSlotCount, String aDescription, ITexture... aTextures) { + super(aID, aName, aNameRegional, aInvSlotCount); + mTier = (byte) Math.max(0, Math.min(aTier, 14)); + mDescriptionArray = aDescription == null ? new String[0] : new String[] { aDescription }; + mDescription = mDescriptionArray.length > 0 ? mDescriptionArray[0] : ""; + // must always be the last call! + if (GT.isClientSide()) mTextures = getTextureSet(aTextures); + else mTextures = null; + } + + public GT_MetaTileEntity_TieredMachineBlock(int aID, String aName, String aNameRegional, int aTier, + int aInvSlotCount, String[] aDescription, ITexture... aTextures) { + super(aID, aName, aNameRegional, aInvSlotCount); + mTier = (byte) Math.max(0, Math.min(aTier, 15)); + mDescriptionArray = aDescription == null ? new String[0] : aDescription; + mDescription = mDescriptionArray.length > 0 ? mDescriptionArray[0] : ""; + + // must always be the last call! + if (GT.isClientSide()) mTextures = getTextureSet(aTextures); + else mTextures = null; + } + + public GT_MetaTileEntity_TieredMachineBlock(String aName, int aTier, int aInvSlotCount, String aDescription, + ITexture[][][] aTextures) { + super(aName, aInvSlotCount); + mTier = (byte) aTier; + mDescriptionArray = aDescription == null ? new String[0] : new String[] { aDescription }; + mDescription = mDescriptionArray.length > 0 ? mDescriptionArray[0] : ""; + mTextures = aTextures; + } + + public GT_MetaTileEntity_TieredMachineBlock(String aName, int aTier, int aInvSlotCount, String[] aDescription, + ITexture[][][] aTextures) { + super(aName, aInvSlotCount); + mTier = (byte) aTier; + mDescriptionArray = aDescription == null ? new String[0] : aDescription; + mDescription = mDescriptionArray.length > 0 ? mDescriptionArray[0] : ""; + mTextures = aTextures; + } + + @Override + public byte getTileEntityBaseType() { + return (byte) (Math.min(3, mTier <= 0 ? 0 : 1 + ((mTier - 1) / 4))); + } + + @Override + public long getInputTier() { + return mTier; + } + + @Override + public long getOutputTier() { + return mTier; + } + + @Override + public String[] getDescription() { + return mDescriptionArray; + } + + /** + * Used Client Side to get a Texture Set for this Block. Called after setting the Tier and the Description so that + * those two are accessible. + * + * @param aTextures is the optional Array you can give to the Constructor. + */ + public abstract ITexture[][][] getTextureSet(ITexture[] aTextures); + + protected SlotWidget createChargerSlot(int x, int y) { + final String batterySlotTooltipKey; + final Object[] batterySlotTooltipArgs; + final String pTier1 = GT_Utility.getColoredTierNameFromTier(mTier); + if (mTier == GT_Values.VN.length - 1) { + batterySlotTooltipKey = BATTERY_SLOT_TOOLTIP_ALT; + batterySlotTooltipArgs = new String[] { pTier1 }; + } else { + batterySlotTooltipKey = BATTERY_SLOT_TOOLTIP; + batterySlotTooltipArgs = new String[] { pTier1, GT_Utility.getColoredTierNameFromTier((byte) (mTier + 1)) }; + } + return createChargerSlot(x, y, batterySlotTooltipKey, batterySlotTooltipArgs); + } + + protected SlotWidget createChargerSlot(int x, int y, String tooltipKey, Object[] tooltipArgs) { + return (SlotWidget) new SlotWidget(inventoryHandler, rechargerSlotStartIndex()).disableShiftInsert() + .setGTTooltip(() -> mTooltipCache.getData(tooltipKey, tooltipArgs)) + .setTooltipShowUpDelay(TOOLTIP_DELAY) + .setBackground(getGUITextureSet().getItemSlot(), GT_UITextures.OVERLAY_SLOT_CHARGER) + .setPos(x, y); + } +} diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_TooltipMultiBlockBase.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_TooltipMultiBlockBase.java new file mode 100644 index 0000000000..c12c7c1442 --- /dev/null +++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_TooltipMultiBlockBase.java @@ -0,0 +1,57 @@ +package gregtech.api.metatileentity.implementations; + +import java.util.concurrent.atomic.AtomicReferenceArray; + +import org.lwjgl.input.Keyboard; + +import gregtech.api.GregTech_API; +import gregtech.api.interfaces.ISecondaryDescribable; +import gregtech.api.util.GT_Multiblock_Tooltip_Builder; + +/** + * A multiblock with tooltip {@link GT_Multiblock_Tooltip_Builder} + */ +public abstract class GT_MetaTileEntity_TooltipMultiBlockBase extends GT_MetaTileEntity_MultiBlockBase + implements ISecondaryDescribable { + + private static final AtomicReferenceArray<GT_Multiblock_Tooltip_Builder> tooltips = new AtomicReferenceArray<>( + GregTech_API.METATILEENTITIES.length); + + public GT_MetaTileEntity_TooltipMultiBlockBase(int aID, String aName, String aNameRegional) { + super(aID, aName, aNameRegional); + } + + public GT_MetaTileEntity_TooltipMultiBlockBase(String aName) { + super(aName); + } + + protected GT_Multiblock_Tooltip_Builder getTooltip() { + int tId = getBaseMetaTileEntity().getMetaTileID(); + GT_Multiblock_Tooltip_Builder tooltip = tooltips.get(tId); + if (tooltip == null) { + tooltip = createTooltip(); + tooltips.set(tId, tooltip); + } + return tooltip; + } + + protected abstract GT_Multiblock_Tooltip_Builder createTooltip(); + + @Override + public String[] getDescription() { + return getCurrentDescription(); + } + + @Override + public boolean isDisplaySecondaryDescription() { + return Keyboard.isKeyDown(Keyboard.KEY_LSHIFT); + } + + public String[] getPrimaryDescription() { + return getTooltip().getInformation(); + } + + public String[] getSecondaryDescription() { + return getTooltip().getStructureInformation(); + } +} diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Transformer.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Transformer.java new file mode 100644 index 0000000000..a8c26957f8 --- /dev/null +++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Transformer.java @@ -0,0 +1,325 @@ +package gregtech.api.metatileentity.implementations; + +import static gregtech.api.enums.GT_Values.V; +import static mcp.mobius.waila.api.SpecialChars.BLUE; +import static mcp.mobius.waila.api.SpecialChars.GOLD; +import static mcp.mobius.waila.api.SpecialChars.GREEN; +import static mcp.mobius.waila.api.SpecialChars.RED; +import static mcp.mobius.waila.api.SpecialChars.RESET; + +import java.util.List; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.world.World; +import net.minecraftforge.common.util.ForgeDirection; + +import cofh.api.energy.IEnergyProvider; +import cofh.api.energy.IEnergyStorage; +import crazypants.enderio.machine.capbank.TileCapBank; +import crazypants.enderio.machine.capbank.network.ICapBankNetwork; +import crazypants.enderio.power.IPowerContainer; +import gregtech.GT_Mod; +import gregtech.api.GregTech_API; +import gregtech.api.enums.Textures; +import gregtech.api.interfaces.ITexture; +import gregtech.api.interfaces.metatileentity.IMetaTileEntity; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.util.GT_Utility; +import mcp.mobius.waila.api.IWailaConfigHandler; +import mcp.mobius.waila.api.IWailaDataAccessor; + +/** + * NEVER INCLUDE THIS FILE IN YOUR MOD!!! + * <p/> + * This is the main construct for my Basic Machines such as the Automatic Extractor Extend this class to make a simple + * Machine + */ +public class GT_MetaTileEntity_Transformer extends GT_MetaTileEntity_TieredMachineBlock { + + public GT_MetaTileEntity_Transformer(int aID, String aName, String aNameRegional, int aTier, String aDescription) { + super(aID, aName, aNameRegional, aTier, 0, aDescription); + } + + public GT_MetaTileEntity_Transformer(String aName, int aTier, String aDescription, ITexture[][][] aTextures) { + super(aName, aTier, 0, aDescription, aTextures); + } + + public GT_MetaTileEntity_Transformer(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures) { + super(aName, aTier, 0, aDescription, aTextures); + } + + @Override + public ITexture[][][] getTextureSet(ITexture[] aTextures) { + ITexture[][][] rTextures = new ITexture[12][17][]; + for (byte i = -1; i < 16; i++) { + rTextures[0][i + 1] = new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][i + 1], + Textures.BlockIcons.OVERLAYS_ENERGY_OUT[mTier] }; + rTextures[1][i + 1] = new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][i + 1], + Textures.BlockIcons.OVERLAYS_ENERGY_OUT[mTier] }; + rTextures[2][i + 1] = new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][i + 1], + Textures.BlockIcons.OVERLAYS_ENERGY_OUT[mTier] }; + rTextures[3][i + 1] = new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][i + 1], + Textures.BlockIcons.OVERLAYS_ENERGY_IN_MULTI[mTier] }; + rTextures[4][i + 1] = new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][i + 1], + Textures.BlockIcons.OVERLAYS_ENERGY_IN_MULTI[mTier] }; + rTextures[5][i + 1] = new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][i + 1], + Textures.BlockIcons.OVERLAYS_ENERGY_IN_MULTI[mTier] }; + rTextures[6][i + 1] = new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][i + 1], + Textures.BlockIcons.OVERLAYS_ENERGY_IN[mTier] }; + rTextures[7][i + 1] = new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][i + 1], + Textures.BlockIcons.OVERLAYS_ENERGY_IN[mTier] }; + rTextures[8][i + 1] = new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][i + 1], + Textures.BlockIcons.OVERLAYS_ENERGY_IN[mTier] }; + rTextures[9][i + 1] = new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][i + 1], + Textures.BlockIcons.OVERLAYS_ENERGY_OUT_MULTI[mTier] }; + rTextures[10][i + 1] = new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][i + 1], + Textures.BlockIcons.OVERLAYS_ENERGY_OUT_MULTI[mTier] }; + rTextures[11][i + 1] = new ITexture[] { Textures.BlockIcons.MACHINE_CASINGS[mTier][i + 1], + Textures.BlockIcons.OVERLAYS_ENERGY_OUT_MULTI[mTier] }; + } + return rTextures; + } + + @Override + public ITexture[] getTexture(IGregTechTileEntity baseMetaTileEntity, ForgeDirection side, + ForgeDirection facingDirection, int colorIndex, boolean active, boolean redstoneLevel) { + return mTextures[Math.min(2, side.ordinal()) + (side == facingDirection ? 3 : 0) + + (baseMetaTileEntity.isAllowedToWork() ? 0 : 6)][colorIndex + 1]; + } + + @Override + public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) { + return new GT_MetaTileEntity_Transformer(mName, mTier, mDescriptionArray, mTextures); + } + + @Override + public boolean isAccessAllowed(EntityPlayer aPlayer) { + return true; + } + + @Override + public boolean isSimpleMachine() { + return true; + } + + @Override + public boolean isFacingValid(ForgeDirection facing) { + return true; + } + + @Override + public boolean isEnetInput() { + return true; + } + + @Override + public boolean isEnetOutput() { + return true; + } + + @Override + public boolean isInputFacing(ForgeDirection side) { + ForgeDirection blockFrontFacing = getBaseMetaTileEntity().getFrontFacing(); + + if (getBaseMetaTileEntity().isAllowedToWork()) { + return side == blockFrontFacing; + } else { + return side != blockFrontFacing; + } + } + + @Override + public boolean isOutputFacing(ForgeDirection side) { + return !isInputFacing(side); + } + + @Override + public boolean isTeleporterCompatible() { + return false; + } + + @Override + public long getMinimumStoredEU() { + return V[mTier + 1]; + } + + @Override + public long maxEUStore() { + return Math.max(512L, 1L << (mTier + 2)) + V[mTier + 1] * 4L; + } + + @Override + public long maxEUInput() { + return V[getBaseMetaTileEntity().isAllowedToWork() ? mTier + 1 : mTier]; + } + + @Override + public long maxEUOutput() { + return V[getBaseMetaTileEntity().isAllowedToWork() ? mTier : mTier + 1]; + } + + @Override + public long maxAmperesOut() { + return getBaseMetaTileEntity().isAllowedToWork() ? 4 : 1; + } + + @Override + public long maxAmperesIn() { + return getBaseMetaTileEntity().isAllowedToWork() ? 1 : 4; + } + + @Override + public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) { + if (aBaseMetaTileEntity.isServerSide() && GregTech_API.mInputRF) { + aBaseMetaTileEntity.setActive(aBaseMetaTileEntity.isAllowedToWork()); + for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) { + if (aBaseMetaTileEntity.getStoredEU() >= aBaseMetaTileEntity.getEUCapacity()) break; + if (!aBaseMetaTileEntity.inputEnergyFrom(side)) continue; + final TileEntity tTileEntity = aBaseMetaTileEntity.getTileEntityAtSide(side); + if (tTileEntity instanceof IEnergyProvider energyProvider + && energyProvider.extractEnergy(side.getOpposite(), 1, true) == 1) { + long tEU = ((IEnergyProvider) tTileEntity).extractEnergy( + side.getOpposite(), + GT_Utility.safeInt(maxEUInput() * 100L / GregTech_API.mRFtoEU), + false); + tEU = tEU * GregTech_API.mRFtoEU / 100; + aBaseMetaTileEntity.injectEnergyUnits(ForgeDirection.UNKNOWN, Math.min(tEU, maxEUInput()), 1); + } else if (tTileEntity instanceof IEnergyStorage energyStorage + && energyStorage.extractEnergy(1, true) == 1) { + long tEU = ((IEnergyStorage) tTileEntity) + .extractEnergy(GT_Utility.safeInt(maxEUInput() * 100L / GregTech_API.mRFtoEU), false); + tEU = tEU * GregTech_API.mRFtoEU / 100; + aBaseMetaTileEntity.injectEnergyUnits(ForgeDirection.UNKNOWN, Math.min(tEU, maxEUInput()), 1); + } else if (GregTech_API.meIOLoaded && tTileEntity instanceof IPowerContainer powerContainer + && powerContainer.getEnergyStored() > 0) { + final int storedRF = powerContainer.getEnergyStored(); + final int extractRF = GT_Utility.safeInt(maxEUInput() * 100L / GregTech_API.mRFtoEU); + long tEU = 0; + if (tTileEntity instanceof TileCapBank capBank) { + ICapBankNetwork network = capBank.getNetwork(); + if (network != null && network.getEnergyStoredL() > 0) { + tEU = Math.min( + (Math.min( + Math.min(network.getEnergyStoredL(), storedRF - extractRF), + network.getMaxOutput())) * (long) GregTech_API.mRFtoEU / 100L, + maxEUInput()); + network.addEnergy(GT_Utility.safeInt(-(tEU * 100 / GregTech_API.mRFtoEU))); + } + } else { + if (storedRF > extractRF) { + powerContainer.setEnergyStored(storedRF - extractRF); + tEU = maxEUInput(); + } else { + powerContainer.setEnergyStored(0); + tEU = storedRF * (long) GregTech_API.mRFtoEU / 100L; + } + } + aBaseMetaTileEntity + .injectEnergyUnits(ForgeDirection.UNKNOWN, Math.min(tEU, maxEUInput()), 1); + } + } + } + } + + @Override + public void saveNBTData(NBTTagCompound aNBT) { + // + } + + @Override + public void loadNBTData(NBTTagCompound aNBT) { + // + } + + @Override + public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side, + ItemStack aStack) { + return false; + } + + @Override + public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side, + ItemStack aStack) { + return false; + } + + @Override + public boolean hasAlternativeModeText() { + return true; + } + + @Override + public String getAlternativeModeText() { + return (getBaseMetaTileEntity().isAllowedToWork() ? GT_Utility.trans("145", "Step Down, In: ") + : GT_Utility.trans("146", "Step Up, In: ")) + maxEUInput() + + GT_Utility.trans("148", "V ") + + maxAmperesIn() + + GT_Utility.trans("147", "A, Out: ") + + maxEUOutput() + + GT_Utility.trans("148", "V ") + + maxAmperesOut() + + GT_Utility.trans("149", "A"); + } + + @Override + public boolean shouldJoinIc2Enet() { + return true; + } + + @Override + public void getWailaBody(ItemStack itemStack, List<String> currenttip, IWailaDataAccessor accessor, + IWailaConfigHandler config) { + final ForgeDirection facing = getBaseMetaTileEntity().getFrontFacing(); + final NBTTagCompound tag = accessor.getNBTData(); + final ForgeDirection side = accessor.getSide(); + final boolean allowedToWork = tag.getBoolean("isAllowedToWork"); + + final byte inputTier = GT_Utility.getTier(tag.getLong("maxEUInput")); + final byte outputTier = GT_Utility.getTier(tag.getLong("maxEUOutput")); + + currenttip.add( + String.format( + "%s %s(%dA) -> %s(%dA)", + (allowedToWork ? (GREEN + "Step Down") : (RED + "Step Up")) + RESET, + GT_Mod.gregtechproxy.mWailaTransformerVoltageTier ? GT_Utility.getColoredTierNameFromTier(inputTier) + : tag.getLong("maxEUInput"), + tag.getLong("maxAmperesIn"), + GT_Mod.gregtechproxy.mWailaTransformerVoltageTier ? GT_Utility.getColoredTierNameFromTier(outputTier) + : tag.getLong("maxEUOutput"), + tag.getLong("maxAmperesOut"))); + + if ((side == facing && allowedToWork) || (side != facing && !allowedToWork)) { + currenttip.add( + String.format( + GOLD + "Input:" + RESET + " %s(%dA)", + GT_Mod.gregtechproxy.mWailaTransformerVoltageTier ? GT_Utility.getColoredTierNameFromTier(inputTier) + : tag.getLong("maxEUInput"), + tag.getLong("maxAmperesIn"))); + } else { + currenttip.add( + String.format( + BLUE + "Output:" + RESET + " %s(%dA)", + GT_Mod.gregtechproxy.mWailaTransformerVoltageTier + ? GT_Utility.getColoredTierNameFromTier(outputTier) + : tag.getLong("maxEUOutput"), + tag.getLong("maxAmperesOut"))); + } + + super.getWailaBody(itemStack, currenttip, accessor, config); + } + + @Override + public void getWailaNBTData(EntityPlayerMP player, TileEntity tile, NBTTagCompound tag, World world, int x, int y, + int z) { + super.getWailaNBTData(player, tile, tag, world, x, y, z); + tag.setBoolean("isAllowedToWork", getBaseMetaTileEntity().isAllowedToWork()); + tag.setLong("maxEUInput", maxEUInput()); + tag.setLong("maxAmperesIn", maxAmperesIn()); + tag.setLong("maxEUOutput", maxEUOutput()); + tag.setLong("maxAmperesOut", maxAmperesOut()); + } +} diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Wireless_Dynamo.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Wireless_Dynamo.java new file mode 100644 index 0000000000..a04f5cd986 --- /dev/null +++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Wireless_Dynamo.java @@ -0,0 +1,146 @@ +package gregtech.api.metatileentity.implementations; + +import static gregtech.api.enums.GT_Values.AuthorColen; +import static gregtech.api.enums.GT_Values.V; +import static gregtech.common.misc.WirelessNetworkManager.addEUToGlobalEnergyMap; +import static gregtech.common.misc.WirelessNetworkManager.strongCheckOrAddUser; + +import java.util.UUID; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.util.EnumChatFormatting; +import net.minecraftforge.common.util.ForgeDirection; + +import gregtech.api.enums.Textures; +import gregtech.api.interfaces.ITexture; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.interfaces.tileentity.IWirelessEnergyHatchInformation; +import gregtech.api.metatileentity.MetaTileEntity; + +public class GT_MetaTileEntity_Wireless_Dynamo extends GT_MetaTileEntity_Hatch_Dynamo + implements IWirelessEnergyHatchInformation { + + private UUID owner_uuid; + + public GT_MetaTileEntity_Wireless_Dynamo(String aName, byte aTier, String[] aDescription, + ITexture[][][] aTextures) { + super(aName, aTier, aDescription, aTextures); + } + + public GT_MetaTileEntity_Wireless_Dynamo(int aID, String aName, String aNameRegional, int aTier) { + super(aID, aName, aNameRegional, aTier, new String[] { "" }); + } + + @Override + public ITexture[] getTexturesActive(ITexture aBaseTexture) { + return new ITexture[] { aBaseTexture, Textures.BlockIcons.OVERLAYS_ENERGY_IN_MULTI_WIRELESS_ON[mTier] }; + } + + @Override + public ITexture[] getTexturesInactive(ITexture aBaseTexture) { + return new ITexture[] { aBaseTexture, Textures.BlockIcons.OVERLAYS_ENERGY_IN_MULTI_WIRELESS_ON[mTier] }; + } + + @Override + public boolean isSimpleMachine() { + return true; + } + + @Override + public boolean isFacingValid(ForgeDirection facing) { + return true; + } + + @Override + public boolean isAccessAllowed(EntityPlayer aPlayer) { + return true; + } + + @Override + public boolean isEnetOutput() { + return false; + } + + @Override + public boolean isInputFacing(ForgeDirection side) { + return side == getBaseMetaTileEntity().getFrontFacing(); + } + + @Override + public boolean isValidSlot(int aIndex) { + return false; + } + + @Override + public long getMinimumStoredEU() { + return 2 * V[mTier]; + } + + @Override + public long maxEUOutput() { + return V[mTier]; + } + + @Override + public long maxEUStore() { + return totalStorage(V[mTier]); + } + + @Override + public String[] getDescription() { + return new String[] { EnumChatFormatting.GRAY + "Stores energy globally in a network, up to 2^(2^31) EU.", + EnumChatFormatting.GRAY + "Does not connect to wires. This block accepts EU into the network.", + AuthorColen }; + } + + @Override + public long maxAmperesOut() { + return 2; + } + + @Override + public ConnectionType getConnectionType() { + return ConnectionType.WIRELESS; + } + + @Override + public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) { + return new GT_MetaTileEntity_Wireless_Dynamo(mName, mTier, new String[] { "" }, mTextures); + } + + @Override + public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side, + ItemStack aStack) { + return false; + } + + @Override + public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side, + ItemStack aStack) { + return false; + } + + @Override + public void onPreTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) { + super.onPreTick(aBaseMetaTileEntity, aTick); + + if (aBaseMetaTileEntity.isServerSide()) { + + // On first tick find the player name and attempt to add them to the map. + if (aTick == 1) { + + // UUID and username of the owner. + owner_uuid = aBaseMetaTileEntity.getOwnerUuid(); + + strongCheckOrAddUser(owner_uuid); + } + + // Every ticks_between_energy_addition ticks change the energy content of the machine. + if (aTick % ticks_between_energy_addition == 0L) { + addEUToGlobalEnergyMap(owner_uuid, getEUVar()); + setEUVar(0L); + } + } + } +} diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Wireless_Hatch.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Wireless_Hatch.java new file mode 100644 index 0000000000..bf624eadd7 --- /dev/null +++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_Wireless_Hatch.java @@ -0,0 +1,168 @@ +package gregtech.api.metatileentity.implementations; + +import static gregtech.api.enums.GT_Values.AuthorColen; +import static gregtech.api.enums.GT_Values.V; +import static gregtech.common.misc.WirelessNetworkManager.addEUToGlobalEnergyMap; +import static java.lang.Long.min; + +import java.math.BigInteger; +import java.util.UUID; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.util.EnumChatFormatting; +import net.minecraftforge.common.util.ForgeDirection; + +import gregtech.api.enums.Textures; +import gregtech.api.interfaces.ITexture; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.interfaces.tileentity.IWirelessEnergyHatchInformation; +import gregtech.api.metatileentity.MetaTileEntity; +import gregtech.common.misc.spaceprojects.SpaceProjectManager; + +public class GT_MetaTileEntity_Wireless_Hatch extends GT_MetaTileEntity_Hatch_Energy + implements IWirelessEnergyHatchInformation { + + private final BigInteger eu_transferred_per_operation = BigInteger + .valueOf(2 * V[mTier] * ticks_between_energy_addition); + private final long eu_transferred_per_operation_long = eu_transferred_per_operation.longValue(); + + private UUID owner_uuid; + + public GT_MetaTileEntity_Wireless_Hatch(String aName, byte aTier, String[] aDescription, ITexture[][][] aTextures) { + super(aName, aTier, 0, aDescription, aTextures); + } + + public GT_MetaTileEntity_Wireless_Hatch(int aID, String aName, String aNameRegional, int aTier) { + super(aID, aName, aNameRegional, aTier, new String[] { "" }); + } + + @Override + public String[] getDescription() { + return new String[] { EnumChatFormatting.GRAY + "Stores energy globally in a network, up to 2^(2^31) EU.", + EnumChatFormatting.GRAY + "Does not connect to wires. This block withdraws EU from the network.", + AuthorColen }; + } + + @Override + public ITexture[] getTexturesActive(ITexture aBaseTexture) { + return new ITexture[] { aBaseTexture, Textures.BlockIcons.OVERLAYS_ENERGY_IN_MULTI_WIRELESS_ON[mTier] }; + } + + @Override + public ITexture[] getTexturesInactive(ITexture aBaseTexture) { + return new ITexture[] { aBaseTexture, Textures.BlockIcons.OVERLAYS_ENERGY_IN_MULTI_WIRELESS_ON[mTier] }; + } + + @Override + public boolean isSimpleMachine() { + return true; + } + + @Override + public boolean isFacingValid(ForgeDirection facing) { + return true; + } + + @Override + public boolean isAccessAllowed(EntityPlayer aPlayer) { + return true; + } + + @Override + public boolean isEnetInput() { + return false; + } + + @Override + public boolean isInputFacing(ForgeDirection side) { + return side == getBaseMetaTileEntity().getFrontFacing(); + } + + @Override + public boolean isValidSlot(int aIndex) { + return false; + } + + @Override + public long getMinimumStoredEU() { + return 2 * V[mTier]; + } + + @Override + public long maxEUInput() { + return V[mTier]; + } + + @Override + public long maxEUStore() { + return totalStorage(V[mTier]); + } + + @Override + public long maxAmperesIn() { + return 2; + } + + @Override + public ConnectionType getConnectionType() { + return ConnectionType.WIRELESS; + } + + @Override + public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) { + return new GT_MetaTileEntity_Wireless_Hatch(mName, mTier, new String[] { "" }, mTextures); + } + + @Override + public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side, + ItemStack aStack) { + return false; + } + + @Override + public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side, + ItemStack aStack) { + return false; + } + + @Override + public void onFirstTick(IGregTechTileEntity aBaseMetaTileEntity) { + super.onFirstTick(aBaseMetaTileEntity); + + if (!aBaseMetaTileEntity.isServerSide()) return; + + // UUID of the owner. + owner_uuid = aBaseMetaTileEntity.getOwnerUuid(); + + SpaceProjectManager.checkOrCreateTeam(owner_uuid);; + + tryFetchingEnergy(); + } + + @Override + public void onPreTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) { + + super.onPreTick(aBaseMetaTileEntity, aTick); + + if (aBaseMetaTileEntity.isServerSide()) { + // This is set up in a way to be as optimised as possible. If a user has a relatively plentiful energy + // network + // it should make no difference to them. Minimising the number of operations on BigInteger is essential. + + // Every ticks_between_energy_addition add eu_transferred_per_operation to internal EU storage from network. + if (aTick % ticks_between_energy_addition == 0L) { + tryFetchingEnergy(); + } + } + } + + private void tryFetchingEnergy() { + long currentEU = getBaseMetaTileEntity().getStoredEU(); + long maxEU = maxEUStore(); + long euToTransfer = min(maxEU - currentEU, eu_transferred_per_operation_long); + if (euToTransfer <= 0) return; // nothing to transfer + if (!addEUToGlobalEnergyMap(owner_uuid, -euToTransfer)) return; + setEUVar(currentEU + euToTransfer); + } +} diff --git a/src/main/java/gregtech/api/multitileentity/MultiTileEntityBlock.java b/src/main/java/gregtech/api/multitileentity/MultiTileEntityBlock.java new file mode 100644 index 0000000000..6ee760553e --- /dev/null +++ b/src/main/java/gregtech/api/multitileentity/MultiTileEntityBlock.java @@ -0,0 +1,654 @@ +package gregtech.api.multitileentity; + +import static gregtech.api.enums.GT_Values.OFFX; +import static gregtech.api.enums.GT_Values.OFFY; +import static gregtech.api.enums.GT_Values.OFFZ; +import static gregtech.api.util.GT_Util.LAST_BROKEN_TILEENTITY; +import static gregtech.api.util.GT_Util.getTileEntity; +import static gregtech.api.util.GT_Util.setTileEntity; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import net.minecraft.block.Block; +import net.minecraft.block.ITileEntityProvider; +import net.minecraft.block.material.Material; +import net.minecraft.client.renderer.texture.IIconRegister; +import net.minecraft.creativetab.CreativeTabs; +import net.minecraft.enchantment.EnchantmentHelper; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EnumCreatureType; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.Blocks; +import net.minecraft.item.Item; +import net.minecraft.item.ItemBlock; +import net.minecraft.item.ItemStack; +import net.minecraft.stats.StatList; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.AxisAlignedBB; +import net.minecraft.util.IIcon; +import net.minecraft.util.MovingObjectPosition; +import net.minecraft.util.StatCollector; +import net.minecraft.world.Explosion; +import net.minecraft.world.IBlockAccess; +import net.minecraft.world.World; +import net.minecraftforge.common.util.ForgeDirection; +import net.minecraftforge.event.ForgeEventFactory; + +import com.cricketcraft.chisel.api.IFacade; + +import cpw.mods.fml.common.Optional; +import cpw.mods.fml.common.registry.GameRegistry; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import gregtech.api.GregTech_API; +import gregtech.api.enums.ItemList; +import gregtech.api.enums.Textures; +import gregtech.api.interfaces.IDebugableBlock; +import gregtech.api.interfaces.tileentity.IDebugableTileEntity; +import gregtech.api.metatileentity.BaseTileEntity; +import gregtech.api.metatileentity.CoverableTileEntity; +import gregtech.api.multitileentity.interfaces.IMultiTileEntity; +import gregtech.api.multitileentity.interfaces.IMultiTileEntity.IMTE_BreakBlock; +import gregtech.api.multitileentity.interfaces.IMultiTileEntity.IMTE_GetBlockHardness; +import gregtech.api.multitileentity.interfaces.IMultiTileEntity.IMTE_GetComparatorInputOverride; +import gregtech.api.multitileentity.interfaces.IMultiTileEntity.IMTE_GetWeakChanges; +import gregtech.api.multitileentity.interfaces.IMultiTileEntity.IMTE_HasMultiBlockMachineRelevantData; +import gregtech.api.multitileentity.interfaces.IMultiTileEntity.IMTE_IsProvidingStrongPower; +import gregtech.api.multitileentity.interfaces.IMultiTileEntity.IMTE_IsProvidingWeakPower; +import gregtech.api.multitileentity.interfaces.IMultiTileEntity.IMTE_OnNeighborBlockChange; +import gregtech.api.multitileentity.interfaces.IMultiTileEntity.IMTE_ShouldCheckWeakPower; +import gregtech.api.objects.XSTR; +import gregtech.api.util.GT_Log; +import gregtech.api.util.GT_Util; +import gregtech.common.covers.CoverInfo; +import gregtech.common.render.GT_MultiTile_Renderer; + +/* + * MultiTileEntityBlock ported from GT6 + */ +@Optional.Interface(iface = "com.cricketcraft.chisel.api.IFacade", modid = "ChiselAPI") +public class MultiTileEntityBlock extends Block implements IDebugableBlock, ITileEntityProvider, IFacade { + + protected static final Map<String, MultiTileEntityBlock> MULTI_BLOCK_MAP = new HashMap<>(); + private static boolean LOCK = false; + + protected final String mNameInternal, mTool; + protected final int mHarvestLevelOffset, mHarvestLevelMinimum, mHarvestLevelMaximum; + protected final boolean mOpaque, mNormalCube; + + public static String getName(String aMaterialName, SoundType aSoundType, String aTool, int aHarvestLevelOffset, + int aHarvestLevelMinimum, int aHarvestLevelMaximum, boolean aOpaque, boolean aNormalCube) { + return "gt.block.multiblock." + aMaterialName + + "." + + aSoundType.soundName + + "." + + aTool + + "." + + aHarvestLevelOffset + + "." + + aHarvestLevelMinimum + + "." + + aHarvestLevelMaximum + + "." + + aOpaque + + "." + + aNormalCube; + } + + /** + * @param aMaterialName the Name of the vanilla Material Field. In case this is not a vanilla Material, + * insert the Name you want to give your own Material instead. + * @param aMaterial the Material used to determine the Block. + * @param aSoundType the Sound Type of the Block. + * @param aTool the Tool used to harvest this Block. + * @param aHarvestLevelOffset obvious + * @param aHarvestLevelMinimum obvious + * @param aHarvestLevelMaximum obvious + * @param aOpaque if this Block is Opaque. + * @param aNormalCube if this Block is a normal Cube (for Redstone Stuff). + */ + public static MultiTileEntityBlock getOrCreate(String aModID, String aMaterialName, Material aMaterial, + SoundType aSoundType, String aTool, int aHarvestLevelOffset, int aHarvestLevelMinimum, int aHarvestLevelMaximum, + boolean aOpaque, boolean aNormalCube) { + final MultiTileEntityBlock rBlock = MULTI_BLOCK_MAP.get( + aModID + ":" + + getName( + aMaterialName, + aSoundType, + aTool = aTool.toLowerCase(), + aHarvestLevelOffset, + aHarvestLevelMinimum, + aHarvestLevelMaximum, + aOpaque, + aNormalCube)); + return rBlock == null + ? new MultiTileEntityBlock( + aModID, + aMaterialName, + aMaterial, + aSoundType, + aTool, + aHarvestLevelOffset, + aHarvestLevelMinimum, + aHarvestLevelMaximum, + aOpaque, + aNormalCube) + : rBlock; + } + + protected MultiTileEntityBlock(String aModID, String aMaterialName, Material aMaterial, SoundType aSoundType, + String aTool, int aHarvestLevelOffset, int aHarvestLevelMinimum, int aHarvestLevelMaximum, boolean aOpaque, + boolean aNormalCube) { + super(aMaterial); + if (GregTech_API.sPreloadFinished) + throw new IllegalStateException("Blocks can only be initialized within preInit!"); + + mNameInternal = getName( + aMaterialName, + aSoundType, + aTool, + aHarvestLevelOffset, + aHarvestLevelMinimum, + aHarvestLevelMaximum, + aOpaque, + aNormalCube); + GameRegistry.registerBlock(this, ItemBlock.class, mNameInternal); + + MULTI_BLOCK_MAP.put(aModID + ":" + mNameInternal, this); + + setStepSound(aSoundType); + mOpaque = aOpaque; + mNormalCube = aNormalCube; + + mTool = aTool.toLowerCase(); + mHarvestLevelOffset = aHarvestLevelOffset; + mHarvestLevelMinimum = Math.max(0, aHarvestLevelMinimum); + mHarvestLevelMaximum = Math.max(aHarvestLevelMinimum, aHarvestLevelMaximum); + + opaque = isOpaqueCube(); + lightOpacity = isOpaqueCube() ? 255 : 0; + } + + @Override + public final void breakBlock(World aWorld, int aX, int aY, int aZ, Block aBlock, int aMetaData) { + final TileEntity aTileEntity = getTileEntity(aWorld, aX, aY, aZ, true); + if (aTileEntity != null) LAST_BROKEN_TILEENTITY.set(aTileEntity); + if (aTileEntity == null || !aTileEntity.shouldRefresh(this, aBlock, aMetaData, aMetaData, aWorld, aX, aY, aZ)) + return; + if (aTileEntity instanceof IMTE_BreakBlock && ((IMTE_BreakBlock) aTileEntity).breakBlock()) return; + if (aTileEntity instanceof IMTE_HasMultiBlockMachineRelevantData + && ((IMTE_HasMultiBlockMachineRelevantData) aTileEntity).hasMultiBlockMachineRelevantData()) + GregTech_API.causeMachineUpdate(aWorld, aX, aY, aZ); + + aWorld.removeTileEntity(aX, aY, aZ); + } + + @Override + public ArrayList<String> getDebugInfo(EntityPlayer aPlayer, int aX, int aY, int aZ, int aLogLevel) { + final TileEntity aTileEntity = aPlayer.worldObj.getTileEntity(aX, aY, aZ); + if (aTileEntity instanceof IDebugableTileEntity mte) { + return mte.getDebugInfo(aPlayer, aLogLevel); + } + return new ArrayList<>(); + } + + @Override + public final boolean func_149730_j /* isFullBlock */() { + return mOpaque; + } + + @Override + public final boolean isNormalCube() { + return mNormalCube; + } + + @Override + public final boolean renderAsNormalBlock() { + return mOpaque || mNormalCube; + } + + @Override + public int getRenderType() { + return GT_MultiTile_Renderer.INSTANCE == null ? super.getRenderType() + : GT_MultiTile_Renderer.INSTANCE.getRenderId(); + } + + @Override + public final float getBlockHardness(World aWorld, int aX, int aY, int aZ) { + final TileEntity aTileEntity = aWorld.getTileEntity(aX, aY, aZ); + return aTileEntity instanceof IMTE_GetBlockHardness ? ((IMTE_GetBlockHardness) aTileEntity).getBlockHardness() + : 1.0F; + } + + @SideOnly(Side.CLIENT) + @Override + public IIcon getIcon(IBlockAccess aIBlockAccess, int aX, int aY, int aZ, int ordinalSide) { + return Textures.BlockIcons.MACHINE_LV_SIDE.getIcon(); + } + + @SideOnly(Side.CLIENT) + @Override + public IIcon getIcon(int ordinalSide, int aMeta) { + return Textures.BlockIcons.MACHINE_LV_SIDE.getIcon(); + } + + @Override + @SuppressWarnings("unchecked") + public final void addCollisionBoxesToList(World aWorld, int aX, int aY, int aZ, AxisAlignedBB aAABB, + List<AxisAlignedBB> aList, Entity aEntity) { + final TileEntity aTileEntity = aWorld.getTileEntity(aX, aY, aZ); + if (aTileEntity instanceof IMultiTileEntity) + ((IMultiTileEntity) aTileEntity).addCollisionBoxesToList(aAABB, aList, aEntity); + else super.addCollisionBoxesToList(aWorld, aX, aY, aZ, aAABB, aList, aEntity); + } + + @Override + public final AxisAlignedBB getCollisionBoundingBoxFromPool(World aWorld, int aX, int aY, int aZ) { + final TileEntity aTileEntity = aWorld.getTileEntity(aX, aY, aZ); + return aTileEntity instanceof IMultiTileEntity mte ? mte.getCollisionBoundingBoxFromPool() + : aTileEntity == null ? null : super.getCollisionBoundingBoxFromPool(aWorld, aX, aY, aZ); + } + + @Override + public final AxisAlignedBB getSelectedBoundingBoxFromPool(World aWorld, int aX, int aY, int aZ) { + final TileEntity aTileEntity = aWorld.getTileEntity(aX, aY, aZ); + return aTileEntity instanceof IMultiTileEntity mte ? mte.getSelectedBoundingBoxFromPool() + : super.getSelectedBoundingBoxFromPool(aWorld, aX, aY, aZ); + } + + @Override + public void setBlockBoundsBasedOnState(IBlockAccess blockAccess, int aX, int aY, int aZ) { + final TileEntity aTileEntity = blockAccess.getTileEntity(aX, aY, aZ); + if (aTileEntity instanceof IMultiTileEntity mte) { + mte.setBlockBoundsBasedOnState(this); + return; + } + super.setBlockBoundsBasedOnState(blockAccess, aX, aY, aZ); + } + + @Override + public final boolean isOpaqueCube() { + return mOpaque; + } + + @Override + public final void onNeighborChange(IBlockAccess aWorld, int aX, int aY, int aZ, int aTileX, int aTileY, + int aTileZ) { + final TileEntity aTileEntity = aWorld.getTileEntity(aX, aY, aZ); + if (!LOCK) { + LOCK = true; + if (aTileEntity instanceof BaseTileEntity) + ((BaseTileEntity) aTileEntity).onAdjacentBlockChange(aTileX, aTileY, aTileZ); + LOCK = false; + } + } + + @Override + public void onNeighborBlockChange(World aWorld, int aX, int aY, int aZ, Block aBlock) { + final TileEntity aTileEntity = aWorld.getTileEntity(aX, aY, aZ); + if (!LOCK) { + LOCK = true; + if (aTileEntity instanceof BaseTileEntity bte) bte.onAdjacentBlockChange(aX, aY, aZ); + LOCK = false; + } + if (aTileEntity instanceof IMTE_OnNeighborBlockChange change) change.onNeighborBlockChange(aWorld, aBlock); + if (aTileEntity == null) aWorld.setBlockToAir(aX, aY, aZ); + } + + @Override + public final void onBlockAdded(World aWorld, int aX, int aY, int aZ) { + final TileEntity aTileEntity = aWorld.getTileEntity(aX, aY, aZ); + if (aTileEntity instanceof IMultiTileEntity mte) mte.onBlockAdded(); + } + + @Override + public float getPlayerRelativeBlockHardness(EntityPlayer aPlayer, World aWorld, int aX, int aY, int aZ) { + final TileEntity aTileEntity = aWorld.getTileEntity(aX, aY, aZ); + return aTileEntity instanceof IMultiTileEntity mte && mte.privateAccess() + && !((IMultiTileEntity) aTileEntity).playerOwnsThis(aPlayer, true) ? -1.0F + : super.getPlayerRelativeBlockHardness(aPlayer, aWorld, aX, aY, aZ); + } + + @Override + public final void onBlockClicked(World aWorld, int aX, int aY, int aZ, EntityPlayer aPlayer) { + final TileEntity aTileEntity = aWorld.getTileEntity(aX, aY, aZ); + if (aTileEntity instanceof IMultiTileEntity mte) mte.onLeftClick(aPlayer); + else super.onBlockClicked(aWorld, aX, aY, aZ, aPlayer); + } + + @Override + public boolean onBlockActivated(World aWorld, int aX, int aY, int aZ, EntityPlayer aPlayer, int ordinalSide, + float aHitX, float aHitY, float aHitZ) { + final TileEntity aTileEntity = aWorld.getTileEntity(aX, aY, aZ); + if (aPlayer != null && ItemList.TC_Thaumometer.isStackEqual(aPlayer.getHeldItem(), true, true)) return false; + return aTileEntity instanceof IMultiTileEntity mte + && mte.onBlockActivated(aPlayer, ForgeDirection.getOrientation(ordinalSide), aHitX, aHitY, aHitZ); + } + + @Override + public final int isProvidingWeakPower(IBlockAccess aWorld, int aX, int aY, int aZ, int ordinalSide) { + final TileEntity aTileEntity = aWorld.getTileEntity(aX, aY, aZ); + return aTileEntity instanceof IMTE_IsProvidingWeakPower power + ? power.isProvidingWeakPower(ForgeDirection.getOrientation(ordinalSide)) + : super.isProvidingWeakPower(aWorld, aX, aY, aZ, ordinalSide); + } + + @Override + public final int isProvidingStrongPower(IBlockAccess aWorld, int aX, int aY, int aZ, int ordinalSide) { + final TileEntity aTileEntity = aWorld.getTileEntity(aX, aY, aZ); + return aTileEntity instanceof IMTE_IsProvidingStrongPower power + ? power.isProvidingStrongPower(ForgeDirection.getOrientation(ordinalSide)) + : super.isProvidingStrongPower(aWorld, aX, aY, aZ, ordinalSide); + } + + @Override + public final boolean shouldCheckWeakPower(IBlockAccess aWorld, int aX, int aY, int aZ, int ordinalSide) { + final TileEntity aTileEntity = aWorld.getTileEntity(aX, aY, aZ); + return aTileEntity instanceof IMTE_ShouldCheckWeakPower power + ? power.shouldCheckWeakPower(ForgeDirection.getOrientation(ordinalSide)) + : isNormalCube(aWorld, aX, aY, aZ); + } + + @Override + public final boolean getWeakChanges(IBlockAccess aWorld, int aX, int aY, int aZ) { + final TileEntity aTileEntity = aWorld.getTileEntity(aX, aY, aZ); + return aTileEntity instanceof IMTE_GetWeakChanges changes ? changes.getWeakChanges() + : super.getWeakChanges(aWorld, aX, aY, aZ); + } + + @Override + public final void harvestBlock(World aWorld, EntityPlayer aPlayer, int aX, int aY, int aZ, int aMeta) { + if (aPlayer == null) aPlayer = harvesters.get(); + aPlayer.addStat(StatList.mineBlockStatArray[getIdFromBlock(this)], 1); + aPlayer.addExhaustion(0.025F); + final boolean aSilkTouch = EnchantmentHelper.getSilkTouchModifier(aPlayer); + final int aFortune = EnchantmentHelper.getFortuneModifier(aPlayer); + float aChance = 1.0F; + final TileEntity aTileEntity = getTileEntity(aWorld, aX, aY, aZ, true); + + if (!(aTileEntity instanceof IMultiTileEntity mte)) { + return; + } + + final ArrayList<ItemStack> tList = mte.getDrops(aFortune, aSilkTouch); + aChance = ForgeEventFactory + .fireBlockHarvesting(tList, aWorld, this, aX, aY, aZ, aMeta, aFortune, aChance, aSilkTouch, aPlayer); + for (final ItemStack tStack : tList) + if (XSTR.XSTR_INSTANCE.nextFloat() <= aChance) dropBlockAsItem(aWorld, aX, aY, aZ, tStack); + + } + + @Override + public final boolean shouldSideBeRendered(IBlockAccess aWorld, int aX, int aY, int aZ, int ordinalSide) { + final TileEntity aTileEntity = aWorld + .getTileEntity(aX - OFFX[ordinalSide], aY - OFFY[ordinalSide], aZ - OFFZ[ordinalSide]); + return aTileEntity instanceof IMultiTileEntity mte + ? mte.shouldSideBeRendered(ForgeDirection.getOrientation(ordinalSide)) + : super.shouldSideBeRendered(aWorld, aX, aY, aZ, ordinalSide); + } + + @Override + public Block getFacade(IBlockAccess aWorld, int aX, int aY, int aZ, int ordinalSide) { + final TileEntity tTileEntity = aWorld.getTileEntity(aX, aY, aZ); + if (tTileEntity instanceof CoverableTileEntity tile) { + final ForgeDirection side = ForgeDirection.getOrientation(ordinalSide); + if (ordinalSide != -1) { + final Block facadeBlock = tile.getCoverInfoAtSide(side) + .getFacadeBlock(); + if (facadeBlock != null) return facadeBlock; + } else { + // we do not allow more than one type of facade per block, so no need to check every side + // see comment in gregtech.common.covers.GT_Cover_FacadeBase.isCoverPlaceable + for (final ForgeDirection tSide : ForgeDirection.VALID_DIRECTIONS) { + final Block facadeBlock = tile.getCoverInfoAtSide(tSide) + .getFacadeBlock(); + if (facadeBlock != null) { + return facadeBlock; + } + } + } + } + return Blocks.air; + } + + @Override + public int getFacadeMetadata(IBlockAccess aWorld, int aX, int aY, int aZ, int ordinalSide) { + final TileEntity tTileEntity = aWorld.getTileEntity(aX, aY, aZ); + if (tTileEntity instanceof CoverableTileEntity tile) { + final ForgeDirection side = ForgeDirection.getOrientation(ordinalSide); + if (ordinalSide != -1) { + final CoverInfo coverInfo = tile.getCoverInfoAtSide(side); + final Block facadeBlock = coverInfo.getFacadeBlock(); + if (facadeBlock != null) return coverInfo.getFacadeMeta(); + } else { + // we do not allow more than one type of facade per block, so no need to check every side + // see comment in gregtech.common.covers.GT_Cover_FacadeBase.isCoverPlaceable + for (final ForgeDirection tSide : ForgeDirection.VALID_DIRECTIONS) { + final CoverInfo coverInfo = tile.getCoverInfoAtSide(tSide); + final Block facadeBlock = coverInfo.getFacadeBlock(); + if (facadeBlock != null) { + return coverInfo.getFacadeMeta(); + } + } + } + } + return 0; + } + + @Override + protected boolean canSilkHarvest() { + return false; + } + + @Override + public final boolean canProvidePower() { + return !mNormalCube; + } + + @Override + public final String getLocalizedName() { + return StatCollector.translateToLocal(mNameInternal + ".name"); + } + + @Override + public final String getUnlocalizedName() { + return mNameInternal; + } + + @Override + public final boolean onBlockEventReceived(World aWorld, int aX, int aY, int aZ, int aID, int aData) { + final TileEntity aTileEntity = aWorld.getTileEntity(aX, aY, aZ); + return aTileEntity == null || aTileEntity.receiveClientEvent(aID, aData); + } + + @Override + public final void getSubBlocks(Item aItem, CreativeTabs aCreativeTab, List<ItemStack> aList) { + /**/ + } + + @Override + public boolean hasComparatorInputOverride() { + return true; + } + + @Override + public final int getComparatorInputOverride(World aWorld, int aX, int aY, int aZ, int ordinalSide) { + final TileEntity aTileEntity = aWorld.getTileEntity(aX, aY, aZ); + if (aTileEntity instanceof IMTE_GetComparatorInputOverride override) { + return override.getComparatorInputOverride(ForgeDirection.getOrientation(ordinalSide)); + } + + if (aTileEntity instanceof IMTE_IsProvidingWeakPower power) { + return power.isProvidingWeakPower( + ForgeDirection.getOrientation(ordinalSide) + .getOpposite()); + } + + return super.getComparatorInputOverride(aWorld, aX, aY, aZ, ordinalSide); + } + + @Override + public final void registerBlockIcons(IIconRegister aIconRegister) { + /**/ + } + + @Override + public final boolean isNormalCube(IBlockAccess aWorld, int aX, int aY, int aZ) { + return mNormalCube; + } + + @Override + public final boolean isSideSolid(IBlockAccess aWorld, int aX, int aY, int aZ, ForgeDirection side) { + final TileEntity aTileEntity = aWorld.getTileEntity(aX, aY, aZ); + return aTileEntity instanceof IMultiTileEntity mte ? mte.isSideSolid(side) : mOpaque; + } + + @Override + public boolean removedByPlayer(World aWorld, EntityPlayer aPlayer, int aX, int aY, int aZ, boolean aWillHarvest) { + final TileEntity aTileEntity = GT_Util.getTileEntity(aWorld, aX, aY, aZ, true); + if (aTileEntity != null) LAST_BROKEN_TILEENTITY.set(aTileEntity); + return super.removedByPlayer(aWorld, aPlayer, aX, aY, aZ, aWillHarvest); + } + + @Override + public int getFlammability(IBlockAccess aWorld, int aX, int aY, int aZ, ForgeDirection face) { + return 0; + } + + @Override + public int getFireSpreadSpeed(IBlockAccess aWorld, int aX, int aY, int aZ, ForgeDirection face) { + return GregTech_API.sMachineFlammable && (aWorld.getBlockMetadata(aX, aY, aZ) == 0) ? 100 : 0; + } + + @Override + public boolean hasTileEntity(int aMeta) { + return true; + } + + @Override + public final ArrayList<ItemStack> getDrops(World aWorld, int aX, int aY, int aZ, int aUnusableMetaData, + int aFortune) { + final TileEntity aTileEntity = getTileEntity(aWorld, aX, aY, aZ, true); + if (aTileEntity instanceof IMultiTileEntity mte) return mte.getDrops(aFortune, false); + return new ArrayList<>(); + } + + @Override + public boolean canCreatureSpawn(EnumCreatureType type, IBlockAccess aWorld, int aX, int aY, int aZ) { + return false; + } + + @Override + public final float getExplosionResistance(Entity aExploder, World aWorld, int aX, int aY, int aZ, + double aExplosionX, double aExplosionY, double aExplosionZ) { + final TileEntity aTileEntity = aWorld.getTileEntity(aX, aY, aZ); + return aTileEntity instanceof IMultiTileEntity mte + ? mte.getExplosionResistance(aExploder, aExplosionX, aExplosionY, aExplosionZ) + : 1.0F; + } + + @Override + public final void onBlockExploded(World aWorld, int aX, int aY, int aZ, Explosion aExplosion) { + if (aWorld.isRemote) return; + final TileEntity aTileEntity = getTileEntity(aWorld, aX, aY, aZ, true); + if (aTileEntity != null) LAST_BROKEN_TILEENTITY.set(aTileEntity); + if (aTileEntity instanceof IMultiTileEntity mte) { + GT_Log.exp.printf( + "Explosion at : %d | %d | %d DIMID: %s due to near explosion!%n", + aX, + aY, + aZ, + aWorld.provider.dimensionId); + mte.onExploded(aExplosion); + } else aWorld.setBlockToAir(aX, aY, aZ); + } + + @Override + public final boolean canConnectRedstone(IBlockAccess aWorld, int aX, int aY, int aZ, int ordinalSide) { + return true; + } + + @Override + public final boolean recolourBlock(World aWorld, int aX, int aY, int aZ, ForgeDirection side, int aColor) { + final TileEntity aTileEntity = aWorld.getTileEntity(aX, aY, aZ); + return aTileEntity instanceof IMultiTileEntity mte && mte.recolourBlock(side, (byte) aColor); + } + + @Override + public final String getHarvestTool(int aMeta) { + return mTool; + } + + @Override + public final int getHarvestLevel(int aMeta) { + return Math.max(mHarvestLevelMinimum, Math.min(mHarvestLevelMaximum, mHarvestLevelOffset + aMeta)); + } + + @Override + public final boolean isToolEffective(String aType, int aMeta) { + return getHarvestTool(aMeta).equals(aType); + } + + @Override + public final ItemStack getPickBlock(MovingObjectPosition aTarget, World aWorld, int aX, int aY, int aZ, + EntityPlayer aPlayer) { + final TileEntity aTileEntity = aWorld.getTileEntity(aX, aY, aZ); + return aTileEntity instanceof IMultiTileEntity mte ? mte.getPickBlock(aTarget) : null; + } + + @Override + public final ItemStack getPickBlock(MovingObjectPosition aTarget, World aWorld, int aX, int aY, int aZ) { + final TileEntity aTileEntity = aWorld.getTileEntity(aX, aY, aZ); + return aTileEntity instanceof IMultiTileEntity mte ? mte.getPickBlock(aTarget) : null; + } + + @Nullable + public final IMultiTileEntity receiveMultiTileEntityData(@Nonnull IBlockAccess aWorld, int aX, int aY, int aZ, + int registryId, int aID) { + if (!(aWorld instanceof World)) return null; + TileEntity aTileEntity = aWorld.getTileEntity(aX, aY, aZ); + + if (!(aTileEntity instanceof IMultiTileEntity mte) || mte.getMultiTileEntityRegistryID() != registryId + || mte.getMultiTileEntityID() != aID) { + final MultiTileEntityRegistry tRegistry = MultiTileEntityRegistry.getRegistry(registryId); + if (tRegistry == null) return null; + + aTileEntity = tRegistry.getNewTileEntity((World) aWorld, aX, aY, aZ, aID); + if (!(aTileEntity instanceof IMultiTileEntity)) return null; + + setTileEntity((World) aWorld, aX, aY, aZ, aTileEntity, false); + } + return (IMultiTileEntity) aTileEntity; + } + + public void receiveCoverData(IMultiTileEntity mte, int aCover0, int aCover1, int aCover2, int aCover3, int aCover4, + int aCover5) { + boolean updated; + updated = mte.setCoverIDAtSideNoUpdate(ForgeDirection.DOWN, aCover0); + updated |= mte.setCoverIDAtSideNoUpdate(ForgeDirection.UP, aCover1); + updated |= mte.setCoverIDAtSideNoUpdate(ForgeDirection.NORTH, aCover2); + updated |= mte.setCoverIDAtSideNoUpdate(ForgeDirection.SOUTH, aCover3); + updated |= mte.setCoverIDAtSideNoUpdate(ForgeDirection.WEST, aCover4); + updated |= mte.setCoverIDAtSideNoUpdate(ForgeDirection.EAST, aCover5); + + if (updated) { + mte.issueBlockUpdate(); + } + } + + @Override + public final TileEntity createTileEntity(World aWorld, int aMeta) { + return null; + } + + @Override + public TileEntity createNewTileEntity(World world, int i) { + return null; + } +} diff --git a/src/main/java/gregtech/api/multitileentity/MultiTileEntityBlockInternal.java b/src/main/java/gregtech/api/multitileentity/MultiTileEntityBlockInternal.java new file mode 100644 index 0000000000..17e217ae7e --- /dev/null +++ b/src/main/java/gregtech/api/multitileentity/MultiTileEntityBlockInternal.java @@ -0,0 +1,118 @@ +package gregtech.api.multitileentity; + +import static gregtech.GT_Mod.GT_FML_LOGGER; +import static gregtech.api.util.GT_Util.setTileEntity; + +import net.minecraft.block.Block; +import net.minecraft.block.material.Material; +import net.minecraft.client.renderer.texture.IIconRegister; +import net.minecraft.init.Blocks; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.StatCollector; +import net.minecraft.world.World; +import net.minecraftforge.common.util.ForgeDirection; + +import gregtech.api.GregTech_API; +import gregtech.api.multitileentity.interfaces.IMultiTileEntity; +import gregtech.api.multitileentity.interfaces.IMultiTileEntity.IMTE_HasMultiBlockMachineRelevantData; +import gregtech.common.render.GT_MultiTile_Renderer; + +public class MultiTileEntityBlockInternal extends Block { + + public MultiTileEntityRegistry mMultiTileEntityRegistry; + + public MultiTileEntityBlockInternal() { + super(Material.anvil); + } + + @Override + public void registerBlockIcons(IIconRegister aIconRegister) { + /* Do Nothing */ + } + + @Override + public int getRenderType() { + return GT_MultiTile_Renderer.INSTANCE == null ? super.getRenderType() + : GT_MultiTile_Renderer.INSTANCE.getRenderId(); + } + + @Override + public final String getUnlocalizedName() { + return mMultiTileEntityRegistry.mNameInternal; + } + + @Override + public final String getLocalizedName() { + return StatCollector.translateToLocal(mMultiTileEntityRegistry.mNameInternal + ".name"); + } + + public boolean placeBlock(World aWorld, int aX, int aY, int aZ, ForgeDirection side, short aMetaData, + NBTTagCompound aNBT, boolean aCauseBlockUpdates, boolean aForcePlacement) { + final MultiTileEntityContainer aMTEContainer = mMultiTileEntityRegistry + .getNewTileEntityContainer(aWorld, aX, aY, aZ, aMetaData, aNBT); + if (aMTEContainer == null) return false; + + final Block tReplacedBlock = aWorld.getBlock(aX, aY, aZ); + + // This is some complicated bullshit Greg had to do to make his MTEs work right. + // Set Block with reverse MetaData first. + aWorld.setBlock(aX, aY, aZ, aMTEContainer.mBlock, 15 - aMTEContainer.mBlockMetaData, 2); + // Make sure the Block has been set, yes I know setBlock has a true/false return value, but guess what, it is + // not reliable in 0.0001% of cases! -Greg + if (aWorld.getBlock(aX, aY, aZ) != aMTEContainer.mBlock) { + aWorld.setBlock(aX, aY, aZ, Blocks.air, 0, 0); + return false; + } + // TileEntity should not refresh yet! -Greg + ((IMultiTileEntity) aMTEContainer.mTileEntity).setShouldRefresh(false); + // Fake-Set the TileEntity first, bypassing a lot of checks. -Greg + setTileEntity(aWorld, aX, aY, aZ, aMTEContainer.mTileEntity, false); + // Now set the Block with the REAL MetaData. -Greg + setTileEntity(aWorld, aX, aY, aZ, aMTEContainer.mBlock, aMTEContainer.mBlockMetaData, 0, false); + // When the TileEntity is set now it SHOULD refresh! -Greg + ((IMultiTileEntity) aMTEContainer.mTileEntity).setShouldRefresh(true); + // But make sure again that the Block we have set was actually set properly, because 0.0001%! -Greg + if (aWorld.getBlock(aX, aY, aZ) != aMTEContainer.mBlock) { + aWorld.setBlock(aX, aY, aZ, Blocks.air, 0, 0); + return false; + } + // And finally properly set the TileEntity for real! -Greg + setTileEntity(aWorld, aX, aY, aZ, aMTEContainer.mTileEntity, aCauseBlockUpdates); + // Yep, all this just to set one Block and its TileEntity properly... -Greg + + try { + if (aMTEContainer.mTileEntity instanceof IMTE_HasMultiBlockMachineRelevantData) { + if (((IMTE_HasMultiBlockMachineRelevantData) aMTEContainer.mTileEntity) + .hasMultiBlockMachineRelevantData()) GregTech_API.causeMachineUpdate(aWorld, aX, aY, aZ); + } + } catch (Throwable e) { + GT_FML_LOGGER.error("causeMachineUpdate", e); + } + + try { + if (!aWorld.isRemote && aCauseBlockUpdates) { + aWorld.notifyBlockChange(aX, aY, aZ, tReplacedBlock); + aWorld.func_147453_f(aX, aY, aZ, aMTEContainer.mBlock); + } + } catch (Throwable e) { + GT_FML_LOGGER.error("aCauseBlockUpdates", e); + } + + try { + ((IMultiTileEntity) aMTEContainer.mTileEntity).onTileEntityPlaced(); + } catch (Throwable e) { + GT_FML_LOGGER.error("onTileEntityPlaced", e); + } + + try { + aWorld.func_147451_t /* updateAllLightTypes */(aX, aY, aZ); + } catch (Throwable e) { + GT_FML_LOGGER.error("updateAllLightTypes", e); + } + return true; + } + + public MultiTileEntityRegistry getRegistry() { + return mMultiTileEntityRegistry; + } +} diff --git a/src/main/java/gregtech/api/multitileentity/MultiTileEntityClassContainer.java b/src/main/java/gregtech/api/multitileentity/MultiTileEntityClassContainer.java new file mode 100644 index 0000000000..4ce4c3c886 --- /dev/null +++ b/src/main/java/gregtech/api/multitileentity/MultiTileEntityClassContainer.java @@ -0,0 +1,205 @@ +package gregtech.api.multitileentity; + +import static gregtech.api.enums.GT_Values.NBT; + +import java.lang.ref.WeakReference; + +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.Tuple; + +import gregtech.api.enums.Materials; +import gregtech.api.multitileentity.base.MultiTileEntity; +import gregtech.api.multitileentity.multiblock.casing.FunctionalCasing; +import gregtech.api.multitileentity.multiblock.casing.UpgradeCasing; +import gregtech.api.util.GT_Util; +import gregtech.common.tileentities.casings.upgrade.Inventory; +import gregtech.common.tileentities.casings.upgrade.Tank; + +public class MultiTileEntityClassContainer { + + private final WeakReference<MultiTileEntityRegistry> mRegistry; + private String mLocalized; + private String mCategoryName; + + public final short mID; + public Class<? extends MultiTileEntity> mClass; + public MultiTileEntityBlock mBlock; + public MultiTileEntity mCanonicalTileEntity; + public NBTTagCompound mParameters; + + // These have defaults + public byte mBlockMetaData = 1; + public byte mStackSize = 64; + public boolean mHidden = false; + + public MultiTileEntityClassContainer(MultiTileEntityRegistry aRegistry, int aID, + Class<? extends MultiTileEntity> aClass) { + /* Start the Builder */ + mRegistry = new WeakReference<>(aRegistry); + mID = (short) aID; + mClass = aClass; + mParameters = new NBTTagCompound(); + } + + public boolean register() { + /* End and register the Builder with the registry */ + final MultiTileEntityRegistry registry = mRegistry.get(); + + if (mParameters.hasKey(NBT.MATERIAL) && !mParameters.hasKey(NBT.COLOR)) mParameters.setInteger( + NBT.COLOR, + GT_Util.getRGBInt( + Materials.get(mParameters.getString(NBT.MATERIAL)) + .getRGBA())); + + try { + mCanonicalTileEntity = mClass.newInstance(); + } catch (Throwable e) { + throw new IllegalArgumentException(e); + } + mCanonicalTileEntity.initFromNBT(mParameters, mID, (short) -1); + + return registry != null && registry.add(this.mLocalized, this.mCategoryName, this) != null; + } + + public MultiTileEntityClassContainer name(String aName) { + mLocalized = aName; + return this; + } + + public MultiTileEntityClassContainer category(String aCategoryName) { + mCategoryName = aCategoryName; + return this; + } + + public MultiTileEntityClassContainer meta(int aMeta) { + mBlockMetaData = (byte) aMeta; + return this; + } + + public MultiTileEntityClassContainer stackSize(int aStackSize) { + mStackSize = (byte) aStackSize; + return this; + } + + public MultiTileEntityClassContainer hide() { + mHidden = true; + return this; + } + + public MultiTileEntityClassContainer setBlock(MultiTileEntityBlock aBlock) { + mBlock = aBlock; + return this; + } + + /* These methods are builder methods for commonly used NBT tags */ + + // Need a base texture for the MTE machine, and then a separate texture set for the machine/active overlays + + public MultiTileEntityClassContainer material(Materials material) { + // Sets the material, and the color from the material, if not already set + mParameters.setString(NBT.MATERIAL, material.toString()); + if (!mParameters.hasKey(NBT.COLOR)) mParameters.setInteger(NBT.COLOR, GT_Util.getRGBInt(material.getRGBA())); + return this; + } + + public MultiTileEntityClassContainer color(int rbg) { + mParameters.setInteger(NBT.COLOR, rbg); + return this; + } + + public MultiTileEntityClassContainer color(short[] rgba) { + mParameters.setInteger(NBT.COLOR, GT_Util.getRGBInt(rgba)); + return this; + } + + public MultiTileEntityClassContainer textureFolder(String texture) { + mParameters.setString(NBT.TEXTURE_FOLDER, texture); + return this; + } + + public MultiTileEntityClassContainer inputInventorySize(int aSize) { + mParameters.setInteger(NBT.INV_INPUT_SIZE, aSize); + return this; + } + + public MultiTileEntityClassContainer outputInventorySize(int aSize) { + mParameters.setInteger(NBT.INV_OUTPUT_SIZE, aSize); + return this; + } + + public MultiTileEntityClassContainer tankCapacity(Long aCapacity) { + mParameters.setLong(NBT.TANK_CAPACITY, aCapacity); + return this; + } + + public MultiTileEntityClassContainer tier(int aTier) { + verifyDescendentOfMultiple(true, UpgradeCasing.class, FunctionalCasing.class); + mParameters.setInteger(NBT.TIER, aTier); + return this; + } + + public MultiTileEntityClassContainer upgradeInventorySize(int aSize) { + verifyDescendentOf(Inventory.class); + + mParameters.setInteger(NBT.UPGRADE_INVENTORY_SIZE, aSize); + return this; + } + + public MultiTileEntityClassContainer upgradeTankCount(int count) { + verifyDescendentOf(Tank.class); + + mParameters.setInteger(NBT.UPGRADE_TANK_COUNT, count); + return this; + } + + public MultiTileEntityClassContainer upgradeTankCapacity(Long aCapacity) { + mParameters.setLong(NBT.UPGRADE_TANK_CAPACITY, aCapacity); + return this; + } + + public MultiTileEntityClassContainer upgradeAmperage(long amperage) { + mParameters.setLong(NBT.UPGRADE_AMPERAGE, amperage); + return this; + } + + @SuppressWarnings("unused") + public MultiTileEntityClassContainer setNBT(String key, Object val) { + return setNBT(new Tuple(key, val)); + } + + public MultiTileEntityClassContainer setNBT(Tuple... aTags) { + /* + * Merge in arbitrary NBT tuples of (key, value). Useful for anything for which a custom method has not yet been + * exposed + */ + mParameters = GT_Util.fuseNBT(mParameters, GT_Util.makeNBT(aTags)); + return this; + } + + private void verifyDescendentOf(Class<?> cls) { + // Check if cls is extended by mClass + if (!cls.isAssignableFrom(mClass)) { + throw new IllegalArgumentException( + "Expected a descendent of " + cls.getName() + " got " + mClass.getName() + " instead."); + } + } + + private void verifyDescendentOfMultiple(boolean onlyOne, Class<?>... classes) { + boolean atLeastOne = false; + String classNames = ""; + for (Class<?> cls : classes) { + classNames += cls.getName() + " "; + if (!onlyOne) { + verifyDescendentOf(cls); + atLeastOne = true; + } else if (cls.isAssignableFrom(mClass)) { + atLeastOne = true; + } + } + + if (!atLeastOne) { + throw new IllegalArgumentException( + "Expected a descendent of any of these " + classNames + " got " + mClass.getName() + " instead."); + } + } +} diff --git a/src/main/java/gregtech/api/multitileentity/MultiTileEntityContainer.java b/src/main/java/gregtech/api/multitileentity/MultiTileEntityContainer.java new file mode 100644 index 0000000000..3510140c12 --- /dev/null +++ b/src/main/java/gregtech/api/multitileentity/MultiTileEntityContainer.java @@ -0,0 +1,30 @@ +package gregtech.api.multitileentity; + +import static gregtech.api.util.GT_Util.setTileEntity; + +import net.minecraft.tileentity.TileEntity; +import net.minecraft.world.World; + +import gregtech.api.multitileentity.interfaces.IMultiTileEntity; + +public class MultiTileEntityContainer { + + public final TileEntity mTileEntity; + public final MultiTileEntityBlock mBlock; + public final byte mBlockMetaData; + + public MultiTileEntityContainer(TileEntity aTileEntity, MultiTileEntityBlock aBlock, byte aBlockMetaData) { + mBlockMetaData = aBlockMetaData; + mTileEntity = aTileEntity; + mBlock = aBlock; + } + + public void setMultiTile(World aWorld, int aX, int aY, int aZ) { + // This is some complicated Bullshit Greg had to do to make his MTEs work right. + ((IMultiTileEntity) mTileEntity).setShouldRefresh(false); + setTileEntity(aWorld, aX, aY, aZ, mTileEntity, false); + setTileEntity(aWorld, aX, aY, aZ, mBlock, mBlockMetaData, 0, false); + ((IMultiTileEntity) mTileEntity).setShouldRefresh(true); + setTileEntity(aWorld, aX, aY, aZ, mTileEntity, true); + } +} diff --git a/src/main/java/gregtech/api/multitileentity/MultiTileEntityItemInternal.java b/src/main/java/gregtech/api/multitileentity/MultiTileEntityItemInternal.java new file mode 100644 index 0000000000..cc10485f84 --- /dev/null +++ b/src/main/java/gregtech/api/multitileentity/MultiTileEntityItemInternal.java @@ -0,0 +1,354 @@ +package gregtech.api.multitileentity; + +import static gregtech.GT_Mod.GT_FML_LOGGER; +import static gregtech.api.enums.GT_Values.SIDE_TOP; + +import java.util.List; + +import net.minecraft.block.Block; +import net.minecraft.block.BlockSnow; +import net.minecraft.client.renderer.texture.IIconRegister; +import net.minecraft.creativetab.CreativeTabs; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.Blocks; +import net.minecraft.init.Items; +import net.minecraft.item.Item; +import net.minecraft.item.ItemBlock; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.AxisAlignedBB; +import net.minecraft.util.IIcon; +import net.minecraft.world.World; +import net.minecraftforge.common.util.ForgeDirection; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.IFluidContainerItem; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import gregtech.api.GregTech_API; +import gregtech.api.metatileentity.CoverableTileEntity; +import gregtech.api.multitileentity.interfaces.IItemUpdatable; +import gregtech.api.multitileentity.interfaces.IMultiTileEntity; +import gregtech.api.multitileentity.interfaces.IMultiTileEntity.IMTE_AddToolTips; +import gregtech.api.multitileentity.interfaces.IMultiTileEntity.IMTE_CanPlace; +import gregtech.api.multitileentity.interfaces.IMultiTileEntity.IMTE_GetMaxStackSize; +import gregtech.api.multitileentity.interfaces.IMultiTileEntity.IMTE_HasMultiBlockMachineRelevantData; +import gregtech.api.multitileentity.interfaces.IMultiTileEntity.IMTE_IgnoreEntityCollisionWhenPlacing; +import gregtech.api.multitileentity.interfaces.IMultiTileEntity.IMTE_OnlyPlaceableWhenSneaking; + +public class MultiTileEntityItemInternal extends ItemBlock implements IFluidContainerItem, IItemUpdatable { + + public final MultiTileEntityBlockInternal mBlock; + + public MultiTileEntityItemInternal(Block aBlock) { + super(aBlock); + setMaxDamage(0); + setHasSubtypes(true); + mBlock = (MultiTileEntityBlockInternal) aBlock; + } + + @Override + @SuppressWarnings("unchecked") + public void addInformation(ItemStack aStack, EntityPlayer aPlayer, List<String> aList, boolean aF3_H) { + final MultiTileEntityContainer tTileEntityContainer = mBlock.mMultiTileEntityRegistry + .getCachedTileEntityContainer(aStack); + if (tTileEntityContainer == null) { + aList.add("INVALID ITEM!"); + return; + } + if (tTileEntityContainer.mTileEntity instanceof IMTE_AddToolTips mte) { + try { + mte.addToolTips(aList, aStack, aF3_H); + } catch (Throwable e) { + GT_FML_LOGGER.error("addInformation", e); + } + } + final NBTTagCompound aNBT = aStack.getTagCompound(); + CoverableTileEntity.addInstalledCoversInformation(aNBT, aList); + // TODO: Add anything else relevant + } + + @Override + @SideOnly(Side.CLIENT) + @SuppressWarnings("unchecked") + public void getSubItems(Item aItem, CreativeTabs aTab, List<ItemStack> aList) { + for (MultiTileEntityClassContainer tClass : mBlock.mMultiTileEntityRegistry.mRegistrations) { + if (!tClass.mHidden && ((IMultiTileEntity) tClass.mCanonicalTileEntity) + .getSubItems(mBlock, aItem, aTab, aList, tClass.mID)) { + aList.add(mBlock.mMultiTileEntityRegistry.getItem(tClass.mID)); + } + } + } + + @Override + public boolean onItemUse(ItemStack aStack, EntityPlayer aPlayer, World aWorld, int aX, int aY, int aZ, + int ordinalSide, float aHitX, float aHitY, float aHitZ) { + + if (aY < 0 || aY > aWorld.getHeight()) return false; + + if (aPlayer == null) return false; + + try { + ForgeDirection side = ForgeDirection.getOrientation(ordinalSide); + final Block tClickedBlock = aWorld.getBlock(aX, aY, aZ); + + if (tClickedBlock instanceof BlockSnow && (aWorld.getBlockMetadata(aX, aY, aZ) & 7) < 1) { + ordinalSide = SIDE_TOP; + side = ForgeDirection.UP; + } else if (tClickedBlock != Blocks.vine && tClickedBlock != Blocks.tallgrass + && tClickedBlock != Blocks.deadbush + && !tClickedBlock.isReplaceable(aWorld, aX, aY, aZ)) { + aX += side.offsetX; + aY += side.offsetY; + aZ += side.offsetZ; + } + final Block tReplacedBlock = aWorld.getBlock(aX, aY, aZ); + + if (!tReplacedBlock.isReplaceable(aWorld, aX, aY, aZ) + || !mBlock.canReplace(aWorld, aX, aY, aZ, ordinalSide, aStack)) { + return false; + } + + if (aStack.stackSize == 0 || (!aPlayer.canPlayerEdit(aX, aY, aZ, ordinalSide, aStack))) { + return false; + } + + final MultiTileEntityContainer aMTEContainer = mBlock.mMultiTileEntityRegistry + .getNewTileEntityContainer(aWorld, aX, aY, aZ, aStack); + + if (aMTEContainer == null) return false; + + if (!aPlayer.isSneaking() && aMTEContainer.mTileEntity instanceof IMTE_OnlyPlaceableWhenSneaking mteSNeaking + && mteSNeaking.onlyPlaceableWhenSneaking()) { + return false; + } + + if ((!(aMTEContainer.mTileEntity instanceof IMTE_IgnoreEntityCollisionWhenPlacing mteIgnoreCollision) + || !mteIgnoreCollision + .ignoreEntityCollisionWhenPlacing(aStack, aPlayer, aWorld, aX, aY, aZ, side, aHitX, aHitY, aHitZ)) + && !aWorld.checkNoEntityCollision(AxisAlignedBB.getBoundingBox(aX, aY, aZ, aX + 1, aY + 1, aZ + 1))) { + return false; + } + + if (aMTEContainer.mTileEntity instanceof IMTE_CanPlace mteCanPlace + && !mteCanPlace.canPlace(aStack, aPlayer, aWorld, aX, aY, aZ, side, aHitX, aHitY, aHitZ)) { + return false; + } + + if (!aWorld.setBlock(aX, aY, aZ, aMTEContainer.mBlock, 15 - aMTEContainer.mBlockMetaData, 2)) { + return false; + } + + aMTEContainer.setMultiTile(aWorld, aX, aY, aZ); + + try { + if (((IMultiTileEntity) aMTEContainer.mTileEntity) + .onPlaced(aStack, aPlayer, aWorld, aX, aY, aZ, side, aHitX, aHitY, aHitZ)) { + aWorld.playSoundEffect( + aX + 0.5, + aY + 0.5, + aZ + 0.5, + aMTEContainer.mBlock.stepSound.func_150496_b(), + (aMTEContainer.mBlock.stepSound.getVolume() + 1) / 2, + aMTEContainer.mBlock.stepSound.getPitch() * 0.8F); + } + } catch (Throwable e) { + GT_FML_LOGGER.error("onPlaced", e); + } + try { + if (aMTEContainer.mTileEntity instanceof IMTE_HasMultiBlockMachineRelevantData mteData + && (mteData.hasMultiBlockMachineRelevantData())) { + GregTech_API.causeMachineUpdate(aWorld, aX, aY, aZ); + } + } catch (Throwable e) { + GT_FML_LOGGER.error("causeMachineUpdate", e); + } + try { + if (!aWorld.isRemote) { + aWorld.notifyBlockChange(aX, aY, aZ, tReplacedBlock); + aWorld.func_147453_f /* updateNeighborsAboutBlockChange */(aX, aY, aZ, aMTEContainer.mBlock); + } + } catch (Throwable e) { + GT_FML_LOGGER.error("notifyBlockChange", e); + } + try { + ((IMultiTileEntity) aMTEContainer.mTileEntity).onTileEntityPlaced(); + } catch (Throwable e) { + GT_FML_LOGGER.error("onTileEntityPlaced", e); + } + try { + aWorld.func_147451_t /* updateAllLightTypes */(aX, aY, aZ); + } catch (Throwable e) { + GT_FML_LOGGER.error("updateAllLightTypes", e); + } + + aStack.stackSize--; + return true; + + } catch (Throwable e) { + GT_FML_LOGGER.error("onItemUse", e); + } + return false; + } + + @Override + public void updateItemStack(ItemStack aStack) { + final MultiTileEntityClassContainer tContainer = mBlock.mMultiTileEntityRegistry.getClassContainer(aStack); + if (tContainer == null) return; + final MultiTileEntityContainer tTileEntityContainer = mBlock.mMultiTileEntityRegistry + .getCachedTileEntityContainer(aStack); + if (tTileEntityContainer != null && tTileEntityContainer.mTileEntity instanceof IItemUpdatable itemUpdatable) { + itemUpdatable.updateItemStack(aStack); + } + } + + @Override + public void updateItemStack(ItemStack aStack, World aWorld, int aX, int aY, int aZ) { + final MultiTileEntityClassContainer tContainer = mBlock.mMultiTileEntityRegistry.getClassContainer(aStack); + if (tContainer == null) return; + final MultiTileEntityContainer tTileEntityContainer = mBlock.mMultiTileEntityRegistry + .getCachedTileEntityContainer(aStack); + if (tTileEntityContainer != null && tTileEntityContainer.mTileEntity instanceof IItemUpdatable itemUpdatable) { + itemUpdatable.updateItemStack(aStack, aWorld, aX, aY, aZ); + } + } + + @Override + public int getItemStackLimit(ItemStack aStack) { + final MultiTileEntityClassContainer tContainer = mBlock.mMultiTileEntityRegistry.getClassContainer(aStack); + if (tContainer == null) return 1; + final MultiTileEntityContainer tTileEntityContainer = mBlock.mMultiTileEntityRegistry + .getCachedTileEntityContainer(aStack); + if (tTileEntityContainer != null + && tTileEntityContainer.mTileEntity instanceof IMTE_GetMaxStackSize maxStackSize) { + return maxStackSize.getMaxStackSize(aStack, tContainer.mStackSize); + } + return tContainer.mStackSize; + } + + @Override + public void onCreated(ItemStack aStack, World aWorld, EntityPlayer aPlayer) { + updateItemStack(aStack); + } + + @Override + public FluidStack getFluid(ItemStack aStack) { + final MultiTileEntityContainer tTileEntityContainer = mBlock.mMultiTileEntityRegistry + .getCachedTileEntityContainer(aStack); + if (tTileEntityContainer != null + && tTileEntityContainer.mTileEntity instanceof IFluidContainerItem fluidContainerItem) { + final FluidStack rFluid = fluidContainerItem.getFluid(aStack); + updateItemStack(aStack); + return rFluid; + } + return null; + } + + @Override + public int getCapacity(ItemStack aStack) { + final MultiTileEntityContainer tTileEntityContainer = mBlock.mMultiTileEntityRegistry + .getCachedTileEntityContainer(aStack); + if (tTileEntityContainer != null + && tTileEntityContainer.mTileEntity instanceof IFluidContainerItem fluidContainerItem) { + final int rCapacity = fluidContainerItem.getCapacity(aStack); + updateItemStack(aStack); + return rCapacity; + } + return 0; + } + + @Override + public int fill(ItemStack aStack, FluidStack aFluid, boolean aDoFill) { + final MultiTileEntityContainer tTileEntityContainer = mBlock.mMultiTileEntityRegistry + .getCachedTileEntityContainer(aStack); + if (tTileEntityContainer != null + && tTileEntityContainer.mTileEntity instanceof IFluidContainerItem fluidContainerItem) { + final int tFilled = fluidContainerItem.fill(aStack, aFluid, aDoFill); + updateItemStack(aStack); + return tFilled; + } + return 0; + } + + @Override + public FluidStack drain(ItemStack aStack, int aMaxDrain, boolean aDoDrain) { + final MultiTileEntityContainer tTileEntityContainer = mBlock.mMultiTileEntityRegistry + .getCachedTileEntityContainer(aStack); + if (tTileEntityContainer != null + && tTileEntityContainer.mTileEntity instanceof IFluidContainerItem fluidContainerItem) { + final FluidStack rFluid = fluidContainerItem.drain(aStack, aMaxDrain, aDoDrain); + updateItemStack(aStack); + return rFluid; + } + return null; + } + + @Override + public boolean func_150936_a /* canPlaceAtSide */(World aWorld, int aX, int aY, int aZ, int ordinalSide, + EntityPlayer aPlayer, ItemStack aStack) { + return true; + } + + @Override + public final String getUnlocalizedName() { + return mBlock.mMultiTileEntityRegistry.mNameInternal; + } + + @Override + public final String getUnlocalizedName(ItemStack aStack) { + return mBlock.mMultiTileEntityRegistry.mNameInternal + "." + getDamage(aStack); + } + + @Override + public int getSpriteNumber() { + return 0; + } + + @Override + @SideOnly(Side.CLIENT) + public void registerIcons(IIconRegister aRegister) { + /* Empty */ + } + + @Override + @SideOnly(Side.CLIENT) + public IIcon getIconFromDamage(int aMeta) { + itemIcon = Items.bread.getIconFromDamage(0); + return itemIcon; /* Fixes Eating Animation Particles. */ + } + + @Override + public boolean doesContainerItemLeaveCraftingGrid(ItemStack aStack) { + return false; + } + + @Override + public final boolean getShareTag() { + return true; // just to be sure + } + + @Override + public int getItemEnchantability() { + return 0; + } + + @Override + public boolean getIsRepairable(ItemStack aStack, ItemStack aMaterial) { + return false; + } + + @Override + public ItemStack getContainerItem(ItemStack aStack) { + return null; + } + + @Override + public final boolean hasContainerItem(ItemStack aStack) { + return getContainerItem(aStack) != null; + } + + @Override + public boolean isBookEnchantable(ItemStack aStack, ItemStack aBook) { + return false; + } +} diff --git a/src/main/java/gregtech/api/multitileentity/MultiTileEntityRegistry.java b/src/main/java/gregtech/api/multitileentity/MultiTileEntityRegistry.java new file mode 100644 index 0000000000..3392d1ab41 --- /dev/null +++ b/src/main/java/gregtech/api/multitileentity/MultiTileEntityRegistry.java @@ -0,0 +1,290 @@ +package gregtech.api.multitileentity; + +import static gregtech.GT_Mod.GT_FML_LOGGER; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; + +import net.minecraft.block.Block; +import net.minecraft.init.Items; +import net.minecraft.item.Item; +import net.minecraft.item.ItemBlock; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.StatCollector; +import net.minecraft.world.World; + +import com.gtnewhorizon.gtnhlib.util.map.ItemStackMap; + +import appeng.core.CreativeTab; +import cpw.mods.fml.common.Loader; +import cpw.mods.fml.common.LoaderState; +import cpw.mods.fml.common.registry.GameRegistry; +import gregtech.api.enums.GT_Values; +import gregtech.api.multitileentity.base.MultiTileEntity; +import gregtech.api.multitileentity.interfaces.IMultiTileEntity; +import gregtech.api.util.GT_LanguageManager; +import gregtech.api.util.GT_Util; +import gregtech.api.util.GT_Utility; + +public class MultiTileEntityRegistry { + + private static final HashMap<String, MultiTileEntityRegistry> NAMED_REGISTRIES = new HashMap<>(); + + // TODO: NBT sensitive or not? Starting with not for now + private static final ItemStackMap<MultiTileEntityRegistry> REGISTRIES = new ItemStackMap<>(false); + private static final HashSet<Class<?>> sRegisteredTileEntities = new HashSet<>(); + private final HashMap<Integer, MultiTileEntityContainer> cachedTileEntityContainers = new HashMap<>(); + + public HashMap<Short, CreativeTab> mCreativeTabs = new HashMap<>(); + public Map<Short, MultiTileEntityClassContainer> mRegistry = new HashMap<>(); + public List<MultiTileEntityClassContainer> mRegistrations = new ArrayList<>(); + + public final String mNameInternal; + public final MultiTileEntityBlockInternal mBlock; + + private static MultiTileEntityBlockInternal regblock(String aNameInternal, MultiTileEntityBlockInternal aBlock, + Class<? extends ItemBlock> aItemClass) { + GameRegistry.registerBlock(aBlock, aItemClass == null ? ItemBlock.class : aItemClass, aNameInternal); + return aBlock; + } + + /** + * @param aNameInternal the internal Name of the Item + */ + public MultiTileEntityRegistry(String aNameInternal) { + this(aNameInternal, new MultiTileEntityBlockInternal(), MultiTileEntityItemInternal.class); + } + + /** + * @param aNameInternal the internal Name of the Item + */ + public MultiTileEntityRegistry(String aNameInternal, MultiTileEntityBlockInternal aBlock, + Class<? extends ItemBlock> aItemClass) { + this(aNameInternal, regblock(aNameInternal, aBlock, aItemClass)); + } + + /** + * @param aNameInternal the internal Name of the Item + */ + public MultiTileEntityRegistry(String aNameInternal, MultiTileEntityBlockInternal aBlock) { + if (!Loader.instance() + .isInState(LoaderState.PREINITIALIZATION)) { + throw new IllegalStateException( + "The MultiTileEntity Registry must be initialized during Preload Phase and not before"); + } + mNameInternal = aNameInternal; + mBlock = aBlock; + GT_FML_LOGGER.info(aNameInternal + " " + Block.getIdFromBlock(aBlock) + "This is the answer"); + mBlock.mMultiTileEntityRegistry = this; + REGISTRIES.put(new ItemStack(Item.getItemById(Block.getIdFromBlock(aBlock)), 1, GT_Values.W), this); + NAMED_REGISTRIES.put(mNameInternal, this); + } + + public static TileEntity getCanonicalTileEntity(int aRegistryID, int aMultiTileEntityID) { + final MultiTileEntityRegistry tRegistry = getRegistry(aRegistryID); + if (tRegistry == null) return null; + final MultiTileEntityClassContainer tClassContainer = tRegistry.getClassContainer(aMultiTileEntityID); + if (tClassContainer == null) return null; + return tClassContainer.mCanonicalTileEntity; + } + + public static MultiTileEntityRegistry getRegistry(int aRegistryID) { + return REGISTRIES.get(new ItemStack(Item.getItemById(aRegistryID), 1, GT_Values.W)); + } + + public static MultiTileEntityRegistry getRegistry(String aRegistryName) { + return NAMED_REGISTRIES.get(aRegistryName); + } + + public MultiTileEntityClassContainer create(int aID, Class<? extends MultiTileEntity> aClass) { + return new MultiTileEntityClassContainer(this, aID, aClass); + } + + /** + * Adds a new MultiTileEntity. It is highly recommended to do this in either the PreInit or the Init Phase. PostInit + * might not work well. + */ + public ItemStack add(String aLocalised, String aCategoricalName, MultiTileEntityClassContainer aClassContainer) { + boolean tFailed = false; + if (GT_Utility.isStringInvalid(aLocalised)) { + GT_FML_LOGGER.error("MULTI-TILE REGISTRY ERROR: Localisation Missing!"); + tFailed = true; + } + if (aClassContainer == null) { + GT_FML_LOGGER.error("MULTI-TILE REGISTRY ERROR: Class Container is null!"); + tFailed = true; + } else { + if (aClassContainer.mClass == null) { + GT_FML_LOGGER.error("MULTI-TILE REGISTRY ERROR: Class inside Class Container is null!"); + tFailed = true; + } + if (aClassContainer.mID == GT_Values.W) { + GT_FML_LOGGER.error("MULTI-TILE REGISTRY ERROR: Class Container uses Wildcard MetaData!"); + tFailed = true; + } + if (aClassContainer.mID < 0) { + GT_FML_LOGGER.error("MULTI-TILE REGISTRY ERROR: Class Container uses negative MetaData!"); + tFailed = true; + } + if (mRegistry.containsKey(aClassContainer.mID)) { + GT_FML_LOGGER.error( + "MULTI-TILE REGISTRY ERROR: Class Container uses occupied MetaData! (" + aClassContainer.mID + ")"); + tFailed = true; + } + } + if (tFailed) { + GT_FML_LOGGER.error("MULTI-TILE REGISTRY ERROR: STACKTRACE START"); + int i = 0; + for (StackTraceElement tElement : new Exception().getStackTrace()) if (i++ < 5 && !tElement.getClassName() + .startsWith("sun")) GT_FML_LOGGER.error("\tat " + tElement); + else break; + GT_FML_LOGGER.error("MULTI-TILE REGISTRY ERROR: STACKTRACE END"); + return null; + } + + GT_LanguageManager.addStringLocalization(mNameInternal + "." + aClassContainer.mID + ".name", aLocalised); + mRegistry.put(aClassContainer.mID, aClassContainer); + mLastRegisteredID = aClassContainer.mID; + mRegistrations.add(aClassContainer); + + if (sRegisteredTileEntities.add(aClassContainer.mCanonicalTileEntity.getClass())) { + aClassContainer.mCanonicalTileEntity.onRegistrationFirst(this, aClassContainer.mID); + } + // // TODO: Recipe + // if (aRecipe != null && aRecipe.length > 1) { + // if (aRecipe[0] instanceof Object[]) aRecipe = (Object[])aRecipe[0]; + // if (aRecipe.length > 2) CR.shaped(getItem(aClassContainer.mID), CR.DEF_REV_NCC, aRecipe); + // } + // // A simple special case to make it easier to add a Machine to Recipe Lists without having to worry + // about anything. + // String tRecipeMapName = aClassContainer.mParameters.getString(NBT_RECIPEMAP); + // if (GT_Utility.isStringValid(tRecipeMapName)) {RecipeMap tMap = + // RecipeMap.RECIPE_MAPS.get(tRecipeMapName); if (tMap != null) + // tMap.mRecipeMachineList.add(getItem(aClassContainer.mID));} + // tRecipeMapName = aClassContainer.mParameters.getString(NBT_FUELMAP); + // if (GT_Utility.isStringValid(tRecipeMapName)) {RecipeMap tMap = + // RecipeMap.RECIPE_MAPS.get(tRecipeMapName); if (tMap != null) + // tMap.mRecipeMachineList.add(getItem(aClassContainer.mID));} + // + return getItem(aClassContainer.mID); + } + + public short mLastRegisteredID = GT_Values.W; + + public ItemStack getItem() { + return getItem(mLastRegisteredID, 1, null); + } + + public ItemStack getItem(int aID) { + return getItem(aID, 1, null); + } + + public ItemStack getItem(int aID, NBTTagCompound aNBT) { + return getItem(aID, 1, aNBT); + } + + public ItemStack getItem(int aID, long aAmount) { + return getItem(aID, aAmount, null); + } + + public ItemStack getItem(int aID, long aAmount, NBTTagCompound aNBT) { + final ItemStack rStack = new ItemStack(mBlock, (int) aAmount, aID); + if (aNBT == null || aNBT.hasNoTags()) { + aNBT = new NBTTagCompound(); + final MultiTileEntityContainer tTileEntityContainer = getNewTileEntityContainer(aID, aNBT); + if (tTileEntityContainer != null) ((IMultiTileEntity) tTileEntityContainer.mTileEntity).writeItemNBT(aNBT); + } + rStack.setTagCompound(aNBT); + return rStack; + } + + public String getLocal(int aID) { + return StatCollector.translateToLocal(mNameInternal + "." + aID + ".name"); + } + + public MultiTileEntityClassContainer getClassContainer(int aID) { + return mRegistry.get((short) aID); + } + + public MultiTileEntityClassContainer getClassContainer(ItemStack aStack) { + return mRegistry.get((short) Items.feather.getDamage(aStack)); + } + + public TileEntity getNewTileEntity(int aID) { + final MultiTileEntityContainer tContainer = getNewTileEntityContainer(null, 0, 0, 0, aID, null); + return tContainer == null ? null : tContainer.mTileEntity; + } + + public MultiTileEntityContainer getNewTileEntityContainer(World aWorld, int aX, int aY, int aZ, int aID, + NBTTagCompound aNBT) { + final MultiTileEntityClassContainer tClass = mRegistry.get((short) aID); + if (tClass == null || tClass.mBlock == null) return null; + final MultiTileEntityContainer rContainer = new MultiTileEntityContainer( + (TileEntity) GT_Utility.callConstructor(tClass.mClass, -1, null, true), + tClass.mBlock, + tClass.mBlockMetaData); + if (rContainer.mTileEntity == null) return null; + rContainer.mTileEntity.setWorldObj(aWorld); + rContainer.mTileEntity.xCoord = aX; + rContainer.mTileEntity.yCoord = aY; + rContainer.mTileEntity.zCoord = aZ; + ((IMultiTileEntity) rContainer.mTileEntity).initFromNBT( + aNBT == null || aNBT.hasNoTags() ? tClass.mParameters : GT_Util.fuseNBT(aNBT, tClass.mParameters), + (short) aID, + (short) Block.getIdFromBlock(mBlock)); + return rContainer; + } + + public TileEntity getNewTileEntity(World aWorld, int aX, int aY, int aZ, int aID) { + final MultiTileEntityContainer tContainer = getNewTileEntityContainer(aWorld, aX, aY, aZ, aID, null); + return tContainer == null ? null : tContainer.mTileEntity; + } + + public TileEntity getNewTileEntity(ItemStack aStack) { + final MultiTileEntityContainer tContainer = getNewTileEntityContainer( + null, + 0, + 0, + 0, + Items.feather.getDamage(aStack), + aStack.getTagCompound()); + return tContainer == null ? null : tContainer.mTileEntity; + } + + public TileEntity getNewTileEntity(World aWorld, int aX, int aY, int aZ, ItemStack aStack) { + final MultiTileEntityContainer tContainer = getNewTileEntityContainer( + aWorld, + aX, + aY, + aZ, + Items.feather.getDamage(aStack), + aStack.getTagCompound()); + return tContainer == null ? null : tContainer.mTileEntity; + } + + public MultiTileEntityContainer getCachedTileEntityContainer(ItemStack stack) { + MultiTileEntityContainer container = cachedTileEntityContainers.get(Items.feather.getDamage(stack)); + if (container == null) { + container = getNewTileEntityContainer(stack); + cachedTileEntityContainers.put(Items.feather.getDamage(stack), container); + } + return container; + } + + public MultiTileEntityContainer getNewTileEntityContainer(ItemStack aStack) { + return getNewTileEntityContainer(null, 0, 0, 0, Items.feather.getDamage(aStack), aStack.getTagCompound()); + } + + public MultiTileEntityContainer getNewTileEntityContainer(World aWorld, int aX, int aY, int aZ, ItemStack aStack) { + return getNewTileEntityContainer(aWorld, aX, aY, aZ, Items.feather.getDamage(aStack), aStack.getTagCompound()); + } + + public MultiTileEntityContainer getNewTileEntityContainer(int aID, NBTTagCompound aNBT) { + return getNewTileEntityContainer(null, 0, 0, 0, aID, aNBT); + } +} diff --git a/src/main/java/gregtech/api/multitileentity/base/MultiTileEntity.java b/src/main/java/gregtech/api/multitileentity/base/MultiTileEntity.java new file mode 100644 index 0000000000..7c56d40296 --- /dev/null +++ b/src/main/java/gregtech/api/multitileentity/base/MultiTileEntity.java @@ -0,0 +1,1381 @@ +package gregtech.api.multitileentity.base; + +import static gregtech.GT_Mod.GT_FML_LOGGER; +import static gregtech.api.enums.GT_Values.VALID_SIDES; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +import javax.annotation.Nonnull; + +import net.minecraft.block.Block; +import net.minecraft.client.Minecraft; +import net.minecraft.creativetab.CreativeTabs; +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.init.Items; +import net.minecraft.item.Item; +import net.minecraft.item.ItemBlock; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.network.Packet; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.AxisAlignedBB; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.MovingObjectPosition; +import net.minecraft.util.ResourceLocation; +import net.minecraft.world.Explosion; +import net.minecraft.world.World; +import net.minecraftforge.common.util.ForgeDirection; +import net.minecraftforge.fluids.Fluid; + +import com.gtnewhorizons.modularui.common.internal.network.NetworkUtils; + +import cpw.mods.fml.common.registry.GameRegistry; +import gregtech.api.GregTech_API; +import gregtech.api.enums.GT_Values; +import gregtech.api.enums.GT_Values.NBT; +import gregtech.api.enums.Materials; +import gregtech.api.enums.Mods; +import gregtech.api.enums.SoundResource; +import gregtech.api.enums.Textures; +import gregtech.api.enums.Textures.BlockIcons.CustomIcon; +import gregtech.api.gui.modularui.GT_UIInfos; +import gregtech.api.interfaces.ITexture; +import gregtech.api.metatileentity.CoverableTileEntity; +import gregtech.api.metatileentity.GregTechTileClientEvents; +import gregtech.api.multitileentity.MultiTileEntityBlockInternal; +import gregtech.api.multitileentity.MultiTileEntityClassContainer; +import gregtech.api.multitileentity.MultiTileEntityRegistry; +import gregtech.api.multitileentity.interfaces.IMultiTileEntity; +import gregtech.api.multitileentity.interfaces.SyncedMultiTileEntity; +import gregtech.api.net.GT_Packet_MultiTileEntity; +import gregtech.api.net.GT_Packet_New; +import gregtech.api.net.data.CommonData; +import gregtech.api.net.data.CoordinateData; +import gregtech.api.net.data.MultiTileEntityData; +import gregtech.api.objects.GT_ItemStack; +import gregtech.api.objects.XSTR; +import gregtech.api.render.TextureFactory; +import gregtech.api.util.GT_Log; +import gregtech.api.util.GT_ModHandler; +import gregtech.api.util.GT_Util; +import gregtech.api.util.GT_Utility; +import gregtech.common.render.MultiTileBasicRender; +import mcp.mobius.waila.api.IWailaConfigHandler; +import mcp.mobius.waila.api.IWailaDataAccessor; + +public abstract class MultiTileEntity extends CoverableTileEntity + implements IMultiTileEntity.IMTE_BreakBlock, MultiTileBasicRender, SyncedMultiTileEntity { + + private ITexture baseTexture = null; + private ITexture topOverlayTexture = null; + private ITexture bottomOverlayTexture = null; + private ITexture leftOverlayTexture = null; + private ITexture rightOverlayTexture = null; + private ITexture backOverlayTexture = null; + private ITexture frontOverlayTexture = null; + + // A Bounding Box without having to constantly specify the Offset Coordinates. + protected static final float[] PX_BOX = { 0, 0, 0, 1, 1, 1 }; + + public Materials material = Materials._NULL; + protected final boolean isTicking; // If this TileEntity is ticking at all + + // Checks if this TileEntity should refresh when the Block is being set. + // This way you can toggle this check at any time. + protected boolean shouldRefresh = true; + + protected boolean needsBlockUpdate = false; // This Variable is for a buffered Block Update. + protected boolean forceFullSelectionBox = false; // This Variable is for forcing the Selection Box to be full. + protected boolean needsUpdate = false; + protected boolean hasInventoryChanged = false; + protected boolean isPainted = false; + @Nonnull + protected ForgeDirection facing = ForgeDirection.WEST; // Default to WEST, so it renders facing Left in the + // inventory + protected byte color; + protected int rgba = GT_Values.UNCOLORED; + private short mteID = GT_Values.W, mteRegistry = GT_Values.W; + private String customName = null; + private String ownerName = ""; + private UUID ownerUUID = GT_Utility.defaultUuid; + private boolean lockUpgrade = false; + + private final GT_Packet_MultiTileEntity fullPacket = new GT_Packet_MultiTileEntity(false); + private final GT_Packet_MultiTileEntity timedPacket = new GT_Packet_MultiTileEntity(false); + private final GT_Packet_MultiTileEntity graphicPacket = new GT_Packet_MultiTileEntity(false); + + public MultiTileEntity(boolean isTicking) { + this.isTicking = isTicking; + } + + @Override + public short getMultiTileEntityID() { + return mteID; + } + + @Override + public short getMultiTileEntityRegistryID() { + return mteRegistry; + } + + @Override + public void onRegistrationFirst(MultiTileEntityRegistry registry, short id) { + GameRegistry.registerTileEntity(getClass(), getTileEntityName()); + } + + @Override + public void initFromNBT(NBTTagCompound nbt, short mteID, short mteRegistry) { + if (this.mteID == mteID && this.mteRegistry == mteRegistry) { + return; + } + // Set ID and Registry ID. + this.mteID = mteID; + this.mteRegistry = mteRegistry; + // Read the Default Parameters from NBT. + if (nbt != null) readFromNBT(nbt); + } + + @Override + public void loadTextures(String folder) { + // Loading the registry + for (SidedTextureNames textureName : SidedTextureNames.TEXTURES) { + ITexture texture; + try { + Minecraft.getMinecraft() + .getResourceManager() + .getResource( + new ResourceLocation( + Mods.GregTech.ID, + "textures/blocks/multitileentity/" + folder + "/" + textureName.getName() + ".png")); + texture = TextureFactory.of(new CustomIcon("multitileentity/" + folder + "/" + textureName.getName())); + } catch (IOException ignored) { + texture = TextureFactory.of(Textures.BlockIcons.VOID); + } + switch (textureName) { + case Top -> topOverlayTexture = texture; + case Bottom -> bottomOverlayTexture = texture; + case Back -> backOverlayTexture = texture; + case Front -> frontOverlayTexture = texture; + case Left -> leftOverlayTexture = texture; + case Right -> rightOverlayTexture = texture; + case Base -> baseTexture = texture; + } + } + } + + @Override + public void copyTextures() { + // Loading an instance + final TileEntity tCanonicalTileEntity = MultiTileEntityRegistry + .getCanonicalTileEntity(getMultiTileEntityRegistryID(), getMultiTileEntityID()); + if (!(tCanonicalTileEntity instanceof MultiTileEntity)) { + return; + } + final MultiTileEntity canonicalEntity = (MultiTileEntity) tCanonicalTileEntity; + baseTexture = canonicalEntity.baseTexture; + topOverlayTexture = canonicalEntity.topOverlayTexture; + bottomOverlayTexture = canonicalEntity.bottomOverlayTexture; + leftOverlayTexture = canonicalEntity.leftOverlayTexture; + rightOverlayTexture = canonicalEntity.rightOverlayTexture; + backOverlayTexture = canonicalEntity.backOverlayTexture; + frontOverlayTexture = canonicalEntity.frontOverlayTexture; + } + + @Override + public ITexture getTexture(ForgeDirection side) { + if (facing == side) { + return TextureFactory.of(baseTexture, frontOverlayTexture); + } + + if (facing.getOpposite() == side) { + return TextureFactory.of(baseTexture, backOverlayTexture); + } + + if (side == ForgeDirection.UP) { + return TextureFactory.of(baseTexture, topOverlayTexture); + } + + if (side == ForgeDirection.DOWN) { + return TextureFactory.of(baseTexture, bottomOverlayTexture); + } + + if (facing.getRotation(ForgeDirection.DOWN) == side) { + return TextureFactory.of(baseTexture, rightOverlayTexture); + } else { + return TextureFactory.of(baseTexture, leftOverlayTexture); + } + } + + @Override + public void readFromNBT(NBTTagCompound nbt) { + // Check if it is a World/Chunk-Loading Process calling readFromNBT + if (mteID == GT_Values.W || mteRegistry == GT_Values.W) { + // Read the ID Tags first + mteID = nbt.getShort(NBT.MTE_ID); + mteRegistry = nbt.getShort(NBT.MTE_REG); + // Add additional Default Parameters in case the Mod updated with new ones + final MultiTileEntityRegistry tRegistry = MultiTileEntityRegistry.getRegistry(mteRegistry); + if (tRegistry != null) { + final MultiTileEntityClassContainer tClass = tRegistry.getClassContainer(mteID); + if (tClass != null) { + // Add the Default Parameters. Useful for things that differ between different tiers/types of the + // same machine + nbt = GT_Util.fuseNBT(nbt, tClass.mParameters); + } + } + } + // read the Coords if it has them. + if (nbt.hasKey("x")) xCoord = nbt.getInteger("x"); + if (nbt.hasKey("y")) yCoord = nbt.getInteger("y"); + if (nbt.hasKey("z")) zCoord = nbt.getInteger("z"); + // read the custom Name. + if (nbt.hasKey(NBT.DISPLAY)) customName = nbt.getCompoundTag(NBT.DISPLAY) + .getString(NBT.CUSTOM_NAME); + + // And now everything else. + try { + if (nbt.hasKey(NBT.MATERIAL)) material = Materials.get(nbt.getString(NBT.MATERIAL)); + if (nbt.hasKey(NBT.COLOR)) rgba = nbt.getInteger(NBT.COLOR); + + ownerName = nbt.getString(NBT.OWNER); + try { + ownerUUID = UUID.fromString(nbt.getString(NBT.OWNER_UUID)); + } catch (IllegalArgumentException e) { + ownerUUID = null; + } + if (nbt.hasKey(NBT.LOCK_UPGRADE)) lockUpgrade = nbt.getBoolean(NBT.LOCK_UPGRADE); + if (nbt.hasKey(NBT.FACING)) facing = ForgeDirection.getOrientation(nbt.getInteger(NBT.FACING)); + + readCoverNBT(nbt); + readTasksNBT(nbt); + readMultiTileNBT(nbt); + + if (NetworkUtils.isDedicatedClient()) { + if (GregTech_API.sBlockIcons == null && nbt.hasKey(NBT.TEXTURE_FOLDER)) { + loadTextures(nbt.getString(NBT.TEXTURE_FOLDER)); + } else { + copyTextures(); + } + } + + if (mSidedRedstone.length != 6) mSidedRedstone = new byte[] { 15, 15, 15, 15, 15, 15 }; + + updateCoverBehavior(); + + } catch (Throwable e) { + GT_FML_LOGGER.error("readFromNBT", e); + } + } + + public void readMultiTileNBT(NBTTagCompound aNBT) { + /* Do Nothing */ + } + + protected void readTasksNBT(NBTTagCompound nbt) {} + + @Override + public final void writeToNBT(NBTTagCompound aNBT) { + super.writeToNBT(aNBT); + // write the IDs + aNBT.setShort(NBT.MTE_ID, mteID); + aNBT.setShort(NBT.MTE_REG, mteRegistry); + // write the Custom Name + if (GT_Utility.isStringValid(customName)) { + final NBTTagCompound displayNBT; + if (aNBT.hasKey(NBT.DISPLAY)) { + displayNBT = aNBT.getCompoundTag(NBT.DISPLAY); + } else { + displayNBT = new NBTTagCompound(); + aNBT.setTag(NBT.DISPLAY, displayNBT); + } + displayNBT.setString(NBT.CUSTOM_NAME, customName); + } + + // write the rest + try { + aNBT.setString(NBT.OWNER, ownerName); + aNBT.setString(NBT.OWNER_UUID, ownerUUID == null ? "" : ownerUUID.toString()); + aNBT.setBoolean(NBT.LOCK_UPGRADE, lockUpgrade); + aNBT.setInteger(NBT.FACING, facing.ordinal()); + + writeCoverNBT(aNBT, false); + writeTasksNBT(aNBT); + writeMultiTileNBT(aNBT); + } catch (Throwable e) { + GT_FML_LOGGER.error("writeToNBT", e); + } + } + + public void writeMultiTileNBT(NBTTagCompound aNBT) { + /* Do Nothing */ + } + + protected void writeTasksNBT(NBTTagCompound aNBT) {} + + @Override + public NBTTagCompound writeItemNBT(NBTTagCompound aNBT) { + writeCoverNBT(aNBT, true); + if (shouldSaveNBTToItemStack()) { + writeTasksNBT(aNBT); + writeMultiTileNBT(aNBT); + } + return aNBT; + } + + protected boolean shouldSaveNBTToItemStack() { + return false; + } + + @Override + public boolean useModularUI() { + return false; + } + + @Override + public long getTimer() { + return 0; + } + + @Override + public int getRandomNumber(int aRange) { + return XSTR.XSTR_INSTANCE.nextInt(aRange); + } + + @Override + public TileEntity getTileEntity(int aX, int aY, int aZ) { + if (worldObj == null + || (ignoreUnloadedChunks && crossedChunkBorder(aX, aZ) && !worldObj.blockExists(aX, aY, aZ))) return null; + return GT_Util.getTileEntity(worldObj, aX, aY, aZ, true); + } + + @Override + public boolean canUpdate() { + return isTicking && shouldRefresh; + } + + @Override + public boolean shouldRefresh(Block aOldBlock, Block aNewBlock, int aOldMeta, int aNewMeta, World aWorld, int aX, + int aY, int aZ) { + return shouldRefresh || aOldBlock != aNewBlock; + } + + @Override + public void updateEntity() { + super.updateEntity(); + if (needsBlockUpdate) doBlockUpdate(); + } + + public void doBlockUpdate() { + final Block tBlock = getBlock(getCoords()); + worldObj.notifyBlocksOfNeighborChange(xCoord, yCoord, zCoord, tBlock); + if (this instanceof IMTE_IsProvidingStrongPower) { + for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) { + if (getBlockAtSide(side) + .isNormalCube(worldObj, xCoord + side.offsetX, yCoord + side.offsetY, zCoord + side.offsetZ)) { + worldObj.notifyBlocksOfNeighborChange( + xCoord + side.offsetX, + yCoord + side.offsetY, + zCoord + side.offsetZ, + tBlock, + side.getOpposite() + .ordinal()); + } + } + } + needsBlockUpdate = false; + } + + @Override + public boolean shouldSideBeRendered(ForgeDirection side) { + final TileEntity tTileEntity = getTileEntityAtSideAndDistance(side, 1); + // TODO: check to an interface + // if (getBlockAtSide(aSide) == Blocks.glass) return false; + return tTileEntity instanceof IMultiTileEntity mte ? !mte.isSurfaceOpaque(side.getOpposite()) + : !getBlockAtSide(side).isOpaqueCube(); + } + + @Override + public boolean isSurfaceOpaque(ForgeDirection side) { + return true; + } + + @Override + public void setCustomName(String aName) { + customName = aName; + } + + @Override + public String getCustomName() { + return GT_Utility.isStringValid(customName) ? customName : null; + } + + @Override + public byte getColorization() { + // TODO + return 0; + } + + @Override + public boolean unpaint() { + return false; + } + + @Override + public byte setColorization(byte aColor) { + // TODO + return 0; + } + + @Override + public boolean isPainted() { + return false; + } + + @Override + public boolean paint(int aRGB) { + return false; + } + + @Override + public boolean isFacingValid(ForgeDirection facing) { + return false; + } + + @Override + public ForgeDirection getFrontFacing() { + return facing; + } + + /** + * Sets the main facing to {aSide} and update as appropriately + * + * @return Whether the facing was changed + */ + @Override + public boolean setMainFacing(ForgeDirection side) { + if (!isValidFacing(side)) return false; + facing = side; + + issueClientUpdate(); + issueBlockUpdate(); + onFacingChange(); + checkDropCover(); + doEnetUpdate(); + + if (shouldTriggerBlockUpdate()) { + // If we're triggering a block update this will call onMachineBlockUpdate() + GregTech_API.causeMachineUpdate(worldObj, xCoord, yCoord, zCoord); + } else { + // If we're not trigger a cascading one, call the update here. + onMachineBlockUpdate(); + } + return true; + } + + @Override + public int getPaint() { + return this.rgba; + } + + @Override + public ForgeDirection getBackFacing() { + return facing.getOpposite(); + } + + @Override + public boolean isValidFacing(ForgeDirection side) { + return side != ForgeDirection.UNKNOWN && getValidFacings()[side.ordinal()]; + } + + @Override + public boolean[] getValidFacings() { + return VALID_SIDES; + } + + @Override + public void issueCoverUpdate(ForgeDirection side) { + super.issueCoverUpdate(side); + issueClientUpdate(); + } + + public AxisAlignedBB box(double[] aBox) { + return AxisAlignedBB.getBoundingBox( + xCoord + aBox[0], + yCoord + aBox[1], + zCoord + aBox[2], + xCoord + aBox[3], + yCoord + aBox[4], + zCoord + aBox[5]); + } + + public boolean box(AxisAlignedBB aAABB, List<AxisAlignedBB> aList, double aMinX, double aMinY, double aMinZ, + double aMaxX, double aMaxY, double aMaxZ) { + final AxisAlignedBB tBox = box(aMinX, aMinY, aMinZ, aMaxX, aMaxY, aMaxZ); + return tBox.intersectsWith(aAABB) && aList.add(tBox); + } + + @Override + public void onFacingChange() { + /* Do nothing */ + } + + public AxisAlignedBB box(double aMinX, double aMinY, double aMinZ, double aMaxX, double aMaxY, double aMaxZ) { + return AxisAlignedBB.getBoundingBox( + xCoord + aMinX, + yCoord + aMinY, + zCoord + aMinZ, + xCoord + aMaxX, + yCoord + aMaxY, + zCoord + aMaxZ); + } + + @Override + public boolean shouldTriggerBlockUpdate() { + return false; + } + + public boolean box(AxisAlignedBB aAABB, List<AxisAlignedBB> aList, double[] aBox) { + final AxisAlignedBB tBox = box(aBox[0], aBox[1], aBox[2], aBox[3], aBox[4], aBox[5]); + return tBox.intersectsWith(aAABB) && aList.add(tBox); + } + + @Override + public void onMachineBlockUpdate() { + /* Do nothing */ + } + + public boolean box(AxisAlignedBB aAABB, List<AxisAlignedBB> aList, float[] aBox) { + final AxisAlignedBB tBox = box(aBox[0], aBox[1], aBox[2], aBox[3], aBox[4], aBox[5]); + return tBox.intersectsWith(aAABB) && aList.add(tBox); + } + + public boolean box(AxisAlignedBB aAABB, List<AxisAlignedBB> aList) { + final AxisAlignedBB tBox = box(PX_BOX); + return tBox.intersectsWith(aAABB) && aList.add(tBox); + } + + public AxisAlignedBB box(float[] aBox) { + return AxisAlignedBB.getBoundingBox( + xCoord + aBox[0], + yCoord + aBox[1], + zCoord + aBox[2], + xCoord + aBox[3], + yCoord + aBox[4], + zCoord + aBox[5]); + } + + public boolean box(Block aBlock) { + aBlock.setBlockBounds(0, 0, 0, 1, 1, 1); + return true; + } + + /** + * Causes a general Texture update. + * <p/> + * Only used Client Side to mark Blocks dirty. + */ + @Override + public void issueTextureUpdate() { + if (!isTicking) { + markBlockForUpdate(); + } else { + needsUpdate = true; + } + } + + public boolean box(Block aBlock, double[] aBox) { + aBlock.setBlockBounds( + (float) aBox[0], + (float) aBox[1], + (float) aBox[2], + (float) aBox[3], + (float) aBox[4], + (float) aBox[5]); + return true; + } + + protected void markBlockForUpdate() { + worldObj.markBlockForUpdate(xCoord, yCoord, zCoord); + // worldObj.func_147479_m(xCoord, yCoord, zCoord); + needsUpdate = false; + } + + public boolean box(Block aBlock, float[] aBox) { + aBlock.setBlockBounds(aBox[0], aBox[1], aBox[2], aBox[3], aBox[4], aBox[5]); + return true; + } + + @Override + public void onTileEntityPlaced() { + /* empty */ + } + + public boolean box(Block aBlock, double aMinX, double aMinY, double aMinZ, double aMaxX, double aMaxY, + double aMaxZ) { + aBlock.setBlockBounds((float) aMinX, (float) aMinY, (float) aMinZ, (float) aMaxX, (float) aMaxY, (float) aMaxZ); + return true; + } + + @Override + public void setShouldRefresh(boolean aShouldRefresh) { + shouldRefresh = aShouldRefresh; + } + + /** + * shouldJoinIc2Enet - defaults to false, override to change + */ + @Override + public boolean shouldJoinIc2Enet() { + return false; + } + + @Override + public final void addCollisionBoxesToList(AxisAlignedBB aAABB, List<AxisAlignedBB> aList, Entity aEntity) { + box(getCollisionBoundingBoxFromPool(), aAABB, aList); + } + + /** + * Simple Function to prevent Block Updates from happening multiple times within the same Tick. + */ + @Override + public final void issueBlockUpdate() { + if (isTicking) needsBlockUpdate = true; + else doBlockUpdate(); + } + + @Override + public boolean isStillValid() { + return !isInvalid(); + } + + @Override + public boolean allowCoverOnSide(ForgeDirection side, GT_ItemStack aCoverID) { + return true; + } + + public AxisAlignedBB box() { + return AxisAlignedBB.getBoundingBox(xCoord, yCoord, zCoord, xCoord + 1, yCoord + 1, zCoord + 1); + } + + public boolean box(AxisAlignedBB aBox, AxisAlignedBB aAABB, List<AxisAlignedBB> aList) { + return aBox != null && aBox.intersectsWith(aAABB) && aList.add(aBox); + } + + public float[] shrunkBox() { + return PX_BOX; + } + + @Override + public void setBlockBoundsBasedOnState(Block aBlock) { + box(aBlock); + } + + @Override + public AxisAlignedBB getCollisionBoundingBoxFromPool() { + return box(); + } + + @Override + public AxisAlignedBB getSelectedBoundingBoxFromPool() { + if (forceFullSelectionBox) return box(); + return box(shrunkBox()); + } + + @Override + public ItemStack getPickBlock(MovingObjectPosition aTarget) { + final MultiTileEntityRegistry tRegistry = MultiTileEntityRegistry.getRegistry(mteRegistry); + return tRegistry == null ? null : tRegistry.getItem(mteID, writeItemNBT(new NBTTagCompound())); + } + + @Override + public void onBlockAdded() {} + + @Override + public String getOwnerName() { + if (GT_Utility.isStringInvalid(ownerName)) return "Player"; + return ownerName; + } + + @Override + public String setOwnerName(String aName) { + if (GT_Utility.isStringInvalid(aName)) return ownerName = "Player"; + return ownerName = aName; + } + + @Override + public UUID getOwnerUuid() { + return ownerUUID; + } + + @Override + public void setOwnerUuid(UUID uuid) { + ownerUUID = uuid; + } + + @Override + public boolean onPlaced(ItemStack aStack, EntityPlayer aPlayer, World aWorld, int aX, int aY, int aZ, + ForgeDirection side, float aHitX, float aHitY, float aHitZ) { + facing = getSideForPlayerPlacing(aPlayer, facing, getValidFacings()); + setOwnerUuid(aPlayer.getUniqueID()); + setOwnerName(aPlayer.getDisplayName()); + onFacingChange(); + return true; + } + + @Override + public boolean allowInteraction(Entity aEntity) { + return true; + } + + public boolean allowRightclick(Entity aEntity) { + return allowInteraction(aEntity); + } + + @Override + public boolean onBlockActivated(EntityPlayer aPlayer, ForgeDirection side, float aX, float aY, float aZ) { + try { + return allowRightclick(aPlayer) && onRightClick(aPlayer, side, aX, aY, aZ); + } catch (Throwable e) { + GT_FML_LOGGER.error("onBlockActivated Failed", e); + e.printStackTrace(GT_Log.err); + return true; + } + } + + @Override + public boolean onRightClick(EntityPlayer aPlayer, ForgeDirection side, float aX, float aY, float aZ) { + if (isClientSide()) { + // Configure Cover, sneak can also be: screwdriver, wrench, side cutter, soldering iron + if (aPlayer.isSneaking()) { + final ForgeDirection tSide = (getCoverIDAtSide(side) == 0) + ? GT_Utility.determineWrenchingSide(side, aX, aY, aZ) + : side; + return (getCoverBehaviorAtSideNew(tSide).hasCoverGUI()); + } else if (getCoverBehaviorAtSideNew(side).onCoverRightclickClient(side, this, aPlayer, aX, aY, aZ)) { + return true; + } + + if (!getCoverInfoAtSide(side).isGUIClickable()) return false; + } else { // server side + if (!privateAccess() || aPlayer.getDisplayName() + .equalsIgnoreCase(getOwnerName())) { + final ItemStack tCurrentItem = aPlayer.inventory.getCurrentItem(); + final ForgeDirection wrenchSide = GT_Utility.determineWrenchingSide(side, aX, aY, aZ); + + if (tCurrentItem != null) { + if (getColorization() >= 0 + && GT_Utility.areStacksEqual(new ItemStack(Items.water_bucket, 1), tCurrentItem)) { + // TODO (Colorization) + } + + if (GT_Utility.isStackInList(tCurrentItem, GregTech_API.sWrenchList)) + return onWrenchRightClick(aPlayer, tCurrentItem, wrenchSide, aX, aY, aZ, tCurrentItem); + if (GT_Utility.isStackInList(tCurrentItem, GregTech_API.sScrewdriverList)) + return onScrewdriverRightClick(aPlayer, tCurrentItem, wrenchSide, aX, aY, aZ, tCurrentItem); + if (GT_Utility.isStackInList(tCurrentItem, GregTech_API.sHardHammerList)) + return onHammerRightClick(aPlayer, tCurrentItem, wrenchSide, aX, aY, aZ, tCurrentItem); + if (GT_Utility.isStackInList(tCurrentItem, GregTech_API.sSoftHammerList)) + return onMalletRightClick(aPlayer, tCurrentItem, wrenchSide, aX, aY, aZ, tCurrentItem); + if (GT_Utility.isStackInList(tCurrentItem, GregTech_API.sSolderingToolList)) + return onSolderingRightClick(aPlayer, tCurrentItem, wrenchSide, aX, aY, aZ, tCurrentItem); + if (GT_Utility.isStackInList(tCurrentItem, GregTech_API.sWireCutterList)) + return onWireCutterRightClick(aPlayer, tCurrentItem, wrenchSide, aX, aY, aZ, tCurrentItem); + + final ForgeDirection coverSide = getCoverIDAtSide(side) == 0 ? wrenchSide : side; + + if (getCoverIDAtSide(coverSide) == 0) { + if (GT_Utility.isStackInList(tCurrentItem, GregTech_API.sCovers.keySet())) { + if (GregTech_API.getCoverBehaviorNew(tCurrentItem) + .isCoverPlaceable(coverSide, tCurrentItem, this) + && allowCoverOnSide(coverSide, new GT_ItemStack(tCurrentItem))) { + setCoverItemAtSide(coverSide, tCurrentItem); + if (!aPlayer.capabilities.isCreativeMode) tCurrentItem.stackSize--; + GT_Utility.sendSoundToPlayers( + worldObj, + SoundResource.IC2_TOOLS_WRENCH, + 1.0F, + -1, + xCoord, + yCoord, + zCoord); + issueClientUpdate(); + } + sendCoverDataIfNeeded(); + return true; + } + } else { + if (GT_Utility.isStackInList(tCurrentItem, GregTech_API.sCrowbarList)) { + if (GT_ModHandler.damageOrDechargeItem(tCurrentItem, 1, 1000, aPlayer)) { + GT_Utility.sendSoundToPlayers( + worldObj, + SoundResource.RANDOM_BREAK, + 1.0F, + -1, + xCoord, + yCoord, + zCoord); + dropCover(coverSide, side, false); + } + sendCoverDataIfNeeded(); + return true; + } + } + } else if (aPlayer.isSneaking()) { // Sneak click, no tool -> open cover config if possible. + side = (getCoverIDAtSide(side) == 0) ? GT_Utility.determineWrenchingSide(side, aX, aY, aZ) : side; + return getCoverIDAtSide(side) > 0 && getCoverBehaviorAtSideNew(side).onCoverShiftRightClick( + side, + getCoverIDAtSide(side), + getComplexCoverDataAtSide(side), + this, + aPlayer); + } + + if (getCoverBehaviorAtSideNew(side).onCoverRightClick( + side, + getCoverIDAtSide(side), + getComplexCoverDataAtSide(side), + this, + aPlayer, + aX, + aY, + aZ)) return true; + + if (!getCoverInfoAtSide(side).isGUIClickable()) return false; + + if (aPlayer.getHeldItem() != null && aPlayer.getHeldItem() + .getItem() instanceof ItemBlock) { + return false; + } + + return openModularUi(aPlayer, side); + } + } + return false; + } + + public boolean hasGui(ForgeDirection side) { + return false; + } + + boolean openModularUi(EntityPlayer aPlayer, ForgeDirection side) { + if (!hasGui(side) || !isServerSide()) { + System.out.println("No GUI or Not Serverside"); + return false; + } + + GT_UIInfos.openGTTileEntityUI(this, aPlayer); + System.out.println("Trying to open a UI"); + return true; + } + + public boolean onWrenchRightClick(EntityPlayer aPlayer, ItemStack tCurrentItem, ForgeDirection wrenchSide, float aX, + float aY, float aZ, ItemStack aTool) { + if (setMainFacing(wrenchSide)) { + GT_ModHandler.damageOrDechargeItem(tCurrentItem, 1, 1000, aPlayer); + GT_Utility.sendSoundToPlayers(worldObj, SoundResource.IC2_TOOLS_WRENCH, 1.0F, -1, xCoord, yCoord, zCoord); + } + return onWrenchRightClick(aPlayer, tCurrentItem, wrenchSide, aX, aY, aZ); + } + + public boolean onScrewdriverRightClick(EntityPlayer aPlayer, ItemStack tCurrentItem, ForgeDirection wrenchSide, + float aX, float aY, float aZ, ItemStack aTool) { + if (GT_ModHandler.damageOrDechargeItem(tCurrentItem, 1, 200, aPlayer)) { + setCoverDataAtSide( + wrenchSide, + getCoverBehaviorAtSideNew(wrenchSide).onCoverScrewdriverClick( + wrenchSide, + getCoverIDAtSide(wrenchSide), + getComplexCoverDataAtSide(wrenchSide), + this, + aPlayer, + aX, + aY, + aZ)); + // TODO: Update connections! + GT_Utility.sendSoundToPlayers(worldObj, SoundResource.IC2_TOOLS_WRENCH, 1.0F, -1, xCoord, yCoord, zCoord); + } + return onScrewdriverRightClick(aPlayer, tCurrentItem, wrenchSide, aX, aY, aZ); + } + + public boolean onHammerRightClick(EntityPlayer aPlayer, ItemStack tCurrentItem, ForgeDirection wrenchSide, float aX, + float aY, float aZ, ItemStack aTool) { + + return onHammerRightClick(aPlayer, tCurrentItem, wrenchSide, aX, aY, aZ); + } + + public boolean onMalletRightClick(EntityPlayer aPlayer, ItemStack tCurrentItem, ForgeDirection wrenchSide, float aX, + float aY, float aZ, ItemStack aTool) { + + return onMalletRightClick(aPlayer, tCurrentItem, wrenchSide, aX, aY, aZ); + } + + public boolean onSolderingRightClick(EntityPlayer aPlayer, ItemStack tCurrentItem, ForgeDirection wrenchSide, + float aX, float aY, float aZ, ItemStack aTool) { + + return onSolderingRightClick(aPlayer, tCurrentItem, wrenchSide, aX, aY, aZ); + } + + public boolean onWireCutterRightClick(EntityPlayer aPlayer, ItemStack tCurrentItem, ForgeDirection wrenchSide, + float aX, float aY, float aZ, ItemStack aTool) { + + return onWireCutterRightClick(aPlayer, tCurrentItem, wrenchSide, aX, aY, aZ); + } + + @Deprecated + public boolean onHammerRightClick(EntityPlayer aPlayer, ItemStack tCurrentItem, ForgeDirection wrenchSide, float aX, + float aY, float aZ) { + return true; + } + + @Deprecated + public boolean onSolderingRightClick(EntityPlayer aPlayer, ItemStack tCurrentItem, ForgeDirection wrenchSide, + float aX, float aY, float aZ) { + return true; + } + + @Deprecated + public boolean onMalletRightClick(EntityPlayer aPlayer, ItemStack tCurrentItem, ForgeDirection wrenchSide, float aX, + float aY, float aZ) { + return true; + } + + @Deprecated + public boolean onWireCutterRightClick(EntityPlayer aPlayer, ItemStack tCurrentItem, ForgeDirection wrenchSide, + float aX, float aY, float aZ) { + return true; + } + + @Deprecated + public boolean onWrenchRightClick(EntityPlayer aPlayer, ItemStack tCurrentItem, ForgeDirection wrenchSide, float aX, + float aY, float aZ) { + return true; + } + + @Deprecated + public boolean onScrewdriverRightClick(EntityPlayer aPlayer, ItemStack tCurrentItem, ForgeDirection wrenchSide, + float aX, float aY, float aZ) { + return true; + } + + @Override + public float getExplosionResistance(Entity aExploder, double aExplosionX, double aExplosionY, double aExplosionZ) { + return getExplosionResistance(); + } + + @Override + public float getExplosionResistance() { + return 10.0F; + } + + @Override + public void onExploded(Explosion aExplosion) {} + + @Override + public boolean isSideSolid(ForgeDirection side) { + return true; + } + + @Override + public ArrayList<ItemStack> getDrops(int aFortune, boolean aSilkTouch) { + final ArrayList<ItemStack> rList = new ArrayList<>(); + final MultiTileEntityRegistry tRegistry = MultiTileEntityRegistry.getRegistry(getMultiTileEntityRegistryID()); + if (tRegistry != null) rList.add(tRegistry.getItem(getMultiTileEntityID(), writeItemNBT(new NBTTagCompound()))); + return rList; + } + + @Override + public boolean breakBlock() { + isDead = true; + onBaseTEDestroyed(); + return false; + } + + @Override + public boolean getSubItems(MultiTileEntityBlockInternal aBlock, Item aItem, CreativeTabs aTab, + List<ItemStack> aList, short aID) { + return true; + } + + @Override + public boolean recolourBlock(ForgeDirection side, byte aColor) { + // if (aColor > 15 || aColor < -1) aColor = -1; + // if(paint((byte) (aColor + 1))) { + //// updateClientData(); + //// causeBlockUpdate(); + // return true; + // } + // if (unpaint()) {updateClientData(); causeBlockUpdate(); return T;} + // mColor = (byte) (aColor + 1); + //// if (canAccessData()) mMetaTileEntity.onColorChangeServer(aColor); + return false; + } + + @Override + public boolean playerOwnsThis(EntityPlayer aPlayer, boolean aCheckPrecicely) { + if (aCheckPrecicely || privateAccess() || (ownerName.length() == 0)) + if ((ownerName.length() == 0) && isServerSide()) { + setOwnerName(aPlayer.getDisplayName()); + setOwnerUuid(aPlayer.getUniqueID()); + } else return !privateAccess() || aPlayer.getDisplayName() + .equals("Player") || ownerName.equals("Player") || ownerName.equals(aPlayer.getDisplayName()); + return true; + } + + @Override + public boolean privateAccess() { + return lockUpgrade; + } + + /** + * @return a Packet containing all Data which has to be synchronised to the Client - Override as needed + */ + public GT_Packet_MultiTileEntity getClientDataPacket() { + + final GT_Packet_MultiTileEntity packet = new GT_Packet_MultiTileEntity(false); + return packet; + } + + @Override + public void sendClientData(EntityPlayerMP aPlayer) { + if (worldObj == null || worldObj.isRemote) return; + final GT_Packet_New tPacket = getClientDataPacket(); + if (aPlayer == null) { + GT_Values.NW.sendPacketToAllPlayersInRange(worldObj, tPacket, getXCoord(), getZCoord()); + } else { + GT_Values.NW.sendToPlayer(tPacket, aPlayer); + } + sendCoverDataIfNeeded(); + } + + @Override + public boolean receiveClientData(int aEventID, int aValue) { + super.receiveClientEvent(aEventID, aValue); + if (isClientSide()) { + issueTextureUpdate(); + switch (aEventID) { + case GregTechTileClientEvents.CHANGE_COMMON_DATA: + facing = ForgeDirection.getOrientation(aValue & 7); + // mActive = ((aValue & 8) != 0); + mRedstone = ((aValue & 16) != 0); + // mLockUpgrade = ((aValue&32) != 0); + // mWorks = ((aValue & 64) != 0); + break; + case GregTechTileClientEvents.CHANGE_CUSTOM_DATA: + // Nothing here, currently + break; + case GregTechTileClientEvents.CHANGE_COLOR: + if (aValue > 16 || aValue < 0) aValue = 0; + color = (byte) aValue; + break; + case GregTechTileClientEvents.CHANGE_REDSTONE_OUTPUT: + mSidedRedstone[0] = (byte) ((aValue & 1) == 1 ? 15 : 0); + mSidedRedstone[1] = (byte) ((aValue & 2) == 2 ? 15 : 0); + mSidedRedstone[2] = (byte) ((aValue & 4) == 4 ? 15 : 0); + mSidedRedstone[3] = (byte) ((aValue & 8) == 8 ? 15 : 0); + mSidedRedstone[4] = (byte) ((aValue & 16) == 16 ? 15 : 0); + mSidedRedstone[5] = (byte) ((aValue & 32) == 32 ? 15 : 0); + break; + // case GregTechTileClientEvents.DO_SOUND: + // if (mTickTimer > 20) + // doSound((byte) aValue, xCoord + 0.5, yCoord + 0.5, zCoord + 0.5); + // break; + // case GregTechTileClientEvents.START_SOUND_LOOP: + // if (mTickTimer > 20) + // startSoundLoop((byte) aValue, xCoord + 0.5, yCoord + 0.5, zCoord + 0.5); + // break; + // case GregTechTileClientEvents.STOP_SOUND_LOOP: + // if (mTickTimer > 20) + // stopSoundLoop((byte) aValue, xCoord + 0.5, yCoord + 0.5, zCoord + 0.5); + // break; + // case GregTechTileClientEvents.CHANGE_LIGHT: + // mLightValue = (byte) aValue; + // break; + } + } + return true; + } + + @Override + public Packet getDescriptionPacket() { + issueClientUpdate(); + return null; + } + + @Override + public void getWailaBody(ItemStack itemStack, List<String> currentTip, IWailaDataAccessor accessor, + IWailaConfigHandler config) { + super.getWailaBody(itemStack, currentTip, accessor, config); + currentTip.add(String.format("Facing: %s", getFrontFacing().name())); + } + + @Override + public void getWailaNBTData(EntityPlayerMP player, TileEntity tile, NBTTagCompound tag, World world, int x, int y, + int z) { + super.getWailaNBTData(player, tile, tag, world, x, y, z); + } + + @Override + public ArrayList<String> getDebugInfo(EntityPlayer aPlayer, int aLogLevel) { + final ArrayList<String> tList = new ArrayList<>(); + if (aLogLevel > 2) { + tList.add( + "MultiTileRegistry-ID: " + EnumChatFormatting.BLUE + + mteRegistry + + EnumChatFormatting.RESET + + " MultiTile-ID: " + + EnumChatFormatting.BLUE + + mteID + + EnumChatFormatting.RESET); + } + + addDebugInfo(aPlayer, aLogLevel, tList); + + return tList; + } + + protected void addDebugInfo(EntityPlayer aPlayer, int aLogLevel, ArrayList<String> tList) { + /* Do nothing */ + } + + /** + * Energy - Do nothing by Default + */ + @Override + public boolean isUniversalEnergyStored(long aEnergyAmount) { + return false; + } + + @Override + public long getUniversalEnergyStored() { + return 0; + } + + @Override + public long getUniversalEnergyCapacity() { + return 0; + } + + @Override + public long getOutputAmperage() { + return 0; + } + + @Override + public long getOutputVoltage() { + return 0; + } + + @Override + public long getInputAmperage() { + return 0; + } + + @Override + public long getInputVoltage() { + return 0; + } + + @Override + public boolean decreaseStoredEnergyUnits(long energy, boolean ignoreTooLittleEnergy) { + return false; + } + + @Override + public boolean increaseStoredEnergyUnits(long energy, boolean ignoreTooMuchEnergy) { + return false; + } + + @Override + public boolean drainEnergyUnits(ForgeDirection side, long aVoltage, long aAmperage) { + return false; + } + + @Override + public long getAverageElectricInput() { + return 0; + } + + @Override + public long getAverageElectricOutput() { + return 0; + } + + @Override + public long getStoredEU() { + return 0; + } + + @Override + public long getEUCapacity() { + return 0; + } + + @Override + public long injectEnergyUnits(ForgeDirection side, long aVoltage, long aAmperage) { + return 0; + } + + @Override + public boolean inputEnergyFrom(ForgeDirection side) { + return false; + } + + @Override + public boolean outputsEnergyTo(ForgeDirection side) { + return false; + } + + /** + * Inventory - Do nothing by default + */ + + @Override + public boolean hasInventoryBeenModified() { + return false; + } + + @Override + public boolean isValidSlot(int aIndex) { + return false; + } + + @Override + public boolean addStackToSlot(int aIndex, ItemStack aStack) { + return false; + } + + @Override + public boolean addStackToSlot(int aIndex, ItemStack aStack, int aAmount) { + return false; + } + + @Override + public void markInventoryBeenModified() { + hasInventoryChanged = true; + } + + /* + * Cover Helpers + */ + + public boolean coverLetsFluidIn(ForgeDirection side, Fluid aFluid) { + return getCoverInfoAtSide(side).letsFluidIn(aFluid); + } + + public boolean coverLetsFluidOut(ForgeDirection side, Fluid aFluid) { + return getCoverInfoAtSide(side).letsFluidOut(aFluid); + } + + public boolean coverLetsEnergyIn(ForgeDirection side) { + return getCoverInfoAtSide(side).letsEnergyIn(); + } + + public boolean coverLetsEnergyOut(ForgeDirection side) { + return getCoverInfoAtSide(side).letsEnergyOut(); + } + + public boolean coverLetsItemsIn(ForgeDirection side, int aSlot) { + return getCoverInfoAtSide(side).letsItemsIn(aSlot); + } + + public boolean coverLetsItemsOut(ForgeDirection side, int aSlot) { + return getCoverInfoAtSide(side).letsItemsOut(aSlot); + } + + @Override + public ItemStack getStackForm(long aAmount) { + return new ItemStack(Item.getItemById(getMultiTileEntityRegistryID()), (int) aAmount, getMultiTileEntityID()); + } + + protected enum SidedTextureNames { + + Base("base"), + Left("left"), + Right("right"), + Top("top"), + Bottom("bottom"), + Back("back"), + Front("front"); + + private final String name; + public static final SidedTextureNames[] TEXTURES = { Base, Left, Right, Top, Bottom, Back, Front }; + + SidedTextureNames(String name) { + this.name = name; + } + + public String getName() { + return name; + } + } + + protected enum StatusTextures { + + Active("active", false), + ActiveWithGlow("active_glow", true), + Inactive("inactive", false), + InactiveWithGlow("inactive_glow", true); + + private final String name; + private final boolean hasGlow; + public static final StatusTextures[] TEXTURES = { Active, ActiveWithGlow, Inactive, InactiveWithGlow }; + + StatusTextures(String name, boolean hasGlow) { + this.name = name; + this.hasGlow = hasGlow; + } + + public String getName() { + return name; + } + + public boolean hasGlow() { + return hasGlow; + } + } + + @Override + public void getFullPacketData(GT_Packet_MultiTileEntity packet) { + packet.addData(new CoordinateData(getCoords())); + packet.addData(new CommonData(mStrongRedstone, color, (byte) 0)); + packet.addData(new MultiTileEntityData(mteRegistry, mteID)); + } + + @Override + public void getGraphicPacketData(GT_Packet_MultiTileEntity packet) { + packet.addData(new CoordinateData(getCoords())); + packet.addData(new MultiTileEntityData(mteRegistry, mteID)); + } + + @Override + public void getTimedPacketData(GT_Packet_MultiTileEntity packet) { + packet.addData(new CoordinateData(getCoords())); + packet.addData(new MultiTileEntityData(mteRegistry, mteID)); + } + + @Override + public void sendFullPacket(@Nonnull EntityPlayerMP player) { + fullPacket.clearData(); + getFullPacketData(fullPacket); + GT_Values.NW.sendToPlayer(fullPacket, player); + } + + @Override + public void sendGraphicPacket() { + graphicPacket.clearData(); + getGraphicPacketData(graphicPacket); + GT_Values.NW.sendPacketToAllPlayersInRange(worldObj, graphicPacket, getXCoord(), getZCoord()); + } + + @Override + public void sendTimedPacket() { + timedPacket.clearData(); + getTimedPacketData(timedPacket); + GT_Values.NW.sendPacketToAllPlayersInRange(worldObj, timedPacket, getXCoord(), getZCoord()); + } +} diff --git a/src/main/java/gregtech/api/multitileentity/base/NonTickableMultiTileEntity.java b/src/main/java/gregtech/api/multitileentity/base/NonTickableMultiTileEntity.java new file mode 100644 index 0000000000..2837a88180 --- /dev/null +++ b/src/main/java/gregtech/api/multitileentity/base/NonTickableMultiTileEntity.java @@ -0,0 +1,62 @@ +package gregtech.api.multitileentity.base; + +import static gregtech.api.enums.GT_Values.NW; + +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.network.Packet; +import net.minecraftforge.common.util.ForgeDirection; + +import gregtech.api.net.GT_Packet_SendCoverData; +import gregtech.api.util.ISerializableObject; +import gregtech.common.covers.CoverInfo; + +public abstract class NonTickableMultiTileEntity extends MultiTileEntity { + + boolean mConstructed = false; // Keeps track of whether this TE has been constructed and placed in the world + + public NonTickableMultiTileEntity() { + super(false); + } + + @Override + public void issueClientUpdate() { + if (worldObj != null && !worldObj.isRemote) { + sendClientData(null); + sendGraphicPacket(); + } + } + + @Override + public Packet getDescriptionPacket() { + // We should have a world object and have been constructed by this point + mConstructed = true; + + super.getDescriptionPacket(); + // We don't get ticked, so if we have any cover data that needs to be sent, send it now + sendCoverDataIfNeeded(); + return null; + } + + @Override + public void issueCoverUpdate(ForgeDirection side) { + if (!mConstructed) { + // Queue these up and send them with the description packet + super.issueCoverUpdate(side); + } else { + // Otherwise, send the data right away + final CoverInfo coverInfo = getCoverInfoAtSide(side); + NW.sendPacketToAllPlayersInRange(worldObj, new GT_Packet_SendCoverData(coverInfo, this), xCoord, zCoord); + + // Just in case + coverInfo.setNeedsUpdate(false); + } + } + + @Override + public void receiveCoverData(ForgeDirection coverSide, int aCoverID, ISerializableObject aCoverData, + EntityPlayerMP aPlayer) { + super.receiveCoverData(coverSide, aCoverID, aCoverData, aPlayer); + // We don't get ticked so issue the texture update right away + issueTextureUpdate(); + } +} diff --git a/src/main/java/gregtech/api/multitileentity/base/TickableMultiTileEntity.java b/src/main/java/gregtech/api/multitileentity/base/TickableMultiTileEntity.java new file mode 100644 index 0000000000..b25504dc6a --- /dev/null +++ b/src/main/java/gregtech/api/multitileentity/base/TickableMultiTileEntity.java @@ -0,0 +1,173 @@ +package gregtech.api.multitileentity.base; + +import static gregtech.GT_Mod.GT_FML_LOGGER; + +import java.util.HashMap; +import java.util.Map; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import net.minecraft.block.Block; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.world.World; +import net.minecraftforge.common.util.ForgeDirection; + +import gregtech.api.enums.GT_Values; +import gregtech.api.multitileentity.interfaces.IMultiTileEntity.IMTE_OnNeighborBlockChange; +import gregtech.api.task.TaskHost; +import gregtech.api.task.TickableTask; +import gregtech.api.util.GT_Log; +import gregtech.api.util.GT_Util; + +public abstract class TickableMultiTileEntity extends MultiTileEntity implements TaskHost, IMTE_OnNeighborBlockChange { + + /** Variable for seeing if the Tick Function is called right now. */ + public boolean isRunningTick = false; + /** Gets set to true when the Block received a Block Update. */ + public boolean blockUpdated = false; + /** Timer Value */ + protected long timer = 0; + /** Variable for updating Data to the Client */ + private boolean sendClientData = false; + + private final Map<String, TickableTask<?>> tasks = new HashMap<>(); + + public TickableMultiTileEntity() { + super(true); + } + + @Override + public final void registerTask(@Nonnull TickableTask<?> task) { + if (tasks.containsKey(task.getName())) { + throw new IllegalStateException(String.format("Task with name %s is already registered", task.getName())); + } + tasks.put(task.getName(), task); + } + + @Nullable + public TickableTask<?> getTask(@Nonnull String name) { + return tasks.get(name); + } + + @Override + public final void updateEntity() { + isRunningTick = true; + final boolean isServerSide = isServerSide(); + try { + if (timer++ == 0) { + markDirty(); + GT_Util.markChunkDirty(this); + onFirstTick(isServerSide); + } + if (isDead()) { + return; + } + onPreTick(timer, isServerSide); + super.updateEntity(); + if (!isServerSide && needsUpdate) { + worldObj.markBlockForUpdate(xCoord, yCoord, zCoord); + needsUpdate = false; + } + onTick(timer, isServerSide); + for (TickableTask<?> task : tasks.values()) { + task.update(timer, isServerSide); + } + if (isServerSide && timer > 2 && sendClientData) { + sendClientData(null); + } + onPostTick(timer, isServerSide); + + } catch (Throwable e) { + GT_FML_LOGGER.error("UpdateEntity Failed", e); + e.printStackTrace(GT_Log.err); + try { + onTickFailed(timer, isServerSide); + } catch (Throwable e2) { + GT_FML_LOGGER.error("UpdateEntity:onTickFailed Failed", e); + } + } + + isRunningTick = false; + } + + @Override + public void sendClientData(EntityPlayerMP aPlayer) { + if (sendClientData) { + // GT_FML_LOGGER.info("Sending client data"); + super.sendClientData(aPlayer); + sendClientData = false; + } + } + + /** + * The very first Tick happening to this TileEntity. + */ + public void onFirstTick(boolean isServerSide) { + if (isServerSide) { + checkDropCover(); + } else { + requestCoverDataIfNeeded(); + } + } + + /** + * The first part of the Tick, before block update. + */ + public void onPreTick(long tick, boolean isServerSide) {} + + /** + * The regular Tick. After block update, before sending data to client. + */ + public void onTick(long tick, boolean isServerSide) {} + + /** + * The absolute last part of the Tick, after sending data to client. + */ + public void onPostTick(long tick, boolean isServerSide) {} + + /** + * Gets called when there is an Exception/Error happening during one of the Tick methods. + */ + public void onTickFailed(long tick, boolean isServerSide) {} + + @Override + protected final void readTasksNBT(NBTTagCompound nbt) { + if (nbt.hasKey(GT_Values.NBT.TASKS)) { + NBTTagCompound tasksTag = nbt.getCompoundTag(GT_Values.NBT.TASKS); + for (TickableTask<?> task : tasks.values()) { + if (tasksTag.hasKey(task.getName())) { + task.readFromNBT(tasksTag.getCompoundTag(task.getName())); + } + } + } + } + + @Override + protected final void writeTasksNBT(NBTTagCompound aNBT) { + NBTTagCompound tasksTag = new NBTTagCompound(); + for (TickableTask<?> task : tasks.values()) { + NBTTagCompound tag = new NBTTagCompound(); + task.writeToNBT(tag); + tasksTag.setTag(task.getName(), tag); + } + aNBT.setTag(GT_Values.NBT.TASKS, tasksTag); + } + + @Override + public void onNeighborBlockChange(World aWorld, Block aBlock) { + blockUpdated = true; + } + + @Override + public void issueClientUpdate() { + sendClientData = true; + sendGraphicPacket(); + } + + @Override + public byte getComparatorValue(ForgeDirection side) { + return 0; + } +} diff --git a/src/main/java/gregtech/api/multitileentity/enums/GT_MultiTileCasing.java b/src/main/java/gregtech/api/multitileentity/enums/GT_MultiTileCasing.java new file mode 100644 index 0000000000..73bd55738a --- /dev/null +++ b/src/main/java/gregtech/api/multitileentity/enums/GT_MultiTileCasing.java @@ -0,0 +1,43 @@ +package gregtech.api.multitileentity.enums; + +import static gregtech.api.util.GT_StructureUtilityMuTE.createMuTEStructureCasing; +import static gregtech.loaders.preload.GT_Loader_MultiTileEntities.CASING_REGISTRY_NAME; + +import gregtech.api.enums.GT_Values; +import gregtech.api.util.GT_StructureUtilityMuTE; + +public enum GT_MultiTileCasing { + + CokeOven(0), + Chemical(1), + Distillation(2), + Macerator(18000), + LaserEngraver(4), + Mirror(5), + BlackLaserEngraverCasing(6), + LaserEngraverUpgrade1(7), + LaserEngraverUpgrade2(8), + LaserEngraverUpgrade3(9), + LaserEngraverUpgrade4(10), + NONE(GT_Values.W); + + private final int meta; + private final GT_StructureUtilityMuTE.MuTEStructureCasing casing; + + GT_MultiTileCasing(int meta) { + this.meta = meta; + casing = createMuTEStructureCasing(CASING_REGISTRY_NAME, meta); + } + + public int getId() { + return meta; + } + + public short getRegistryId() { + return (short) casing.getRegistryId(); + } + + public GT_StructureUtilityMuTE.MuTEStructureCasing getCasing() { + return casing; + } +} diff --git a/src/main/java/gregtech/api/multitileentity/enums/GT_MultiTileComponentCasing.java b/src/main/java/gregtech/api/multitileentity/enums/GT_MultiTileComponentCasing.java new file mode 100644 index 0000000000..e062ecc705 --- /dev/null +++ b/src/main/java/gregtech/api/multitileentity/enums/GT_MultiTileComponentCasing.java @@ -0,0 +1,130 @@ +package gregtech.api.multitileentity.enums; + +import gregtech.api.enums.GT_Values; + +public enum GT_MultiTileComponentCasing { + + LV_Motor(0), + MV_Motor(1), + HV_Motor(2), + EV_Motor(3), + IV_Motor(4), + LuV_Motor(5), + ZPM_Motor(6), + UV_Motor(7), + UHV_Motor(8), + UEV_Motor(9), + UIV_Motor(10), + UMV_Motor(11), + UXV_Motor(12), + MAX_Motor(13), + LV_Pump(14), + MV_Pump(15), + HV_Pump(16), + EV_Pump(17), + IV_Pump(18), + LuV_Pump(19), + ZPM_Pump(20), + UV_Pump(21), + UHV_Pump(22), + UEV_Pump(23), + UIV_Pump(24), + UMV_Pump(25), + UXV_Pump(26), + MAX_Pump(27), + LV_Conveyor(28), + MV_Conveyor(29), + HV_Conveyor(30), + EV_Conveyor(31), + IV_Conveyor(32), + LuV_Conveyor(33), + ZPM_Conveyor(34), + UV_Conveyor(35), + UHV_Conveyor(36), + UEV_Conveyor(37), + UIV_Conveyor(38), + UMV_Conveyor(39), + UXV_Conveyor(40), + MAX_Conveyor(41), + LV_Piston(42), + MV_Piston(43), + HV_Piston(44), + EV_Piston(45), + IV_Piston(46), + LuV_Piston(47), + ZPM_Piston(48), + UV_Piston(49), + UHV_Piston(50), + UEV_Piston(51), + UIV_Piston(52), + UMV_Piston(53), + UXV_Piston(54), + MAX_Piston(55), + LV_RobotArm(56), + MV_RobotArm(57), + HV_RobotArm(58), + EV_RobotArm(59), + IV_RobotArm(60), + LuV_RobotArm(61), + ZPM_RobotArm(62), + UV_RobotArm(63), + UHV_RobotArm(64), + UEV_RobotArm(65), + UIV_RobotArm(66), + UMV_RobotArm(67), + UXV_RobotArm(68), + MAX_RobotArm(69), + LV_Emitter(70), + MV_Emitter(71), + HV_Emitter(72), + EV_Emitter(73), + IV_Emitter(74), + LuV_Emitter(75), + ZPM_Emitter(76), + UV_Emitter(77), + UHV_Emitter(78), + UEV_Emitter(79), + UIV_Emitter(80), + UMV_Emitter(81), + UXV_Emitter(82), + MAX_Emitter(83), + LV_Sensor(84), + MV_Sensor(85), + HV_Sensor(86), + EV_Sensor(87), + IV_Sensor(88), + LuV_Sensor(89), + ZPM_Sensor(90), + UV_Sensor(91), + UHV_Sensor(92), + UEV_Sensor(93), + UIV_Sensor(94), + UMV_Sensor(95), + UXV_Sensor(96), + MAX_Sensor(97), + LV_FieldGenerator(98), + MV_FieldGenerator(99), + HV_FieldGenerator(100), + EV_FieldGenerator(101), + IV_FieldGenerator(102), + LuV_FieldGenerator(103), + ZPM_FieldGenerator(104), + UV_FieldGenerator(105), + UHV_FieldGenerator(106), + UEV_FieldGenerator(107), + UIV_FieldGenerator(108), + UMV_FieldGenerator(109), + UXV_FieldGenerator(110), + MAX_FieldGenerator(111), + NONE(GT_Values.W); + + private final int meta; + + GT_MultiTileComponentCasing(int meta) { + this.meta = meta; + } + + public int getId() { + return meta; + } +} diff --git a/src/main/java/gregtech/api/multitileentity/enums/GT_MultiTileMachine.java b/src/main/java/gregtech/api/multitileentity/enums/GT_MultiTileMachine.java new file mode 100644 index 0000000000..7cdde78986 --- /dev/null +++ b/src/main/java/gregtech/api/multitileentity/enums/GT_MultiTileMachine.java @@ -0,0 +1,19 @@ +package gregtech.api.multitileentity.enums; + +import gregtech.api.enums.GT_Values; + +public enum GT_MultiTileMachine { + + CokeOven(0), + NONE(GT_Values.W); + + private final int meta; + + GT_MultiTileMachine(int meta) { + this.meta = meta; + } + + public int getId() { + return meta; + } +} diff --git a/src/main/java/gregtech/api/multitileentity/enums/GT_MultiTileUpgradeCasing.java b/src/main/java/gregtech/api/multitileentity/enums/GT_MultiTileUpgradeCasing.java new file mode 100644 index 0000000000..296bae546d --- /dev/null +++ b/src/main/java/gregtech/api/multitileentity/enums/GT_MultiTileUpgradeCasing.java @@ -0,0 +1,71 @@ +package gregtech.api.multitileentity.enums; + +import gregtech.api.enums.GT_Values; + +public enum GT_MultiTileUpgradeCasing { + + ULV_Inventory(0), + LV_Inventory(1), + MV_Inventory(2), + HV_Inventory(3), + EV_Inventory(4), + IV_Inventory(5), + LuV_Inventory(6), + ZPM_Inventory(7), + UV_Inventory(8), + UHV_Inventory(9), + UEV_Inventory(10), + UIV_Inventory(11), + UXV_Inventory(12), + UMV_Inventory(13), + MAX_Inventory(14), + ULV_Tank(15), + LV_Tank(16), + MV_Tank(17), + HV_Tank(18), + EV_Tank(19), + IV_Tank(20), + LuV_Tank(21), + ZPM_Tank(22), + UV_Tank(23), + UHV_Tank(24), + UEV_Tank(25), + UIV_Tank(26), + UXV_Tank(27), + UMV_Tank(28), + MAX_Tank(29), + Amp_4(30), + Amp_16(31), + Amp_64(32), + Amp_256(33), + Amp_1_024(34), + Amp_4_096(35), + Amp_16_384(36), + Amp_65_536(37), + Amp_262_144(38), + Amp_1_048_576(39), + Laser(40), + Wireless(41), + Cleanroom(42), + Heater_Prototype(100), + Heater_IndustrialGrade(101), + Heater_NextGen(102), + Heater_Omnipotent(103), + Heater_OmegaType(104), + Insulator_Prototype(105), + Insulator_IndustrialGrade(106), + Insulator_NextGen(107), + Insulator_Omnipotent(108), + Insulator_OmegaType(109), + NONE(GT_Values.W); + + private final int meta; + + GT_MultiTileUpgradeCasing(int meta) { + this.meta = meta; + } + + public int getId() { + return meta; + } +} diff --git a/src/main/java/gregtech/api/multitileentity/enums/MultiTileCasingPurpose.java b/src/main/java/gregtech/api/multitileentity/enums/MultiTileCasingPurpose.java new file mode 100644 index 0000000000..2733da33cf --- /dev/null +++ b/src/main/java/gregtech/api/multitileentity/enums/MultiTileCasingPurpose.java @@ -0,0 +1,13 @@ +package gregtech.api.multitileentity.enums; + +/** + * Purposes with which a casing can registered itself in the MuTE controller to be ticked. + * Can be used for example to auto output recipe outputs from output casings. + * + * @author minecraft7771 + */ +public enum MultiTileCasingPurpose { + ItemOutput, + FluidOutput, + EnergyOutput, +} diff --git a/src/main/java/gregtech/api/multitileentity/interfaces/IItemUpdatable.java b/src/main/java/gregtech/api/multitileentity/interfaces/IItemUpdatable.java new file mode 100644 index 0000000000..89d281eb27 --- /dev/null +++ b/src/main/java/gregtech/api/multitileentity/interfaces/IItemUpdatable.java @@ -0,0 +1,19 @@ +package gregtech.api.multitileentity.interfaces; + +import net.minecraft.item.ItemStack; +import net.minecraft.world.World; + +public interface IItemUpdatable { + + /** + * Updates the Data of the ItemStack. Not called every tick but instead called whenever something important happens + * to the Stack. + */ + void updateItemStack(ItemStack aStack); + + /** + * Updates the Data of the ItemStack. Not called every tick but instead called whenever something important happens + * to the Stack. + */ + void updateItemStack(ItemStack aStack, World aWorld, int aX, int aY, int aZ); +} diff --git a/src/main/java/gregtech/api/multitileentity/interfaces/IMultiBlockController.java b/src/main/java/gregtech/api/multitileentity/interfaces/IMultiBlockController.java new file mode 100644 index 0000000000..58af918c50 --- /dev/null +++ b/src/main/java/gregtech/api/multitileentity/interfaces/IMultiBlockController.java @@ -0,0 +1,51 @@ +package gregtech.api.multitileentity.interfaces; + +import java.util.UUID; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import net.minecraft.util.ChunkCoordinates; +import net.minecraftforge.common.util.ForgeDirection; + +import gregtech.api.enums.InventoryType; +import gregtech.api.gui.GUIHost; +import gregtech.api.logic.FluidInventoryLogic; +import gregtech.api.logic.ItemInventoryLogic; +import gregtech.api.logic.interfaces.FluidInventoryLogicHost; +import gregtech.api.logic.interfaces.ItemInventoryLogicHost; +import gregtech.api.logic.interfaces.PowerLogicHost; +import gregtech.api.multitileentity.enums.MultiTileCasingPurpose; + +public interface IMultiBlockController + extends IMultiTileEntity, FluidInventoryLogicHost, ItemInventoryLogicHost, UpgradableMuTE, PowerLogicHost, GUIHost { + + boolean checkStructure(boolean aForceReset); + + /** Set the structure as having changed, and trigger an update */ + void onStructureChange(); + + @Override + ChunkCoordinates getCoords(); + + void registerCoveredPartOnSide(final ForgeDirection side, IMultiBlockPart part); + + void unregisterCoveredPartOnSide(final ForgeDirection side, IMultiBlockPart part); + + void registerCaseWithPurpose(MultiTileCasingPurpose purpose, IMultiBlockPart part); + + void unregisterCaseWithPurpose(MultiTileCasingPurpose purpose, IMultiBlockPart part); + + UUID registerItemInventory(int slots, int tier, @Nonnull InventoryType type, boolean isUpgradeInventory); + + ItemInventoryLogic unregisterItemInventory(@Nonnull UUID id, @Nonnull InventoryType type); + + void changeItemInventoryDisplayName(@Nonnull UUID id, @Nullable String displayName, @Nonnull InventoryType type); + + UUID registerFluidInventory(int tanks, long capacity, int tier, @Nonnull InventoryType type, + boolean isUpgradeInventory); + + FluidInventoryLogic unregisterFluidInventory(@Nonnull UUID id, @Nonnull InventoryType type); + + void changeFluidInventoryDisplayName(@Nonnull UUID id, @Nullable String displayName, @Nonnull InventoryType type); +} diff --git a/src/main/java/gregtech/api/multitileentity/interfaces/IMultiBlockEnergy.java b/src/main/java/gregtech/api/multitileentity/interfaces/IMultiBlockEnergy.java new file mode 100644 index 0000000000..d6d8bf5310 --- /dev/null +++ b/src/main/java/gregtech/api/multitileentity/interfaces/IMultiBlockEnergy.java @@ -0,0 +1,42 @@ +package gregtech.api.multitileentity.interfaces; + +import net.minecraftforge.common.util.ForgeDirection; + +import gregtech.api.multitileentity.multiblock.base.MultiBlockPart; + +interface IMultiBlockEnergy { + + boolean isUniversalEnergyStored(MultiBlockPart aPart, long aEnergyAmount); + + long getUniversalEnergyStored(MultiBlockPart aPart); + + long getUniversalEnergyCapacity(MultiBlockPart aPart); + + long getOutputAmperage(MultiBlockPart aPart); + + long getOutputVoltage(MultiBlockPart aPart); + + long getInputAmperage(MultiBlockPart aPart); + + long getInputVoltage(MultiBlockPart aPart); + + boolean decreaseStoredEnergyUnits(MultiBlockPart aPart, long aEnergy, boolean aIgnoreTooLittleEnergy); + + boolean increaseStoredEnergyUnits(MultiBlockPart aPart, long aEnergy, boolean aIgnoreTooMuchEnergy); + + boolean drainEnergyUnits(MultiBlockPart aPart, ForgeDirection side, long aVoltage, long aAmperage); + + long injectEnergyUnits(MultiBlockPart aPart, ForgeDirection side, long aVoltage, long aAmperage); + + long getAverageElectricInput(MultiBlockPart aPart); + + long getAverageElectricOutput(MultiBlockPart aPart); + + long getStoredEU(MultiBlockPart aPart); + + long getEUCapacity(MultiBlockPart aPart); + + boolean inputEnergyFrom(MultiBlockPart aPart, ForgeDirection side); + + boolean outputsEnergyTo(MultiBlockPart aPart, ForgeDirection side); +} diff --git a/src/main/java/gregtech/api/multitileentity/interfaces/IMultiBlockFluidHandler.java b/src/main/java/gregtech/api/multitileentity/interfaces/IMultiBlockFluidHandler.java new file mode 100644 index 0000000000..b513f51324 --- /dev/null +++ b/src/main/java/gregtech/api/multitileentity/interfaces/IMultiBlockFluidHandler.java @@ -0,0 +1,32 @@ +package gregtech.api.multitileentity.interfaces; + +import java.util.List; + +import net.minecraftforge.common.util.ForgeDirection; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.FluidTankInfo; +import net.minecraftforge.fluids.IFluidTank; + +import gregtech.api.multitileentity.multiblock.base.MultiBlockPart; + +public interface IMultiBlockFluidHandler { + + int fill(MultiBlockPart aPart, ForgeDirection aDirection, FluidStack aFluid, boolean aDoFill); + + FluidStack drain(MultiBlockPart aPart, ForgeDirection aDirection, FluidStack aFluid, boolean aDoDrain); + + FluidStack drain(MultiBlockPart aPart, ForgeDirection aDirection, int aAmountToDrain, boolean aDoDrain); + + boolean canFill(MultiBlockPart aPart, ForgeDirection aDirection, Fluid aFluid); + + boolean canDrain(MultiBlockPart aPart, ForgeDirection aDirection, Fluid aFluid); + + FluidTankInfo[] getTankInfo(MultiBlockPart aPart, ForgeDirection aDirection); + + IFluidTank[] getFluidTanksForGUI(MultiBlockPart aPart); + + List<String> getTankArrayNames(MultiBlockPart aPart); + + List<String> getTankArrayIDs(MultiBlockPart aPart); +} diff --git a/src/main/java/gregtech/api/multitileentity/interfaces/IMultiBlockInventory.java b/src/main/java/gregtech/api/multitileentity/interfaces/IMultiBlockInventory.java new file mode 100644 index 0000000000..5dc0fec196 --- /dev/null +++ b/src/main/java/gregtech/api/multitileentity/interfaces/IMultiBlockInventory.java @@ -0,0 +1,60 @@ +package gregtech.api.multitileentity.interfaces; + +import java.util.List; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraftforge.common.util.ForgeDirection; + +import com.gtnewhorizons.modularui.api.forge.IItemHandlerModifiable; + +import gregtech.api.multitileentity.multiblock.base.MultiBlockPart; + +public interface IMultiBlockInventory { + + boolean hasInventoryBeenModified(MultiBlockPart aPart); + + boolean isValidSlot(MultiBlockPart aPart, int aIndex); + + boolean addStackToSlot(MultiBlockPart aPart, int aIndex, ItemStack aStack); + + boolean addStackToSlot(MultiBlockPart aPart, int aIndex, ItemStack aStack, int aAmount); + + int[] getAccessibleSlotsFromSide(MultiBlockPart aPart, ForgeDirection side); + + boolean canInsertItem(MultiBlockPart aPart, int aSlot, ItemStack aStack, ForgeDirection side); + + boolean canExtractItem(MultiBlockPart aPart, int aSlot, ItemStack aStack, ForgeDirection side); + + int getSizeInventory(MultiBlockPart aPart); + + ItemStack getStackInSlot(MultiBlockPart aPart, int aSlot); + + ItemStack decrStackSize(MultiBlockPart aPart, int aSlot, int aDecrement); + + ItemStack getStackInSlotOnClosing(MultiBlockPart aPart, int aSlot); + + void setInventorySlotContents(MultiBlockPart aPart, int aSlot, ItemStack aStack); + + String getInventoryName(MultiBlockPart aPart); + + boolean hasCustomInventoryName(MultiBlockPart aPart); + + int getInventoryStackLimit(MultiBlockPart aPart); + + void markDirty(MultiBlockPart aPart); + + boolean isUseableByPlayer(MultiBlockPart aPart, EntityPlayer aPlayer); + + void openInventory(MultiBlockPart aPart); + + void closeInventory(MultiBlockPart aPart); + + boolean isItemValidForSlot(MultiBlockPart aPart, int aSlot, ItemStack aStack); + + IItemHandlerModifiable getInventoryForGUI(MultiBlockPart aPart); + + List<String> getInventoryNames(MultiBlockPart aPart); + + List<String> getInventoryIDs(MultiBlockPart aPart); +} diff --git a/src/main/java/gregtech/api/multitileentity/interfaces/IMultiBlockPart.java b/src/main/java/gregtech/api/multitileentity/interfaces/IMultiBlockPart.java new file mode 100644 index 0000000000..59d838fdeb --- /dev/null +++ b/src/main/java/gregtech/api/multitileentity/interfaces/IMultiBlockPart.java @@ -0,0 +1,26 @@ +package gregtech.api.multitileentity.interfaces; + +import java.util.UUID; + +import net.minecraft.util.ChunkCoordinates; +import net.minecraftforge.common.util.ForgeDirection; + +import gregtech.api.logic.interfaces.FluidInventoryLogicHost; +import gregtech.api.logic.interfaces.ItemInventoryLogicHost; + +public interface IMultiBlockPart extends IMultiTileEntity, ItemInventoryLogicHost, FluidInventoryLogicHost { + + ChunkCoordinates getTargetPos(); + + void setTargetPos(ChunkCoordinates aTargetPos); + + void setLockedInventoryIndex(int aIndex); + + int getLockedInventoryIndex(); + + UUID getLockedInventory(); + + boolean tickCoverAtSide(ForgeDirection side, long aTickTimer); + + boolean shouldTick(long tickTimer); +} diff --git a/src/main/java/gregtech/api/multitileentity/interfaces/IMultiTileEntity.java b/src/main/java/gregtech/api/multitileentity/interfaces/IMultiTileEntity.java new file mode 100644 index 0000000000..91803690fc --- /dev/null +++ b/src/main/java/gregtech/api/multitileentity/interfaces/IMultiTileEntity.java @@ -0,0 +1,293 @@ +package gregtech.api.multitileentity.interfaces; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +import net.minecraft.block.Block; +import net.minecraft.creativetab.CreativeTabs; +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.AxisAlignedBB; +import net.minecraft.util.MovingObjectPosition; +import net.minecraft.world.Explosion; +import net.minecraft.world.World; +import net.minecraftforge.common.util.ForgeDirection; + +import cpw.mods.fml.common.Optional; +import gregtech.api.enums.Mods; +import gregtech.api.interfaces.tileentity.ICoverable; +import gregtech.api.interfaces.tileentity.IDebugableTileEntity; +import gregtech.api.interfaces.tileentity.ITurnable; +import gregtech.api.multitileentity.MultiTileEntityBlockInternal; +import gregtech.api.multitileentity.MultiTileEntityItemInternal; +import gregtech.api.multitileentity.MultiTileEntityRegistry; + +/* + * Heavily inspired by GT6 + */ +public interface IMultiTileEntity extends ICoverable, ITurnable, IDebugableTileEntity { + + /** + * Those two IDs HAVE to be saved inside the NBT of the TileEntity itself. They get set by the Registry itself, when + * the TileEntity is placed. + */ + short getMultiTileEntityID(); + + short getMultiTileEntityRegistryID(); + + /** + * Called by the Registry with the default NBT Parameters and the two IDs you have to save, when the TileEntity is + * created. aNBT may be null, take that into account if you decide to call the regular readFromNBT Function from + * here. + */ + void initFromNBT(NBTTagCompound aNBT, short aMTEID, short aMTERegistry); + + /** Writes Item Data to the NBT. */ + NBTTagCompound writeItemNBT(NBTTagCompound aNBT); + + /** Sets the Item Display Name. Use null to reset it. */ + void setCustomName(String aName); + + String getCustomName(); + + /** return the internal Name of this TileEntity to be registered. */ + String getTileEntityName(); + + /** + * Called when a TileEntity of this particular Class is being registered first at any MultiTileEntity Registry. So + * basically one call per Class. + */ + void onRegistrationFirst(MultiTileEntityRegistry aRegistry, short aID); + + /** Called after the TileEntity has been placed and set up. */ + void onTileEntityPlaced(); + + /** Checks if the TileEntity is Invalid or Unloaded, should bes required for every TileEntity. */ + @Override + boolean isDead(); + + void loadTextures(String folder); + + void copyTextures(); + + void issueClientUpdate(); + + void sendClientData(EntityPlayerMP aPlayer); + + boolean receiveClientData(int aEventID, int aValue); + + void setShouldRefresh(boolean aShouldRefresh); + + void addCollisionBoxesToList(AxisAlignedBB aAABB, List<AxisAlignedBB> aList, Entity aEntity); + + AxisAlignedBB getCollisionBoundingBoxFromPool(); + + AxisAlignedBB getSelectedBoundingBoxFromPool(); + + void setBlockBoundsBasedOnState(Block aBlock); + + void onBlockAdded(); + + boolean playerOwnsThis(EntityPlayer aPlayer, boolean aCheckPrecicely); + + boolean privateAccess(); + + /** @return the amount of Time this TileEntity has been loaded. */ + @Override + long getTimer(); + + /** Sets the Owner of the Machine. Returns the set Name. */ + String setOwnerName(String aName); + + /** gets the Name of the Machines Owner or "Player" if not set. */ + String getOwnerName(); + + /** Gets the UniqueID of the Machines Owner. */ + UUID getOwnerUuid(); + + /** Sets the UniqueID of the Machines Owner. */ + void setOwnerUuid(UUID uuid); + + /** + * Causes a general Texture update. Only used Client Side to mark Blocks dirty. + */ + void issueTextureUpdate(); + + /** + * Paintable Support + */ + boolean unpaint(); + + boolean isPainted(); + + boolean paint(int aRGB); + + int getPaint(); + + /** + * Sets the main facing to {side} and update as appropriately + * + * @return Whether the facing was changed + */ + boolean setMainFacing(ForgeDirection side); + + boolean isFacingValid(ForgeDirection facing); + + void onFacingChange(); + + @Override + default void setFrontFacing(ForgeDirection side) { + setMainFacing(side); + } + + boolean shouldTriggerBlockUpdate(); + + void onMachineBlockUpdate(); + + boolean allowInteraction(Entity aEntity); + + default void onLeftClick(EntityPlayer aPlayer) { + /* do nothing */ + } + + boolean onBlockActivated(EntityPlayer aPlayer, ForgeDirection side, float aX, float aY, float aZ); + + boolean onRightClick(EntityPlayer aPlayer, ForgeDirection side, float aX, float aY, float aZ); + + ArrayList<ItemStack> getDrops(int aFortune, boolean aSilkTouch); + + boolean isSideSolid(ForgeDirection side); + + float getExplosionResistance(Entity aExploder, double aExplosionX, double aExplosionY, double aExplosionZ); + + float getExplosionResistance(); + + void onExploded(Explosion aExplosion); + + boolean recolourBlock(ForgeDirection side, byte aColor); + + /** Adds to the Creative Tab. return false to prevent it from being added. */ + boolean getSubItems(MultiTileEntityBlockInternal aBlock, Item aItem, CreativeTabs aTab, List<ItemStack> aList, + short aID); + + ItemStack getPickBlock(MovingObjectPosition aTarget); + + boolean shouldSideBeRendered(ForgeDirection side); + + boolean isSurfaceOpaque(ForgeDirection side); + + boolean onPlaced(ItemStack aStack, EntityPlayer aPlayer, World aWorld, int aX, int aY, int aZ, ForgeDirection side, + float aHitX, float aHitY, float aHitZ); + + // ItemStack getPickBlock(MovingObjectPosition aTarget); + + /* + * Various Sub Interfaces from GT6 + */ + + interface IMTE_OnNeighborBlockChange extends IMultiTileEntity { + + void onNeighborBlockChange(World aWorld, Block aBlock); + } + + interface IMTE_IsProvidingWeakPower extends IMultiTileEntity { + + /** Remember that it passes the opposite Side due to the way vanilla works! */ + int isProvidingWeakPower(ForgeDirection oppositeSide); + } + + interface IMTE_IsProvidingStrongPower extends IMultiTileEntity { + + /** Remember that it passes the opposite Side due to the way vanilla works! */ + int isProvidingStrongPower(ForgeDirection oppositeSide); + } + + interface IMTE_ShouldCheckWeakPower extends IMultiTileEntity { + + boolean shouldCheckWeakPower(ForgeDirection side); + } + + interface IMTE_GetWeakChanges extends IMultiTileEntity { + + boolean getWeakChanges(); + } + + interface IMTE_GetComparatorInputOverride extends IMultiTileEntity { + + int getComparatorInputOverride(ForgeDirection side); + } + + interface IMTE_BreakBlock extends IMultiTileEntity { + + /** return true to prevent the TileEntity from being removed. */ + boolean breakBlock(); + } + + interface IMTE_HasMultiBlockMachineRelevantData extends IMultiTileEntity { + + /** Return true to mark this Block as a Machine Block for Multiblocks. (Triggers machine update thread) */ + boolean hasMultiBlockMachineRelevantData(); + } + + interface IMTE_GetBlockHardness extends IMultiTileEntity { + + float getBlockHardness(); + } + + interface IMTE_GetFoodValues extends IMultiTileEntity { + + @Optional.Method(modid = Mods.Names.APPLE_CORE) + squeek.applecore.api.food.FoodValues getFoodValues(MultiTileEntityItemInternal aItem, ItemStack aStack); + } + + interface IMTE_OnlyPlaceableWhenSneaking extends IMultiTileEntity { + + /** Return true to prevent placing this Block without Sneaking. */ + boolean onlyPlaceableWhenSneaking(); + } + + interface IMTE_IgnoreEntityCollisionWhenPlacing extends IMultiTileEntity { + + /** + * Return true to ignore the Player standing in the way of placing this Block; useful for things like + * pipes/wires. + */ + boolean ignoreEntityCollisionWhenPlacing(ItemStack aStack, EntityPlayer aPlayer, World aWorld, int aX, int aY, + int aZ, ForgeDirection side, float aHitX, float aHitY, float aHitZ); + } + + interface IMTE_CanPlace extends IMultiTileEntity { + + /** Return false if this TileEntity cannot be placed at that Location. */ + boolean canPlace(ItemStack aStack, EntityPlayer aPlayer, World aWorld, int aX, int aY, int aZ, + ForgeDirection side, float aHitX, float aHitY, float aHitZ); + } + + interface IMTE_GetMaxStackSize extends IMultiTileEntity { + + /** Gets the Max Stacksize of this Item. */ + byte getMaxStackSize(ItemStack aStack, byte aDefault); + } + + interface IMTE_AddToolTips extends IMultiTileEntity { + + /** Adds ToolTips to the Item. */ + void addToolTips(List<String> aList, ItemStack aStack, boolean aF3_H); + } + + interface IMTE_HasModes extends IMultiTileEntity { + + int getMode(); + + void setMode(int mode); + + int getAllowedModes(); + + void setAllowedModes(int allowedModes); + } +} diff --git a/src/main/java/gregtech/api/multitileentity/interfaces/IMultiTileMachine.java b/src/main/java/gregtech/api/multitileentity/interfaces/IMultiTileMachine.java new file mode 100644 index 0000000000..babb85d118 --- /dev/null +++ b/src/main/java/gregtech/api/multitileentity/interfaces/IMultiTileMachine.java @@ -0,0 +1,10 @@ +package gregtech.api.multitileentity.interfaces; + +public interface IMultiTileMachine { + + void setBooleans(int booleans); + + int getBooleans(); + + void setSound(byte soundEvent, int soundEventValue); +} diff --git a/src/main/java/gregtech/api/multitileentity/interfaces/SyncedMultiTileEntity.java b/src/main/java/gregtech/api/multitileentity/interfaces/SyncedMultiTileEntity.java new file mode 100644 index 0000000000..2045f28d67 --- /dev/null +++ b/src/main/java/gregtech/api/multitileentity/interfaces/SyncedMultiTileEntity.java @@ -0,0 +1,63 @@ +package gregtech.api.multitileentity.interfaces; + +import javax.annotation.Nonnull; + +import net.minecraft.entity.player.EntityPlayerMP; + +import gregtech.api.net.GT_Packet_MultiTileEntity; + +public interface SyncedMultiTileEntity { + + public static final int DEFAULT_TIMED_PACKET_PERIOD = 20; + + /** + * Will send a packet to the client when they open the controller or access a casing. + * Should only be sent to one player! + */ + void sendFullPacket(@Nonnull EntityPlayerMP player); + + /** + * Should always collect all the data that the controller or casing has and should send + * Called by {@link #sendFullPacket()} + * + * @param packet The packet which will be sent + */ + void getFullPacketData(GT_Packet_MultiTileEntity packet); + + /** + * Will send a packet at a certain period of time, defined by {@link #getTimedPacketPeriod()}, to all players around + * the controller or casing to send important information. + * Redstone state, color, ect. It shouldn't send data about the internals like inventory and processing time + */ + void sendTimedPacket(); + + /** + * Collects all the data that should be sent out at a certain period of time defined by + * {@link #getTimedPacketPeriod()} + * Called by {@link #sendTimedPacket()} + * + * @param packet The packet which will be sent + */ + void getTimedPacketData(GT_Packet_MultiTileEntity packet); + + /** + * Defines the period of time at which a timed packet should be sent out. Default 20 ticks + */ + default int getTimedPacketPeriod() { + return DEFAULT_TIMED_PACKET_PERIOD; + } + + /** + * Will send a packet, which should only contain data about how the TileEntity should be rendered. + * !!! Warning !!! This is sent every single tick! Do not put a lot of data here! + */ + void sendGraphicPacket(); + + /** + * Collects all the data that is needed to be send every single tick + * Called by {@link #sendGraphicPacket()} + * + * @param packet The packet which will be sent + */ + void getGraphicPacketData(GT_Packet_MultiTileEntity packet); +} diff --git a/src/main/java/gregtech/api/multitileentity/interfaces/UpgradableModularMuTE.java b/src/main/java/gregtech/api/multitileentity/interfaces/UpgradableModularMuTE.java new file mode 100644 index 0000000000..3b4c3cb6f3 --- /dev/null +++ b/src/main/java/gregtech/api/multitileentity/interfaces/UpgradableModularMuTE.java @@ -0,0 +1,10 @@ +package gregtech.api.multitileentity.interfaces; + +import gregtech.api.util.GT_StructureUtilityMuTE.UpgradeCasings; + +public interface UpgradableModularMuTE extends UpgradableMuTE { + + void increaseMucCount(UpgradeCasings casingType, int tier); + + void resetMucCount(); +} diff --git a/src/main/java/gregtech/api/multitileentity/interfaces/UpgradableMuTE.java b/src/main/java/gregtech/api/multitileentity/interfaces/UpgradableMuTE.java new file mode 100644 index 0000000000..c18852c95e --- /dev/null +++ b/src/main/java/gregtech/api/multitileentity/interfaces/UpgradableMuTE.java @@ -0,0 +1,12 @@ +package gregtech.api.multitileentity.interfaces; + +public interface UpgradableMuTE { + + void setCleanroom(boolean isCleanroom); + + void setWirelessSupport(boolean canUse); + + void setLaserSupport(boolean canUse); + + void setMaxAmperage(long amperage); +} diff --git a/src/main/java/gregtech/api/multitileentity/machine/MultiTileBasicMachine.java b/src/main/java/gregtech/api/multitileentity/machine/MultiTileBasicMachine.java new file mode 100644 index 0000000000..4b348c2fec --- /dev/null +++ b/src/main/java/gregtech/api/multitileentity/machine/MultiTileBasicMachine.java @@ -0,0 +1,800 @@ +package gregtech.api.multitileentity.machine; + +import static gregtech.api.enums.GT_Values.*; +import static gregtech.api.enums.TickTime.MINUTE; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Objects; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import net.minecraft.client.Minecraft; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.tileentity.TileEntityFurnace; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.StatCollector; +import net.minecraftforge.common.util.ForgeDirection; +import net.minecraftforge.fluids.FluidStack; + +import org.jetbrains.annotations.ApiStatus.OverrideOnly; + +import com.gtnewhorizons.modularui.api.forge.IItemHandlerModifiable; +import com.gtnewhorizons.modularui.api.forge.ItemStackHandler; +import com.gtnewhorizons.modularui.api.screen.UIBuildContext; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import gregtech.api.enums.GT_Values; +import gregtech.api.enums.GT_Values.NBT; +import gregtech.api.enums.InventoryType; +import gregtech.api.enums.Mods; +import gregtech.api.enums.SoundResource; +import gregtech.api.enums.Textures; +import gregtech.api.enums.Textures.BlockIcons.CustomIcon; +import gregtech.api.enums.TickTime; +import gregtech.api.enums.VoidingMode; +import gregtech.api.gui.GUIHost; +import gregtech.api.gui.GUIProvider; +import gregtech.api.interfaces.ITexture; +import gregtech.api.logic.FluidInventoryLogic; +import gregtech.api.logic.ItemInventoryLogic; +import gregtech.api.logic.MuTEProcessingLogic; +import gregtech.api.logic.NullPowerLogic; +import gregtech.api.logic.PowerLogic; +import gregtech.api.logic.interfaces.PowerLogicHost; +import gregtech.api.logic.interfaces.ProcessingLogicHost; +import gregtech.api.metatileentity.GregTechTileClientEvents; +import gregtech.api.multitileentity.MultiTileEntityRegistry; +import gregtech.api.multitileentity.base.TickableMultiTileEntity; +import gregtech.api.multitileentity.interfaces.IMultiTileMachine; +import gregtech.api.render.TextureFactory; +import gregtech.api.task.tasks.ProcessingTask; +import gregtech.api.util.GT_Utility; +import gregtech.client.GT_SoundLoop; +import gregtech.common.gui.MachineGUIProvider; + +public abstract class MultiTileBasicMachine<P extends MuTEProcessingLogic<P>> extends TickableMultiTileEntity + implements IMultiTileMachine, ProcessingLogicHost<P>, PowerLogicHost, GUIHost { + + protected static final int ACTIVE = B[0]; + protected static final int TICKS_BETWEEN_RECIPE_CHECKS = 5 * TickTime.SECOND; + protected static final int POLLUTION_TICK = TickTime.SECOND; + protected static final byte INTERRUPT_SOUND_INDEX = 8; + protected static final byte PROCESS_START_SOUND_INDEX = 1; + + protected static final IItemHandlerModifiable EMPTY_INVENTORY = new ItemStackHandler(0); + + public ITexture activeOverlayTexture = null; + public ITexture activeOverlayGlowTexture = null; + public ITexture inactiveOverlayTexture = null; + public ITexture inactiveOverlayGlowTexture = null; + + protected int maxParallel = 1; + protected boolean active = false; + protected int tier = 0; + protected long burnTime = 0; + protected long totalBurnTime = 0; + + protected boolean outputInventoryChanged = false; + protected boolean powerShutDown = false; + protected boolean wasEnabled = false; + protected boolean canWork = true; + protected boolean isElectric = true; + protected boolean isSteam = false; + protected boolean acceptsFuel = false; + + protected byte soundEvent = 0; + protected int soundEventValue = 0; + protected ItemInventoryLogic itemInput; + protected ItemInventoryLogic itemOutput; + protected FluidInventoryLogic fluidInput; + protected FluidInventoryLogic fluidOutput; + + protected P processingLogic; + @Nonnull + protected VoidingMode voidingMode = VoidingMode.VOID_NONE; + protected boolean processingUpdate = false; + @Nonnull + protected PowerLogic power = createPowerLogic(); + @Nonnull + protected GUIProvider<?> guiProvider = createGUIProvider(); + + @SideOnly(Side.CLIENT) + protected GT_SoundLoop activitySoundLoop; + + public MultiTileBasicMachine() { + new ProcessingTask<>(this); + } + + @Override + public String getTileEntityName() { + return "gt.multitileentity.machine.basic"; + } + + @Override + public void writeMultiTileNBT(NBTTagCompound nbt) { + super.writeMultiTileNBT(nbt); + if (maxParallel > 0) { + nbt.setInteger(NBT.PARALLEL, maxParallel); + } + + if (active) { + nbt.setBoolean(NBT.ACTIVE, active); + } + + saveItemLogic(nbt); + saveFluidLogic(nbt); + + if (processingLogic != null) { + NBTTagCompound processingLogicNBT = processingLogic.saveToNBT(); + nbt.setTag("processingLogic", processingLogicNBT); + } + + nbt.setInteger(NBT.TIER, tier); + nbt.setLong(NBT.BURN_TIME_LEFT, burnTime); + nbt.setLong(NBT.TOTAL_BURN_TIME, totalBurnTime); + nbt.setBoolean(NBT.ALLOWED_WORK, canWork); + nbt.setBoolean(NBT.ACTIVE, active); + power.saveToNBT(nbt); + } + + protected void saveItemLogic(NBTTagCompound nbt) { + NBTTagCompound nbtListInput = itemInput.saveToNBT(); + nbt.setTag(NBT.INV_INPUT_LIST, nbtListInput); + NBTTagCompound nbtListOutput = itemOutput.saveToNBT(); + nbt.setTag(NBT.INV_OUTPUT_LIST, nbtListOutput); + } + + protected void saveFluidLogic(NBTTagCompound nbt) { + NBTTagCompound fluidInputNBT = fluidInput.saveToNBT(); + nbt.setTag(NBT.TANK_IN, fluidInputNBT); + NBTTagCompound fluidOutputNBT = fluidOutput.saveToNBT(); + nbt.setTag(NBT.TANK_OUT, fluidOutputNBT); + } + + @Override + public void readMultiTileNBT(NBTTagCompound nbt) { + super.readMultiTileNBT(nbt); + if (nbt.hasKey(NBT.PARALLEL)) { + maxParallel = Math.max(1, nbt.getInteger(NBT.PARALLEL)); + } + + if (nbt.hasKey(NBT.ACTIVE)) { + active = nbt.getBoolean(NBT.ACTIVE); + } + + loadItemLogic(nbt); + loadFluidLogic(nbt); + + if (nbt.hasKey("processingLogic")) { + P processingLogic = getProcessingLogic(); + processingLogic.loadFromNBT(Objects.requireNonNull(nbt.getCompoundTag("processingLogic"))); + } + + tier = nbt.getInteger(NBT.TIER); + burnTime = nbt.getLong(NBT.BURN_TIME_LEFT); + totalBurnTime = nbt.getLong(NBT.TOTAL_BURN_TIME); + canWork = nbt.getBoolean(NBT.ALLOWED_WORK); + active = nbt.getBoolean(NBT.ACTIVE); + power.loadFromNBT(nbt); + } + + protected void loadItemLogic(NBTTagCompound nbt) { + itemInput = new ItemInventoryLogic(nbt.getInteger(NBT.INV_OUTPUT_SIZE), tier); + itemOutput = new ItemInventoryLogic(nbt.getInteger(NBT.INV_OUTPUT_SIZE), tier); + if (nbt.hasKey(NBT.INV_INPUT_LIST)) { + itemInput.loadFromNBT(nbt.getCompoundTag(NBT.INV_INPUT_LIST)); + } + if (nbt.hasKey(NBT.INV_OUTPUT_LIST)) { + itemOutput.loadFromNBT(nbt.getCompoundTag(NBT.INV_OUTPUT_LIST)); + } + } + + protected void loadFluidLogic(NBTTagCompound nbt) { + fluidInput = new FluidInventoryLogic(16, 10000, tier); + fluidOutput = new FluidInventoryLogic(16, 10000, tier); + fluidInput.loadFromNBT(nbt.getCompoundTag(NBT.TANK_IN)); + fluidOutput.loadFromNBT(nbt.getCompoundTag(NBT.TANK_OUT)); + } + + public boolean checkTexture(String modID, String resourcePath) { + try { + Minecraft.getMinecraft() + .getResourceManager() + .getResource(new ResourceLocation(modID, resourcePath)); + return true; + } catch (IOException ignored) { + return false; + } + } + + @Override + public void loadTextures(String folder) { + super.loadTextures(folder); + for (StatusTextures textureName : StatusTextures.TEXTURES) { + ITexture texture = null; + String texturePath = "textures/blocks/multitileentity/" + folder + "/" + textureName.getName() + ".png"; + if (!checkTexture(Mods.GregTech.ID, texturePath)) { + texture = TextureFactory.of(Textures.BlockIcons.VOID); + } else { + if (textureName.hasGlow()) { + texture = TextureFactory.builder() + .addIcon(new CustomIcon("multitileentity/" + folder + "/" + textureName.getName())) + .glow() + .build(); + } else { + texture = TextureFactory + .of(new CustomIcon("multitileentity/" + folder + "/" + textureName.getName())); + } + } + switch (textureName) { + case Active -> activeOverlayTexture = texture; + case ActiveWithGlow -> activeOverlayGlowTexture = texture; + case Inactive -> inactiveOverlayTexture = texture; + case InactiveWithGlow -> inactiveOverlayGlowTexture = texture; + } + } + } + + @Override + public void copyTextures() { + super.copyTextures(); + final TileEntity tCanonicalTileEntity = MultiTileEntityRegistry + .getCanonicalTileEntity(getMultiTileEntityRegistryID(), getMultiTileEntityID()); + if (!(tCanonicalTileEntity instanceof MultiTileBasicMachine)) { + return; + } + final MultiTileBasicMachine canonicalEntity = (MultiTileBasicMachine) tCanonicalTileEntity; + activeOverlayTexture = canonicalEntity.activeOverlayTexture; + activeOverlayGlowTexture = canonicalEntity.activeOverlayGlowTexture; + inactiveOverlayTexture = canonicalEntity.inactiveOverlayTexture; + inactiveOverlayGlowTexture = canonicalEntity.inactiveOverlayGlowTexture; + } + + @Override + public ITexture getTexture(ForgeDirection side) { + final ITexture texture = super.getTexture(side); + if (side == facing) { + if (isActive()) { + return TextureFactory.of(texture, activeOverlayTexture, activeOverlayGlowTexture); + } + + return TextureFactory.of(texture, inactiveOverlayTexture, inactiveOverlayGlowTexture); + } + + return TextureFactory.of(texture, getCoverTexture(side)); + } + /* + * Fluids + */ + + /** + * The number of fluid (input) slots available for this machine + */ + public int getFluidInputCount() { + return 7; + } + + /** + * The number of fluid (output) slots available for this machine + */ + public int getFluidOutputCount() { + return 3; + } + + @Override + public void setLightValue(byte aLightValue) {} + + /* + * Inventory + */ + + @Override + public boolean hasInventoryBeenModified() { + // True if the input inventory has changed + return hasInventoryChanged; + } + + public void markOutputInventoryBeenModified() { + outputInventoryChanged = true; + } + + public boolean hasOutputInventoryBeenModified() { + // True if the output inventory has changed + return outputInventoryChanged; + } + + public void markInputInventoryBeenModified() { + hasInventoryChanged = true; + } + + // #region Machine + + @Override + public void onPostTick(long tick, boolean isServerSide) { + if (isServerSide) { + runMachine(tick); + } else { + doActivitySound(getActivitySoundLoop()); + } + } + + /** + * Runs only on server side + * + * @param tick The current tick of the machine + */ + protected void runMachine(long tick) { + if (acceptsFuel() && isActive() && !consumeFuel()) { + stopMachine(true); + return; + } + + if (hasThingsToDo()) { + markDirty(); + runningTick(tick); + return; + } + + if (tick % TICKS_BETWEEN_RECIPE_CHECKS == 0 || hasWorkJustBeenEnabled() + || hasInventoryBeenModified() && isAllowedToWork()) { + wasEnabled = false; + if (checkRecipe()) { + setActive(true); + setSound(GregTechTileClientEvents.START_SOUND_LOOP, PROCESS_START_SOUND_INDEX); + updateSlots(); + markDirty(); + issueClientUpdate(); + } + } + } + + /** + * Runs only on server side + * + * @param tick The current tick of the machine + */ + protected void runningTick(long tick) { + consumeEnergy(); + } + + /** + * Runs only on server side + */ + protected boolean checkRecipe() { + return false; + } + + /** + * Runs only on server side + */ + protected void consumeEnergy() { + PowerLogic logic = getPowerLogic(); + + P processing = getProcessingLogic(); + + if (!logic.removeEnergyUnsafe(processing.getCalculatedEut())) { + stopMachine(true); + } + } + + public void doSound(byte aIndex, double aX, double aY, double aZ) { + switch (aIndex) { + case PROCESS_START_SOUND_INDEX -> { + if (getProcessStartSound() != null) + GT_Utility.doSoundAtClient(getProcessStartSound(), getTimeBetweenProcessSounds(), 1.0F, aX, aY, aZ); + } + case INTERRUPT_SOUND_INDEX -> GT_Utility + .doSoundAtClient(SoundResource.IC2_MACHINES_INTERRUPT_ONE, 100, 1.0F, aX, aY, aZ); + } + } + + public void startSoundLoop(byte aIndex, double aX, double aY, double aZ) { + if (aIndex == PROCESS_START_SOUND_INDEX && getProcessStartSound() != null) { + GT_Utility.doSoundAtClient(getProcessStartSound(), getTimeBetweenProcessSounds(), 1.0F, aX, aY, aZ); + } + } + + protected ResourceLocation getProcessStartSound() { + return null; + } + + protected int getTimeBetweenProcessSounds() { + return 100; + } + + @SideOnly(Side.CLIENT) + protected void doActivitySound(ResourceLocation activitySound) { + if (isActive() && activitySound != null && activitySoundLoop == null) { + activitySoundLoop = new GT_SoundLoop(activitySound, this, false, true); + Minecraft.getMinecraft() + .getSoundHandler() + .playSound(activitySoundLoop); + return; + } + + if (activitySoundLoop != null) { + activitySoundLoop = null; + } + + } + + @SideOnly(Side.CLIENT) + protected ResourceLocation getActivitySoundLoop() { + return null; + } + + protected ItemStack[] getInputItems() { + return itemInput.getStoredItems(); + } + + protected FluidStack[] getInputFluids() { + return fluidInput.getStoredFluids(); + } + + @Override + public int getProgress() { + P processing = getProcessingLogic(); + return processing.getProgress(); + } + + @Override + public int getMaxProgress() { + P processing = getProcessingLogic(); + return processing.getDuration(); + } + + @Override + public boolean increaseProgress(int progressAmount) { + P processing = getProcessingLogic(); + processing.increaseProgress(progressAmount); + return true; + } + + @Override + public boolean hasThingsToDo() { + return getMaxProgress() > 0; + } + + @Override + public boolean hasWorkJustBeenEnabled() { + return wasEnabled; + } + + @Override + public void enableWorking() { + wasEnabled = true; + canWork = true; + } + + @Override + public void disableWorking() { + canWork = false; + } + + @Override + public boolean wasShutdown() { + return powerShutDown; + } + + @Override + public boolean isAllowedToWork() { + return canWork; + } + + @Override + public boolean isActive() { + return active; + } + + @Override + public void setActive(boolean active) { + this.active = active; + } + + protected boolean isElectric() { + return isElectric; + } + + protected void setElectric(boolean isElectric) { + this.isElectric = isElectric; + } + + protected boolean isSteam() { + return isSteam; + } + + protected void setSteam(boolean isSteam) { + this.isSteam = isSteam; + } + + protected boolean acceptsFuel() { + return acceptsFuel; + } + + protected void setFuel(boolean acceptsFuel) { + this.acceptsFuel = acceptsFuel; + } + + protected boolean consumeFuel() { + if (isElectric() || isSteam()) return false; + if (isActive() && burnTime <= 0) { + for (int i = 0; i < itemInput.getSlots(); i++) { + ItemStack item = itemInput.getItemInSlot(i); + if (item == null) continue; + int checkBurnTime = TileEntityFurnace.getItemBurnTime(item) / 10; + if (checkBurnTime <= 0) continue; + item.stackSize--; + burnTime = checkBurnTime; + totalBurnTime = checkBurnTime; + break; + } + updateSlots(); + } + + if (--burnTime < 0) { + burnTime = 0; + totalBurnTime = 0; + return false; + } + return false; + } + + @Override + protected void addDebugInfo(EntityPlayer player, int logLevel, ArrayList<String> list) { + list.add( + GT_Utility.trans("186", "Owned by: ") + EnumChatFormatting.BLUE + + getOwnerName() + + EnumChatFormatting.RESET + + " (" + + EnumChatFormatting.AQUA + + getOwnerUuid() + + EnumChatFormatting.RESET + + ")"); + + if (acceptsFuel()) { + list.add("Fuel: " + EnumChatFormatting.GOLD + burnTime + "/" + totalBurnTime); + } + + PowerLogic logic = getPowerLogic(); + if (isElectric) { + list.add( + StatCollector.translateToLocal("GT5U.multiblock.energy") + ": " + + EnumChatFormatting.GREEN + + GT_Utility.formatNumbers(logic.getStoredEnergy()) + + EnumChatFormatting.RESET + + " EU / " + + EnumChatFormatting.YELLOW + + GT_Utility.formatNumbers(logic.getCapacity()) + + EnumChatFormatting.RESET + + " EU"); + list.add( + StatCollector.translateToLocal("GT5U.multiblock.usage") + ": " + + EnumChatFormatting.RED + + GT_Utility.formatNumbers(getProcessingLogic().getCalculatedEut()) + + EnumChatFormatting.RESET + + " EU/t"); + list.add( + StatCollector.translateToLocal("GT5U.multiblock.mei") + ": " + + EnumChatFormatting.YELLOW + + GT_Utility.formatNumbers(logic.getVoltage()) + + EnumChatFormatting.RESET + // TODO: Put ampere getter here, once that's variable + + " EU/t(*2A) " + + StatCollector.translateToLocal("GT5U.machines.tier") + + ": " + + EnumChatFormatting.YELLOW + + VN[GT_Utility.getTier(logic.getVoltage())] + + EnumChatFormatting.RESET); + } + + addProgressStringToScanner(player, logLevel, list); + + // TODO: Add CPU load calculator + list.add( + "Average CPU load of ~" + GT_Utility.formatNumbers(0) + + "ns over " + + GT_Utility.formatNumbers(0) + + " ticks with worst time of " + + GT_Utility.formatNumbers(0) + + "ns."); + } + + protected void addProgressStringToScanner(EntityPlayer player, int logLevel, ArrayList<String> list) { + P processing = getProcessingLogic(); + int progressTime = processing.getProgress(); + int maxProgressTime = processing.getDuration(); + list.add( + StatCollector.translateToLocal("GT5U.multiblock.Progress") + ": " + + EnumChatFormatting.GREEN + + GT_Utility.formatNumbers(progressTime > 20 ? progressTime / 20 : progressTime) + + EnumChatFormatting.RESET + + (progressTime > 20 ? " s / " : " ticks / ") + + EnumChatFormatting.YELLOW + + GT_Utility.formatNumbers(maxProgressTime > 20 ? maxProgressTime / 20 : maxProgressTime) + + EnumChatFormatting.RESET + + (maxProgressTime > 20 ? " s" : " ticks")); + } + + protected void stopMachine(boolean powerShutDown) { + setActive(false); + disableWorking(); + if (powerShutDown) { + setSound(GregTechTileClientEvents.STOP_SOUND_LOOP, INTERRUPT_SOUND_INDEX); + } + issueClientUpdate(); + } + + protected void updateSlots() { + itemInput.update(false); + itemOutput.update(false); + fluidInput.update(); + fluidOutput.update(); + } + + @Override + public int getBooleans() { + int booleans = 0; + if (isActive()) { + booleans |= ACTIVE; + } + return booleans; + } + + @Override + public void setBooleans(int booleans) { + setActive((booleans & ACTIVE) == ACTIVE); + } + + public boolean hasItemInput() { + return true; + } + + public boolean hasItemOutput() { + return true; + } + + public boolean hasFluidInput() { + return true; + } + + public boolean hasFluidOutput() { + return true; + } + + @Override + public void setSound(byte soundEvent, int soundEventValue) { + this.soundEvent = soundEvent; + this.soundEventValue = soundEventValue; + if (isServerSide()) { + return; + } + + switch (soundEventValue) { + case PROCESS_START_SOUND_INDEX -> { + if (getProcessStartSound() != null) GT_Utility.doSoundAtClient( + getProcessStartSound(), + getTimeBetweenProcessSounds(), + 1.0F, + getXCoord(), + getYCoord(), + getZCoord()); + } + case INTERRUPT_SOUND_INDEX -> GT_Utility.doSoundAtClient( + SoundResource.IC2_MACHINES_INTERRUPT_ONE, + 100, + 1.0F, + getXCoord(), + getYCoord(), + getZCoord()); + } + + } + + @Nullable + public ItemInventoryLogic getItemLogic(@Nonnull ForgeDirection side, @Nonnull InventoryType type) { + if (side == facing) return null; + return switch (type) { + case Input -> itemInput; + case Output -> itemOutput; + default -> null; + }; + } + + @Nullable + public FluidInventoryLogic getFluidLogic(@Nonnull ForgeDirection side, @Nonnull InventoryType type) { + if (side == facing) return null; + return switch (type) { + case Input -> fluidInput; + case Output -> fluidOutput; + default -> null; + }; + } + + @Override + @Nonnull + public P getProcessingLogic() { + if (processingLogic == null) { + processingLogic = createProcessingLogic().setMachineHost(this); + } + return Objects.requireNonNull(processingLogic); + } + + @OverrideOnly + @Nonnull + protected abstract P createProcessingLogic(); + + @Override + public boolean isInputSeparated() { + return false; + } + + @Nonnull + @Override + public VoidingMode getVoidMode() { + return voidingMode; + } + + @Override + public boolean needsUpdate() { + return processingUpdate; + } + + @Override + public void setProcessingUpdate(boolean update) { + processingUpdate = update; + } + + @Override + @Nonnull + public PowerLogic getPowerLogic(@Nonnull ForgeDirection side) { + if (side == facing) return new NullPowerLogic(); + return power; + } + + @Override + @Nonnull + public ForgeDirection getPowerOutputSide() { + return Objects.requireNonNull(facing.getOpposite()); + } + + protected void updatePowerLogic() { + power.setEnergyCapacity(GT_Values.V[tier] * power.getMaxAmperage() * 2 * MINUTE); + power.setMaxVoltage(GT_Values.V[tier]); + power.setMaxAmperage(1); + } + + @Nonnull + protected PowerLogic createPowerLogic() { + return new PowerLogic().setMaxAmperage(1) + .setType(PowerLogic.RECEIVER); + } + + @Nonnull + protected GUIProvider<?> createGUIProvider() { + return new MachineGUIProvider<>(this); + } + + @Nonnull + public GUIProvider<?> getGUI(@Nonnull UIBuildContext uiContext) { + return guiProvider; + } + + @Override + public ItemStack getAsItem() { + return MultiTileEntityRegistry.getRegistry(getMultiTileEntityRegistryID()) + .getItem(getMultiTileEntityID()); + } + + @Override + public String getMachineName() { + return StatCollector.translateToLocal(getAsItem().getUnlocalizedName()); + } + +} diff --git a/src/main/java/gregtech/api/multitileentity/multiblock/base/ComplexParallelController.java b/src/main/java/gregtech/api/multitileentity/multiblock/base/ComplexParallelController.java new file mode 100644 index 0000000000..cdcb77d6e5 --- /dev/null +++ b/src/main/java/gregtech/api/multitileentity/multiblock/base/ComplexParallelController.java @@ -0,0 +1,111 @@ +package gregtech.api.multitileentity.multiblock.base; + +import java.util.ArrayList; +import java.util.List; + +import javax.annotation.Nonnull; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.StatCollector; +import net.minecraft.world.World; + +import gregtech.api.logic.ComplexParallelProcessingLogic; +import gregtech.api.util.GT_Utility; +import gregtech.api.util.GT_Waila; +import mcp.mobius.waila.api.IWailaConfigHandler; +import mcp.mobius.waila.api.IWailaDataAccessor; + +public abstract class ComplexParallelController<C extends ComplexParallelController<C, P>, P extends ComplexParallelProcessingLogic<P>> + extends Controller<C, P> { + + protected int maxComplexParallels = 0; + protected int currentComplexParallels = 0; + + public ComplexParallelController() { + isSimpleMachine = false; + } + + protected void setMaxComplexParallels(int parallel, boolean stopMachine) { + if (parallel != maxComplexParallels && maxComplexParallels != 0 && stopMachine) { + stopMachine(false); + } + maxComplexParallels = parallel; + setProcessingUpdate(true); + } + + @Override + protected void stopMachine(boolean powerShutDown) { + super.stopMachine(powerShutDown); + } + + protected boolean hasPerfectOverclock() { + return false; + } + + @Override + protected void addProgressStringToScanner(EntityPlayer player, int logLevel, ArrayList<String> list) { + P processing = getProcessingLogic(); + for (int i = 0; i < maxComplexParallels; i++) { + list.add( + StatCollector.translateToLocal("GT5U.multiblock.Progress") + " " + + (i + 1) + + ": " + + EnumChatFormatting.GREEN + + GT_Utility.formatNumbers( + processing.getProgress(i) > 20 ? processing.getProgress(i) / 20 : processing.getProgress(i)) + + EnumChatFormatting.RESET + + (processing.getProgress(i) > 20 ? " s / " : " ticks / ") + + EnumChatFormatting.YELLOW + + GT_Utility.formatNumbers( + processing.getDuration(i) > 20 ? processing.getDuration(i) / 20 : processing.getDuration(i)) + + EnumChatFormatting.RESET + + (processing.getDuration(i) > 20 ? " s" : " ticks")); + } + } + + @Override + public void getWailaNBTData(EntityPlayerMP player, TileEntity tile, NBTTagCompound tag, World world, int x, int y, + int z) { + super.getWailaNBTData(player, tile, tag, world, x, y, z); + P processing = getProcessingLogic(); + tag.setInteger("maxComplexParallels", maxComplexParallels); + for (int i = 0; i < maxComplexParallels; i++) { + tag.setInteger("maxProgress" + i, processing.getDuration(i)); + tag.setInteger("progress" + i, processing.getProgress(i)); + } + } + + @Override + public void getWailaBody(ItemStack itemStack, List<String> currentTip, IWailaDataAccessor accessor, + IWailaConfigHandler config) { + super.getWailaBody(itemStack, currentTip, accessor, config); + final NBTTagCompound tag = accessor.getNBTData(); + maxComplexParallels = tag.getInteger("maxComplexParallels"); + for (int i = 0; i < maxComplexParallels; i++) { + long maxProgress = tag.getInteger("maxProgress" + i); + long progress = tag.getInteger("progress" + i); + currentTip.add( + "Process " + (i + 1) + + ": " + + GT_Waila + .getMachineProgressString(maxProgress > 0 && maxProgress >= progress, maxProgress, progress)); + } + } + + @Override + public void setProcessingLogicPower(@Nonnull P processingLogic) { + processingLogic.setAmperageOC(true); + processingLogic.setAvailableAmperage(getPowerLogic().getMaxAmperage() / maxComplexParallels); + processingLogic.setAvailableVoltage(getPowerLogic().getVoltage() / maxComplexParallels); + } + + @Override + public void updateProcessingLogic(@Nonnull P processingLogic) { + processingLogic.setMaxComplexParallel(maxComplexParallels); + } +} diff --git a/src/main/java/gregtech/api/multitileentity/multiblock/base/Controller.java b/src/main/java/gregtech/api/multitileentity/multiblock/base/Controller.java new file mode 100644 index 0000000000..7ffdc4fb60 --- /dev/null +++ b/src/main/java/gregtech/api/multitileentity/multiblock/base/Controller.java @@ -0,0 +1,1083 @@ +package gregtech.api.multitileentity.multiblock.base; + +import static gregtech.api.util.GT_Utility.moveMultipleItemStacks; +import static gregtech.common.misc.WirelessNetworkManager.strongCheckOrAddUser; +import static mcp.mobius.waila.api.SpecialChars.*; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.inventory.IInventory; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.StatCollector; +import net.minecraft.world.World; +import net.minecraftforge.common.util.ForgeDirection; +import net.minecraftforge.fluids.FluidStack; + +import org.lwjgl.input.Keyboard; + +import com.gtnewhorizon.structurelib.StructureLibAPI; +import com.gtnewhorizon.structurelib.alignment.IAlignment; +import com.gtnewhorizon.structurelib.alignment.IAlignmentLimits; +import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable; +import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing; +import com.gtnewhorizon.structurelib.alignment.enumerable.Flip; +import com.gtnewhorizon.structurelib.alignment.enumerable.Rotation; +import com.gtnewhorizon.structurelib.structure.IStructureDefinition; +import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment; +import com.gtnewhorizon.structurelib.util.Vec3Impl; +import com.gtnewhorizons.modularui.api.screen.ModularWindow; + +import cpw.mods.fml.common.network.NetworkRegistry; +import gregtech.api.enums.GT_Values.NBT; +import gregtech.api.enums.InventoryType; +import gregtech.api.enums.VoidingMode; +import gregtech.api.interfaces.IDescribable; +import gregtech.api.interfaces.fluid.IFluidStore; +import gregtech.api.logic.ControllerFluidLogic; +import gregtech.api.logic.ControllerItemLogic; +import gregtech.api.logic.FluidInventoryLogic; +import gregtech.api.logic.ItemInventoryLogic; +import gregtech.api.logic.MuTEProcessingLogic; +import gregtech.api.logic.PowerLogic; +import gregtech.api.multitileentity.enums.MultiTileCasingPurpose; +import gregtech.api.multitileentity.interfaces.IMultiBlockController; +import gregtech.api.multitileentity.interfaces.IMultiBlockPart; +import gregtech.api.multitileentity.interfaces.IMultiTileEntity.IMTE_AddToolTips; +import gregtech.api.multitileentity.machine.MultiTileBasicMachine; +import gregtech.api.multitileentity.multiblock.casing.FunctionalCasing; +import gregtech.api.multitileentity.multiblock.casing.UpgradeCasing; +import gregtech.api.net.GT_Packet_MultiTileEntity; +import gregtech.api.objects.GT_ItemStack; +import gregtech.api.util.GT_Multiblock_Tooltip_Builder; +import gregtech.api.util.GT_Utility; +import gregtech.api.util.GT_Waila; +import mcp.mobius.waila.api.IWailaConfigHandler; +import mcp.mobius.waila.api.IWailaDataAccessor; + +/** + * Multi Tile Entities - or MuTEs - don't have dedicated hatches, but their casings can become hatches. + */ +public abstract class Controller<C extends Controller<C, P>, P extends MuTEProcessingLogic<P>> + extends MultiTileBasicMachine<P> + implements IAlignment, IMultiBlockController, IDescribable, IMTE_AddToolTips, ISurvivalConstructable { + + public static final String ALL_INVENTORIES_NAME = "all"; + protected static final int AUTO_OUTPUT_FREQUENCY_TICK = 20; + + private static final Map<Integer, GT_Multiblock_Tooltip_Builder> tooltip = new ConcurrentHashMap<>(); + private final List<UpgradeCasing> upgradeCasings = new ArrayList<>(); + private final List<FunctionalCasing> functionalCasings = new ArrayList<>(); + protected BuildState buildState = new BuildState(); + + private boolean structureOkay = false, structureChanged = false; + private ExtendedFacing extendedFacing = ExtendedFacing.DEFAULT; + private IAlignmentLimits limits = getInitialAlignmentLimits(); + protected boolean separateInputs = getDefaultInputSeparationMode(); + protected VoidingMode voidingMode = getDefaultVoidingMode(); + protected boolean batchMode = getDefaultBatchMode(); + protected boolean recipeLock = getDefaultRecipeLockingMode(); + protected boolean shouldSort = false; + /** If this is set to true, the machine will get default WAILA behavior */ + protected boolean isSimpleMachine = true; + + protected boolean isCleanroom = false; + protected ControllerItemLogic controllerItemInput = new ControllerItemLogic(); + protected ControllerItemLogic controllerItemOutput = new ControllerItemLogic(); + protected ControllerFluidLogic controllerFluidInput = new ControllerFluidLogic(); + protected ControllerFluidLogic controllerFluidOutput = new ControllerFluidLogic(); + + // A list of sides + // Each side has a list of parts that have a cover that need to be ticked + protected List<LinkedList<WeakReference<IMultiBlockPart>>> registeredCoveredParts = Arrays.asList( + new LinkedList<>(), + new LinkedList<>(), + new LinkedList<>(), + new LinkedList<>(), + new LinkedList<>(), + new LinkedList<>()); + + // A list for each purpose that a casing can register to, to be ticked + protected List<LinkedList<WeakReference<IMultiBlockPart>>> registeredTickableParts = new ArrayList<>(); + + public Controller() { + for (int i = 0; i < MultiTileCasingPurpose.values().length; i++) { + registeredTickableParts.add(new LinkedList<>()); + } + } + + /** Registry ID of the required casing */ + public abstract short getCasingRegistryID(); + + /** Meta ID of the required casing */ + public abstract int getCasingMeta(); + + /** + * Create the tooltip for this multi block controller. + */ + protected abstract GT_Multiblock_Tooltip_Builder createTooltip(); + + /** + * @return The starting offset for the structure builder + */ + public abstract Vec3Impl getStartingStructureOffset(); + + /** + * Due to limitation of Java type system, you might need to do an unchecked cast. HOWEVER, the returned + * IStructureDefinition is expected to be evaluated against current instance only, and should not be used against + * other instances, even for those of the same class. + */ + @Override + public abstract IStructureDefinition<C> getStructureDefinition(); + + /** + * Checks the Machine. + * <p> + * NOTE: If using `buildState` be sure to `startBuilding()` and either `endBuilding()` or `failBuilding()` + */ + public boolean checkMachine() { + calculateTier(); + updatePowerLogic(); + return tier > 0; + } + + protected void calculateTier() { + double sum = 0; + if (functionalCasings == null || functionalCasings.size() == 0) { + return; + } + for (FunctionalCasing casing : functionalCasings) { + sum += casing.getPartTier() * casing.getPartModifier(); + } + tier = (int) Math.min(Math.floor(sum / functionalCasings.size()), 14); + } + + @Override + public void writeMultiTileNBT(NBTTagCompound nbt) { + super.writeMultiTileNBT(nbt); + + nbt.setBoolean(NBT.STRUCTURE_OK, structureOkay); + nbt.setByte( + NBT.ROTATION, + (byte) extendedFacing.getRotation() + .getIndex()); + nbt.setByte( + NBT.FLIP, + (byte) extendedFacing.getFlip() + .getIndex()); + + nbt.setString(NBT.VOIDING_MODE, voidingMode.name); + nbt.setBoolean(NBT.SEPARATE_INPUTS, separateInputs); + nbt.setBoolean(NBT.RECIPE_LOCK, recipeLock); + nbt.setBoolean(NBT.BATCH_MODE, batchMode); + } + + @Override + protected void saveItemLogic(NBTTagCompound nbt) { + NBTTagCompound itemInputNBT = controllerItemInput.saveToNBT(); + nbt.setTag(NBT.INV_INPUT_LIST, itemInputNBT); + NBTTagCompound itemOutputNBT = controllerItemOutput.saveToNBT(); + nbt.setTag(NBT.INV_OUTPUT_LIST, itemOutputNBT); + } + + @Override + protected void saveFluidLogic(NBTTagCompound nbt) { + NBTTagCompound fluidInputNBT = controllerFluidInput.saveToNBT(); + nbt.setTag(NBT.TANK_IN, fluidInputNBT); + NBTTagCompound fluidOutputNBT = controllerFluidOutput.saveToNBT(); + nbt.setTag(NBT.TANK_OUT, fluidOutputNBT); + } + + @Override + public void readMultiTileNBT(NBTTagCompound nbt) { + super.readMultiTileNBT(nbt); + + // Multiblock inventories are a collection of inventories. The first inventory is the default internal + // inventory, and the others are added by inventory extending blocks. + + structureOkay = nbt.getBoolean(NBT.STRUCTURE_OK); + extendedFacing = ExtendedFacing + .of(getFrontFacing(), Rotation.byIndex(nbt.getByte(NBT.ROTATION)), Flip.byIndex(nbt.getByte(NBT.FLIP))); + + voidingMode = VoidingMode.fromName(nbt.getString(NBT.VOIDING_MODE)); + separateInputs = nbt.getBoolean(NBT.SEPARATE_INPUTS); + recipeLock = nbt.getBoolean(NBT.RECIPE_LOCK); + batchMode = nbt.getBoolean(NBT.BATCH_MODE); + } + + @Override + protected void loadItemLogic(NBTTagCompound nbt) { + if (!nbt.hasKey(NBT.INV_INPUT_LIST) && !nbt.hasKey(NBT.INV_OUTPUT_LIST)) { + controllerItemInput.addInventory(new ItemInventoryLogic(16)); + controllerItemOutput.addInventory(new ItemInventoryLogic(16)); + return; + } + controllerItemInput.loadFromNBT(nbt.getCompoundTag(NBT.INV_INPUT_LIST)); + controllerItemOutput.loadFromNBT(nbt.getCompoundTag(NBT.INV_OUTPUT_LIST)); + } + + @Override + protected void loadFluidLogic(NBTTagCompound nbt) { + if (!nbt.hasKey(NBT.TANK_IN) && !nbt.hasKey(NBT.TANK_OUT)) { + controllerFluidInput.addInventory(new FluidInventoryLogic(16, 32000)); + controllerFluidOutput.addInventory(new FluidInventoryLogic(16, 32000)); + return; + } + controllerFluidInput.loadFromNBT(nbt.getCompoundTag(NBT.TANK_IN)); + controllerFluidOutput.loadFromNBT(nbt.getCompoundTag(NBT.TANK_OUT)); + } + + @Override + public void addToolTips(List<String> aList, ItemStack aStack, boolean aF3_H) { + aList.addAll(Arrays.asList(getDescription())); + } + + @Override + public String[] getDescription() { + if (Keyboard.isKeyDown(Keyboard.KEY_LSHIFT)) { + return getTooltip().getStructureInformation(); + } + + return getTooltip().getInformation(); + } + + @Override + protected void addDebugInfo(EntityPlayer aPlayer, int aLogLevel, ArrayList<String> tList) { + super.addDebugInfo(aPlayer, aLogLevel, tList); + tList.add("Structure ok: " + checkStructure(false)); + } + + protected int getToolTipID() { + return getMultiTileEntityRegistryID() << 16 + getMultiTileEntityID(); + } + + protected GT_Multiblock_Tooltip_Builder getTooltip() { + GT_Multiblock_Tooltip_Builder builder = tooltip.get(getToolTipID()); + if (builder == null) { + builder = createTooltip(); + tooltip.put(getToolTipID(), builder); + } + return builder; + } + + @Override + public boolean checkStructure(boolean aForceReset) { + if (!isServerSide()) return structureOkay; + + // Only trigger an update if forced (from onPostTick, generally), or if the structure has changed + if ((structureChanged || aForceReset)) { + clearSpecialLists(); + structureOkay = checkMachine(); + } + structureChanged = false; + return structureOkay; + } + + @Override + public void onStructureChange() { + structureChanged = true; + } + + public final boolean checkPiece(String piece, Vec3Impl offset) { + return checkPiece(piece, offset.get0(), offset.get1(), offset.get2()); + } + + /** + * Explanation of the world coordinate these offset means: + * <p> + * Imagine you stand in front of the controller, with controller facing towards you not rotated or flipped. + * <p> + * The horizontalOffset would be the number of blocks on the left side of the controller, not counting controller + * itself. The verticalOffset would be the number of blocks on the top side of the controller, not counting + * controller itself. The depthOffset would be the number of blocks between you and controller, not counting + * controller itself. + * <p> + * All these offsets can be negative. + */ + protected final boolean checkPiece(String piece, int horizontalOffset, int verticalOffset, int depthOffset) { + return getCastedStructureDefinition().check( + this, + piece, + getWorld(), + getExtendedFacing(), + getXCoord(), + getYCoord(), + getZCoord(), + horizontalOffset, + verticalOffset, + depthOffset, + !structureOkay); + } + + public final boolean buildPiece(String piece, ItemStack trigger, boolean hintsOnly, Vec3Impl offset) { + return buildPiece(piece, trigger, hintsOnly, offset.get0(), offset.get1(), offset.get2()); + } + + protected final boolean buildPiece(String piece, ItemStack trigger, boolean hintOnly, int horizontalOffset, + int verticalOffset, int depthOffset) { + return getCastedStructureDefinition().buildOrHints( + this, + trigger, + piece, + getWorld(), + getExtendedFacing(), + getXCoord(), + getYCoord(), + getZCoord(), + horizontalOffset, + verticalOffset, + depthOffset, + hintOnly); + } + + protected final int survivalBuildPiece(String piece, ItemStack trigger, Vec3Impl offset, int elementBudget, + ISurvivalBuildEnvironment env, boolean check) { + return survivalBuildPiece( + piece, + trigger, + offset.get0(), + offset.get1(), + offset.get2(), + elementBudget, + env, + check); + } + + protected final Integer survivalBuildPiece(String piece, ItemStack trigger, int horizontalOffset, + int verticalOffset, int depthOffset, int elementBudget, ISurvivalBuildEnvironment env, boolean check) { + return getCastedStructureDefinition().survivalBuild( + this, + trigger, + piece, + getWorld(), + getExtendedFacing(), + getXCoord(), + getYCoord(), + getZCoord(), + horizontalOffset, + verticalOffset, + depthOffset, + elementBudget, + env, + check); + } + + @SuppressWarnings("unchecked") + private IStructureDefinition<Controller<C, P>> getCastedStructureDefinition() { + return (IStructureDefinition<Controller<C, P>>) getStructureDefinition(); + } + + @Override + public ExtendedFacing getExtendedFacing() { + return extendedFacing; + } + + @Override + public void setExtendedFacing(ExtendedFacing newExtendedFacing) { + if (extendedFacing == newExtendedFacing) { + return; + } + + onStructureChange(); + if (structureOkay) stopMachine(false); + extendedFacing = newExtendedFacing; + structureOkay = false; + if (isServerSide()) { + StructureLibAPI.sendAlignment( + this, + new NetworkRegistry.TargetPoint( + getWorld().provider.dimensionId, + getXCoord(), + getYCoord(), + getZCoord(), + 512)); + } else { + issueTextureUpdate(); + } + + } + + @Override + public boolean onWrenchRightClick(EntityPlayer aPlayer, ItemStack tCurrentItem, ForgeDirection wrenchSide, float aX, + float aY, float aZ, ItemStack aTool) { + if (wrenchSide != getFrontFacing()) + return super.onWrenchRightClick(aPlayer, tCurrentItem, wrenchSide, aX, aY, aZ); + if (aPlayer.isSneaking()) { + // we won't be allowing horizontal flips, as it can be perfectly emulated by rotating twice and flipping + // horizontally allowing an extra round of flip make it hard to draw meaningful flip markers in + // GT_Proxy#drawGrid + toolSetFlip(getFlip().isHorizontallyFlipped() ? Flip.NONE : Flip.HORIZONTAL); + } else { + toolSetRotation(null); + } + return true; + } + + @Override + public void registerCoveredPartOnSide(final ForgeDirection side, IMultiBlockPart part) { + if (side == ForgeDirection.UNKNOWN) return; + + final LinkedList<WeakReference<IMultiBlockPart>> registeredCovers = registeredCoveredParts.get(side.ordinal()); + // TODO: Make sure that we're not already registered on this side + registeredCovers.add(new WeakReference<>(part)); + } + + @Override + public void unregisterCoveredPartOnSide(final ForgeDirection side, IMultiBlockPart aPart) { + if (side == ForgeDirection.UNKNOWN) return; + + final LinkedList<WeakReference<IMultiBlockPart>> coveredParts = registeredCoveredParts.get(side.ordinal()); + final Iterator<WeakReference<IMultiBlockPart>> it = coveredParts.iterator(); + while (it.hasNext()) { + final IMultiBlockPart part = (it.next()).get(); + if (part == null || part == aPart) it.remove(); + } + } + + @Override + public void registerCaseWithPurpose(MultiTileCasingPurpose purpose, IMultiBlockPart part) { + final LinkedList<WeakReference<IMultiBlockPart>> tickableParts = registeredTickableParts.get(purpose.ordinal()); + final Iterator<WeakReference<IMultiBlockPart>> it = tickableParts.iterator(); + while (it.hasNext()) { + final IMultiBlockPart next = (it.next()).get(); + if (next == null) { + it.remove(); + } else if (next == part) { + return; + } + } + tickableParts.add(new WeakReference<>(part)); + } + + @Override + public void unregisterCaseWithPurpose(MultiTileCasingPurpose purpose, IMultiBlockPart part) { + final LinkedList<WeakReference<IMultiBlockPart>> tickableParts = registeredTickableParts.get(purpose.ordinal()); + final Iterator<WeakReference<IMultiBlockPart>> it = tickableParts.iterator(); + while (it.hasNext()) { + final IMultiBlockPart next = (it.next()).get(); + if (next == null || next == part) it.remove(); + } + } + + @Override + public void onFirstTick(boolean isServerSide) { + super.onFirstTick(isServerSide); + if (isServerSide) { + checkStructure(true); + } else { + StructureLibAPI.queryAlignment(this); + } + } + + private boolean tickCovers() { + for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) { + // TODO: Tick controller covers, if any + final LinkedList<WeakReference<IMultiBlockPart>> coveredParts = this.registeredCoveredParts + .get(side.ordinal()); + final Iterator<WeakReference<IMultiBlockPart>> it = coveredParts.iterator(); + while (it.hasNext()) { + final IMultiBlockPart part = (it.next()).get(); + if (part == null) { + it.remove(); + continue; + } + if (!part.tickCoverAtSide(side, mTickTimer)) it.remove(); + } + } + + return true; + } + + @Override + public void onTick(long tick, boolean isServerSide) { + if (!tickCovers()) { + return; + } + } + + @Override + public void onPostTick(long tick, boolean isServerSide) { + if (!isServerSide) { // client side + doActivitySound(getActivitySoundLoop()); + return; + } + + // server side + if (tick % 600 == 5) { + // Recheck the structure every 30 seconds or so + if (!checkStructure(false)) checkStructure(true); + } + if (checkStructure(false)) { + runMachine(tick); + pushItemOutputs(tick); + pushFluidOutputs(tick); + + } else { + stopMachine(false); + } + + } + + protected void pushItemOutputs(long tick) { + if (tick % AUTO_OUTPUT_FREQUENCY_TICK != 0) return; + final LinkedList<WeakReference<IMultiBlockPart>> registeredItemOutputs = registeredTickableParts + .get(MultiTileCasingPurpose.ItemOutput.ordinal()); + final Iterator<WeakReference<IMultiBlockPart>> itemOutputIterator = registeredItemOutputs.iterator(); + while (itemOutputIterator.hasNext()) { + final IMultiBlockPart part = (itemOutputIterator.next()).get(); + if (part == null) { + itemOutputIterator.remove(); + continue; + } + if (!part.shouldTick(mTickTimer)) { + itemOutputIterator.remove(); + continue; + } + + final IInventory facingInventory = part.getIInventoryAtSide(part.getFrontFacing()); + if (facingInventory == null) { + continue; + } + + moveMultipleItemStacks( + part, + facingInventory, + part.getFrontFacing(), + part.getBackFacing(), + null, + false, + (byte) 64, + (byte) 1, + (byte) 64, + (byte) 1, + part.getSizeInventory()); + for (int i = 0; i < part.getSizeInventory(); i++) { + if (part.getStackInSlot(i) != null && part.getStackInSlot(i).stackSize <= 0) { + part.setInventorySlotContents(i, null); + } + } + + } + } + + protected void pushFluidOutputs(long tick) { + if (tick % AUTO_OUTPUT_FREQUENCY_TICK != 0) return; + final LinkedList<WeakReference<IMultiBlockPart>> registeredFluidOutputs = registeredTickableParts + .get(MultiTileCasingPurpose.FluidOutput.ordinal()); + final Iterator<WeakReference<IMultiBlockPart>> fluidOutputIterator = registeredFluidOutputs.iterator(); + while (fluidOutputIterator.hasNext()) { + final IMultiBlockPart part = (fluidOutputIterator.next()).get(); + if (part == null) { + fluidOutputIterator.remove(); + continue; + } + if (!part.shouldTick(mTickTimer)) { + fluidOutputIterator.remove(); + } + } + } + + @Override + public void setCleanroom(boolean cleanroom) { + isCleanroom = cleanroom; + } + + protected void clearSpecialLists() { + upgradeCasings.clear(); + functionalCasings.clear(); + } + + @Override + public final boolean isFacingValid(ForgeDirection facing) { + return canSetToDirectionAny(facing); + } + + @Override + public void onFacingChange() { + toolSetDirection(getFrontFacing()); + onStructureChange(); + } + + @Override + public boolean allowCoverOnSide(ForgeDirection side, GT_ItemStack aCoverID) { + return side != facing; + } + + @Override + public String[] getStructureDescription(ItemStack stackSize) { + return getTooltip().getStructureHint(); + } + + @Override + public IAlignmentLimits getAlignmentLimits() { + return limits; + } + + protected void setAlignmentLimits(IAlignmentLimits mLimits) { + this.limits = mLimits; + } + + public boolean isSeparateInputs() { + return separateInputs; + } + + public void setSeparateInputs(boolean aSeparateInputs) { + separateInputs = aSeparateInputs; + } + + protected IAlignmentLimits getInitialAlignmentLimits() { + return (d, r, f) -> !f.isVerticallyFliped(); + } + + public static class BuildState { + + /** + * Utility class to keep track of the build state of a multiblock + */ + boolean building = false; + + Vec3Impl currentOffset; + + public void startBuilding(Vec3Impl structureOffset) { + if (building) throw new IllegalStateException("Already building!"); + building = true; + setCurrentOffset(structureOffset); + } + + public Vec3Impl setCurrentOffset(Vec3Impl structureOffset) { + verifyBuilding(); + return (currentOffset = structureOffset); + } + + private void verifyBuilding() { + if (!building) throw new IllegalStateException("Not building!"); + } + + public boolean failBuilding() { + building = false; + currentOffset = null; + return false; + } + + public Vec3Impl stopBuilding() { + final Vec3Impl toReturn = getCurrentOffset(); + building = false; + currentOffset = null; + + return toReturn; + } + + public Vec3Impl getCurrentOffset() { + verifyBuilding(); + return currentOffset; + } + + public Vec3Impl addOffset(Vec3Impl offset) { + verifyBuilding(); + return setCurrentOffset(currentOffset.add(offset)); + } + } + + public void registerSpecialCasings(MultiBlockPart part) { + if (part instanceof UpgradeCasing) { + upgradeCasings.add((UpgradeCasing) part); + } + if (part instanceof FunctionalCasing) { + functionalCasings.add((FunctionalCasing) part); + } + } + + // #region Fluid - MultiBlock related Fluid Tank behaviour. + + @Override + @Nullable + public FluidInventoryLogic getFluidLogic(@Nonnull ForgeDirection side, @Nonnull InventoryType type) { + if (side == facing) return null; + return switch (type) { + case Input -> controllerFluidInput.getAllInventoryLogics(); + case Output -> controllerFluidOutput.getAllInventoryLogics(); + default -> null; + }; + } + + @Nullable + public FluidInventoryLogic getFluidLogic(@Nonnull InventoryType type, @Nullable UUID id) { + return switch (type) { + case Input -> controllerFluidInput.getInventoryLogic(id); + case Output -> controllerFluidOutput.getInventoryLogic(id); + default -> null; + }; + } + + @Override + @Nonnull + public UUID registerFluidInventory(int tanks, long capacity, int tier, @Nonnull InventoryType type, + boolean isUpgradeInventory) { + return switch (type) { + case Input -> controllerFluidInput + .addInventory(new FluidInventoryLogic(tanks, capacity, tier, isUpgradeInventory)); + case Output -> controllerFluidOutput + .addInventory(new FluidInventoryLogic(tanks, capacity, tier, isUpgradeInventory)); + case Both -> { + UUID id = controllerFluidInput + .addInventory(new FluidInventoryLogic(tanks, capacity, tier, isUpgradeInventory)); + controllerFluidOutput + .addInventory(id, new FluidInventoryLogic(tanks, capacity, tier, isUpgradeInventory)); + yield id; + } + }; + } + + @Override + @Nonnull + public FluidInventoryLogic unregisterFluidInventory(@Nonnull UUID id, @Nonnull InventoryType type) { + return switch (type) { + case Input -> controllerFluidInput.removeInventory(id); + case Output -> controllerFluidOutput.removeInventory(id); + case Both -> { + FluidInventoryLogic input = controllerFluidInput.removeInventory(id); + FluidInventoryLogic output = controllerFluidOutput.removeInventory(id); + yield new FluidInventoryLogic( + Arrays.asList(input, output) + .stream() + .map(inv -> inv.getInventory()) + .collect(Collectors.toList())); + } + }; + } + + @Override + public void changeFluidInventoryDisplayName(@Nullable UUID id, @Nullable String displayName, + @Nonnull InventoryType type) { + switch (type) { + case Input: + controllerFluidInput.setInventoryDisplayName(id, displayName); + break; + case Output: + controllerFluidOutput.setInventoryDisplayName(id, displayName); + break; + case Both: + controllerFluidInput.setInventoryDisplayName(id, displayName); + controllerFluidOutput.setInventoryDisplayName(id, displayName); + break; + } + } + + // #endregion Fluid + + // #region Item - MultiBlock related Item behaviour. + + @Override + @Nullable + public ItemInventoryLogic getItemLogic(@Nonnull ForgeDirection side, @Nonnull InventoryType type) { + if (side == facing) return null; + return switch (type) { + case Input -> controllerItemInput.getAllInventoryLogics(); + case Output -> controllerItemOutput.getAllInventoryLogics(); + default -> null; + }; + } + + @Override + @Nullable + public ItemInventoryLogic getItemLogic(@Nonnull InventoryType type, @Nullable UUID id) { + return switch (type) { + case Input -> controllerItemInput.getInventoryLogic(id); + case Output -> controllerItemOutput.getInventoryLogic(id); + default -> null; + }; + } + + @Override + @Nonnull + public UUID registerItemInventory(int slots, int tier, @Nonnull InventoryType type, boolean isUpgradeInventory) { + return switch (type) { + case Input -> controllerItemInput.addInventory(new ItemInventoryLogic(slots, tier, isUpgradeInventory)); + case Output -> controllerItemOutput.addInventory(new ItemInventoryLogic(slots, tier, isUpgradeInventory)); + case Both -> { + UUID id = controllerItemInput.addInventory(new ItemInventoryLogic(slots, tier, isUpgradeInventory)); + controllerItemOutput.addInventory(id, new ItemInventoryLogic(slots, tier, isUpgradeInventory)); + yield id; + } + }; + } + + @Override + public ItemInventoryLogic unregisterItemInventory(@Nonnull UUID id, @Nonnull InventoryType type) { + return switch (type) { + case Input -> controllerItemInput.removeInventory(id); + case Output -> controllerItemOutput.removeInventory(id); + case Both -> { + ItemInventoryLogic input = controllerItemInput.removeInventory(id); + ItemInventoryLogic output = controllerItemOutput.removeInventory(id); + yield new ItemInventoryLogic( + Arrays.asList(input, output) + .stream() + .map(inv -> inv.getInventory()) + .collect(Collectors.toList())); + } + }; + } + + @Override + public void changeItemInventoryDisplayName(@Nullable UUID id, @Nullable String displayName, + @Nonnull InventoryType type) { + switch (type) { + case Input: + controllerItemInput.setInventoryDisplayName(id, displayName); + break; + case Output: + controllerItemOutput.setInventoryDisplayName(id, displayName); + break; + case Both: + controllerItemInput.setInventoryDisplayName(id, displayName); + controllerItemOutput.setInventoryDisplayName(id, displayName); + break; + } + } + + // #endregion Item + + // #region Energy + + @Nonnull + @Override + public PowerLogic getPowerLogic() { + return getPowerLogic(ForgeDirection.UNKNOWN); + } + + // #endregion Energy + + @Override + protected void updateSlots() { + controllerItemInput.getAllInventoryLogics() + .update(shouldSort); + controllerItemOutput.getAllInventoryLogics() + .update(shouldSort); + controllerFluidInput.getAllInventoryLogics() + .update(); + controllerFluidOutput.getAllInventoryLogics() + .update(); + } + + /* + * GUI Work - Multiblock GUI related methods + */ + @Override + public boolean useModularUI() { + return true; + } + + @Override + public boolean hasGui(ForgeDirection side) { + return true; + } + + @Override + protected void addTitleTextStyle(ModularWindow.Builder builder, String title) { + // leave empty + } + + @Override + public boolean supportsVoidProtection() { + return true; + } + + @Override + public VoidingMode getVoidingMode() { + return voidingMode; + } + + @Override + public void setVoidingMode(VoidingMode mode) { + this.voidingMode = mode; + } + + @Override + public boolean canDumpItemToME() { + return false; + } + + @Override + public boolean canDumpFluidToME() { + return false; + } + + @Override + public boolean supportsInputSeparation() { + return true; + } + + @Override + public boolean isInputSeparated() { + return separateInputs; + } + + @Override + public void setInputSeparation(Boolean enabled) { + this.separateInputs = enabled; + } + + @Override + public boolean supportsBatchMode() { + return true; + } + + @Override + public boolean isBatchModeEnabled() { + return batchMode; + } + + @Override + public void setBatchMode(Boolean mode) { + this.batchMode = mode; + } + + @Override + public boolean supportsSingleRecipeLocking() { + return false; + } + + @Override + public boolean isRecipeLockingEnabled() { + return recipeLock; + } + + @Override + public void setRecipeLocking(Boolean enabled) { + this.recipeLock = enabled; + } + + @Override + public void getWailaNBTData(EntityPlayerMP player, TileEntity tile, NBTTagCompound tag, World world, int x, int y, + int z) { + super.getWailaNBTData(player, tile, tag, world, x, y, z); + P processing = getProcessingLogic(); + tag.setInteger("progress", processing.getProgress()); + tag.setInteger("maxProgress", processing.getDuration()); + tag.setBoolean("structureOkay", structureOkay); + tag.setBoolean("isActive", isActive()); + if (isActive()) { + tag.setLong("energyUsage", getProcessingLogic().getCalculatedEut()); + tag.setLong("energyTier", tier); + } + } + + @Override + public void getWailaBody(ItemStack itemStack, List<String> currentTip, IWailaDataAccessor accessor, + IWailaConfigHandler config) { + super.getWailaBody(itemStack, currentTip, accessor, config); + final NBTTagCompound tag = accessor.getNBTData(); + if (!tag.getBoolean("structureOkay")) { + currentTip.add(RED + "** INCOMPLETE STRUCTURE **" + RESET); + } else { + currentTip.add((GREEN + "Running Fine") + RESET); + } + if (isSimpleMachine) { + boolean isActive = tag.getBoolean("isActive"); + currentTip.add( + GT_Waila.getMachineProgressString(isActive, tag.getInteger("maxProgress"), tag.getInteger("progress"))); + } + boolean isActive = tag.getBoolean("isActive"); + if (isActive) { + long energyTier = tag.getLong("energyTier"); + long actualEnergyUsage = tag.getLong("energyUsage"); + if (actualEnergyUsage > 0) { + currentTip.add( + StatCollector.translateToLocalFormatted( + "GT5U.waila.energy.use_with_amperage", + GT_Utility.formatNumbers(actualEnergyUsage), + GT_Utility.getAmperageForTier(actualEnergyUsage, (byte) energyTier), + GT_Utility.getColoredTierNameFromTier((byte) energyTier))); + } else if (actualEnergyUsage < 0) { + currentTip.add( + StatCollector.translateToLocalFormatted( + "GT5U.waila.energy.produce_with_amperage", + GT_Utility.formatNumbers(-actualEnergyUsage), + GT_Utility.getAmperageForTier(-actualEnergyUsage, (byte) energyTier), + GT_Utility.getColoredTierNameFromTier((byte) energyTier))); + } + } + } + + @Override + public GT_Packet_MultiTileEntity getClientDataPacket() { + final GT_Packet_MultiTileEntity packet = super.getClientDataPacket(); + + return packet; + + } + + @Override + public void enableWorking() { + super.enableWorking(); + if (!structureOkay) { + checkStructure(true); + } + } + + @Override + public List<ItemStack> getItemOutputSlots(ItemStack[] toOutput) { + return new ArrayList<>(0); + } + + @Override + public List<? extends IFluidStore> getFluidOutputSlots(FluidStack[] toOutput) { + return new ArrayList<>(0); + } + + @Override + @Nonnull + public Set<Entry<UUID, FluidInventoryLogic>> getAllFluidInventoryLogics(@Nonnull InventoryType type) { + return switch (type) { + case Input -> controllerFluidInput.getAllInventoryLogicsAsEntrySet(); + case Output -> controllerFluidOutput.getAllInventoryLogicsAsEntrySet(); + default -> super.getAllFluidInventoryLogics(type); + }; + } + + @Override + @Nonnull + public Set<Entry<UUID, ItemInventoryLogic>> getAllItemInventoryLogics(@Nonnull InventoryType type) { + return switch (type) { + case Input -> controllerItemInput.getAllInventoryLogicsAsEntrySet(); + case Output -> controllerItemOutput.getAllInventoryLogicsAsEntrySet(); + default -> super.getAllItemInventoryLogics(type); + }; + } + + @Override + public void setWirelessSupport(boolean canUse) { + if (canUse) { + strongCheckOrAddUser(getOwnerUuid()); + } + power.setCanUseWireless(canUse, getOwnerUuid()); + } + + @Override + public void setLaserSupport(boolean canUse) { + power.setCanUseLaser(canUse); + } + + @Override + public void setMaxAmperage(long amperage) { + power.setMaxAmperage(amperage); + } +} diff --git a/src/main/java/gregtech/api/multitileentity/multiblock/base/MultiBlockPart.java b/src/main/java/gregtech/api/multitileentity/multiblock/base/MultiBlockPart.java new file mode 100644 index 0000000000..5331d1477d --- /dev/null +++ b/src/main/java/gregtech/api/multitileentity/multiblock/base/MultiBlockPart.java @@ -0,0 +1,711 @@ +package gregtech.api.multitileentity.multiblock.base; + +import static com.google.common.math.LongMath.log2; +import static gregtech.api.enums.GT_Values.B; +import static gregtech.api.enums.Textures.BlockIcons.FLUID_IN_SIGN; +import static gregtech.api.enums.Textures.BlockIcons.FLUID_OUT_SIGN; +import static gregtech.api.enums.Textures.BlockIcons.ITEM_IN_SIGN; +import static gregtech.api.enums.Textures.BlockIcons.ITEM_OUT_SIGN; +import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_ENERGY_IN_MULTI; +import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_ENERGY_OUT_MULTI; +import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_PIPE_IN; +import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_PIPE_OUT; + +import java.math.RoundingMode; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.UUID; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.ChunkCoordinates; +import net.minecraft.util.StatCollector; +import net.minecraftforge.common.util.ForgeDirection; +import net.minecraftforge.fluids.Fluid; + +import com.gtnewhorizons.modularui.api.screen.ModularWindow.Builder; +import com.gtnewhorizons.modularui.api.screen.UIBuildContext; +import com.gtnewhorizons.modularui.common.widget.DrawableWidget; + +import gregtech.api.enums.GT_Values.NBT; +import gregtech.api.enums.InventoryType; +import gregtech.api.fluid.FluidTankGT; +import gregtech.api.gui.GUIHost; +import gregtech.api.gui.GUIProvider; +import gregtech.api.interfaces.ITexture; +import gregtech.api.logic.FluidInventoryLogic; +import gregtech.api.logic.ItemInventoryLogic; +import gregtech.api.logic.NullPowerLogic; +import gregtech.api.logic.PowerLogic; +import gregtech.api.logic.interfaces.PowerLogicHost; +import gregtech.api.multitileentity.MultiTileEntityRegistry; +import gregtech.api.multitileentity.base.NonTickableMultiTileEntity; +import gregtech.api.multitileentity.enums.MultiTileCasingPurpose; +import gregtech.api.multitileentity.interfaces.IMultiBlockController; +import gregtech.api.multitileentity.interfaces.IMultiBlockPart; +import gregtech.api.multitileentity.interfaces.IMultiTileEntity; +import gregtech.api.multitileentity.interfaces.IMultiTileEntity.IMTE_HasModes; +import gregtech.api.render.TextureFactory; +import gregtech.api.util.GT_Utility; +import gregtech.common.covers.CoverInfo; +import gregtech.common.gui.PartGUIProvider; +import mcp.mobius.waila.api.IWailaConfigHandler; +import mcp.mobius.waila.api.IWailaDataAccessor; + +public abstract class MultiBlockPart extends NonTickableMultiTileEntity + implements IMultiBlockPart, IMTE_HasModes, PowerLogicHost, IMultiTileEntity.IMTE_AddToolTips, GUIHost { + + public static final int NOTHING = 0, ENERGY_IN = B[0], ENERGY_OUT = B[1], FLUID_IN = B[2], FLUID_OUT = B[3], + ITEM_IN = B[4], ITEM_OUT = B[5]; + + protected final List<Integer> BASIC_MODES = new ArrayList<>( + Arrays.asList(NOTHING, ENERGY_IN, ENERGY_OUT, FLUID_IN, FLUID_OUT, ITEM_IN, ITEM_OUT)); + + protected Set<MultiTileCasingPurpose> registeredPurposes = new HashSet<>(); + + protected ChunkCoordinates targetPosition = null; + + protected int allowedModes = NOTHING; // BITMASK - Modes allowed for this part + protected int mode = 0; // Mode selected for this part + + protected UUID lockedInventory; + protected int mLockedInventoryIndex = 0; + protected FluidTankGT configurationTank = new FluidTankGT(); + + @Nonnull + protected final GUIProvider<?> guiProvider = createGUIProvider(); + + /** + * What Part Tier is this part? All Basic Casings are Tier 1, and will allow: Energy, Item, Fluid input/output. Some + * of the more advanced modes can be set to require a higher tier part. + */ + public int getPartTier() { + return 1; + } + + @Override + public UUID getLockedInventory() { + return lockedInventory; + } + + public void setTarget(IMultiBlockController newTarget, int aAllowedModes) { + IMultiBlockController currentTarget = getTarget(false); + if (currentTarget != null && currentTarget != newTarget) { + for (MultiTileCasingPurpose purpose : registeredPurposes) { + unregisterPurpose(purpose); + } + } + targetPosition = (newTarget == null ? null : newTarget.getCoords()); + allowedModes = aAllowedModes; + if (newTarget != null) { + registerCovers(newTarget); + registerPurposes(); + } + } + + protected void registerPurpose(MultiTileCasingPurpose purpose) { + IMultiBlockController target = getTarget(false); + if (target != null) { + target.registerCaseWithPurpose(purpose, this); + registeredPurposes.add(purpose); + } + } + + protected void unregisterPurpose(MultiTileCasingPurpose purpose) { + IMultiBlockController target = getTarget(false); + if (target != null) { + target.unregisterCaseWithPurpose(purpose, this); + } + registeredPurposes.remove(purpose); + } + + @Override + protected void addDebugInfo(EntityPlayer aPlayer, int aLogLevel, ArrayList<String> tList) { + final IMultiBlockController controller = getTarget(false); + if (controller != null) { + tList.add("Has controller"); + } else { + tList.add("No Controller"); + } + tList.add("Casing Mode: " + getModeName(mode)); + } + + @Override + public void getWailaBody(ItemStack itemStack, List<String> currentTip, IWailaDataAccessor accessor, + IWailaConfigHandler config) { + super.getWailaBody(itemStack, currentTip, accessor, config); + currentTip.add(String.format("Mode: %s", getModeName(mode))); + if (modeSelected(FLUID_OUT)) { + if (configurationTank != null && configurationTank.get() != null) { + currentTip.add( + String.format( + "Locked to: %s", + configurationTank.get() + .getLocalizedName())); + } else { + currentTip.add("Locked to: Nothing"); + } + } + } + + public IMultiBlockController getTarget(boolean aCheckValidity) { + if (targetPosition == null) { + return null; + } + + if (!worldObj.blockExists(targetPosition.posX, targetPosition.posY, targetPosition.posZ)) { + return null; + } + final TileEntity te = worldObj.getTileEntity(targetPosition.posX, targetPosition.posY, targetPosition.posZ); + IMultiBlockController target = null; + if (te instanceof IMultiBlockController targetFound) { + target = targetFound; + } else { + targetPosition = null; + return null; + } + + if (aCheckValidity) { + return target != null && target.checkStructure(false) ? target : null; + } + return target; + } + + public void registerCovers(IMultiBlockController controller) { + for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) { + final CoverInfo coverInfo = getCoverInfoAtSide(side); + if (coverInfo.isValid() && coverInfo.getTickRate() > 0) { + controller.registerCoveredPartOnSide(side, this); + } + } + } + + protected void registerPurposes() { + for (MultiTileCasingPurpose purpose : registeredPurposes) { + registerPurpose(purpose); + } + } + + @Override + public void setCoverItemAtSide(ForgeDirection side, ItemStack aCover) { + super.setCoverItemAtSide(side, aCover); + // TODO: Filter on tickable covers + final IMultiBlockController tTarget = getTarget(true); + if (tTarget == null) { + return; + } + + final CoverInfo coverInfo = getCoverInfoAtSide(side); + if (coverInfo.isValid() && coverInfo.getTickRate() > 0) { + tTarget.registerCoveredPartOnSide(side, this); + } + + } + + public void unregisterCovers(IMultiBlockController controller) { + for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) { + if (getCoverInfoAtSide(side).isValid()) { + controller.unregisterCoveredPartOnSide(side, this); + } + } + } + + @Override + public boolean dropCover(ForgeDirection side, ForgeDirection droppedSide, boolean aForced) { + final boolean res = super.dropCover(side, droppedSide, aForced); + final IMultiBlockController tTarget = getTarget(true); + if (tTarget != null) { + tTarget.unregisterCoveredPartOnSide(side, this); + } + return res; + } + + @Override + public void readMultiTileNBT(NBTTagCompound aNBT) { + if (aNBT.hasKey(NBT.ALLOWED_MODES)) allowedModes = aNBT.getInteger(NBT.ALLOWED_MODES); + if (aNBT.hasKey(NBT.MODE)) setMode(aNBT.getByte(NBT.MODE)); + if (aNBT.hasKey(NBT.TARGET)) { + targetPosition = new ChunkCoordinates( + aNBT.getInteger(NBT.TARGET_X), + aNBT.getShort(NBT.TARGET_Y), + aNBT.getInteger(NBT.TARGET_Z)); + } + if (aNBT.hasKey(NBT.LOCKED_INVENTORY)) { + lockedInventory = UUID.fromString(aNBT.getString(NBT.LOCKED_INVENTORY)); + } + if (aNBT.hasKey(NBT.LOCKED_INVENTORY_INDEX)) { + mLockedInventoryIndex = aNBT.getInteger(NBT.LOCKED_INVENTORY_INDEX); + } + if (aNBT.hasKey(NBT.LOCKED_FLUID)) { + configurationTank.readFromNBT(aNBT, NBT.LOCKED_FLUID); + } + if (modeSelected(ITEM_OUT)) { + registeredPurposes.add(MultiTileCasingPurpose.ItemOutput); + } + if (modeSelected(FLUID_OUT)) { + registeredPurposes.add(MultiTileCasingPurpose.FluidOutput); + } + } + + @Override + public void writeMultiTileNBT(NBTTagCompound aNBT) { + if (allowedModes != NOTHING) aNBT.setInteger(NBT.ALLOWED_MODES, allowedModes); + if (mode != 0) aNBT.setInteger(NBT.MODE, mode); + if (targetPosition != null) { + aNBT.setBoolean(NBT.TARGET, true); + aNBT.setInteger(NBT.TARGET_X, targetPosition.posX); + aNBT.setShort(NBT.TARGET_Y, (short) targetPosition.posY); + aNBT.setInteger(NBT.TARGET_Z, targetPosition.posZ); + } + if (lockedInventory != null) { + aNBT.setString(NBT.LOCKED_INVENTORY, lockedInventory.toString()); + } + if (mLockedInventoryIndex != 0) { + aNBT.setInteger(NBT.LOCKED_INVENTORY_INDEX, mLockedInventoryIndex); + } + configurationTank.writeToNBT(aNBT, NBT.LOCKED_FLUID); + } + + @Override + public void setLockedInventoryIndex(int aIndex) { + mLockedInventoryIndex = aIndex; + } + + @Override + public int getLockedInventoryIndex() { + return mLockedInventoryIndex; + } + + @Override + public void setTargetPos(ChunkCoordinates aTargetPos) { + targetPosition = aTargetPos; + IMultiBlockController target = getTarget(false); + setTarget(target, allowedModes); + } + + @Override + public ChunkCoordinates getTargetPos() { + return targetPosition; + } + + @Override + public void setMode(int mode) { + if (this.mode == mode) return; + if (modeSelected(FLUID_OUT)) { + unregisterPurpose(MultiTileCasingPurpose.FluidOutput); + } + if (modeSelected(ITEM_OUT)) { + unregisterPurpose(MultiTileCasingPurpose.ItemOutput); + } + this.mode = mode; + if (modeSelected(FLUID_OUT)) { + registerPurpose(MultiTileCasingPurpose.FluidOutput); + } + if (modeSelected(ITEM_OUT)) { + registerPurpose(MultiTileCasingPurpose.ItemOutput); + } + } + + @Override + public int getMode() { + return mode; + } + + @Override + public int getAllowedModes() { + return allowedModes; + } + + @Override + public void setAllowedModes(int aAllowedModes) { + allowedModes = aAllowedModes; + } + + /** + * True if `aMode` is one of the allowed modes + */ + public boolean hasMode(int aMode) { + // This is not sent to the client + return (allowedModes & aMode) != 0; + } + + /** + * Returns true if the part has any of the modes provided, and that mode is the currently selected mode + */ + public boolean modeSelected(int... aModes) { + for (int aMode : aModes) { + if (hasMode(aMode) && mode == getModeOrdinal(aMode)) return true; + } + return false; + } + + @Override + public boolean breakBlock() { + final IMultiBlockController tTarget = getTarget(false); + if (tTarget != null) { + unregisterCovers(tTarget); + tTarget.onStructureChange(); + } + return false; + } + + @Override + public void onBlockAdded() { + for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) { + final TileEntity te = getTileEntityAtSide(side); + if (te instanceof MultiBlockPart part) { + final IMultiBlockController tController = part.getTarget(false); + if (tController != null) tController.onStructureChange(); + } else if (te instanceof IMultiBlockController controller) { + controller.onStructureChange(); + } + } + } + + @Override + public ITexture getTexture(ForgeDirection side) { + ITexture texture = super.getTexture(side); + if (mode != 0 && side == facing) { + if (mode == getModeOrdinal(ITEM_IN)) { + return TextureFactory.of( + texture, + TextureFactory.of(OVERLAY_PIPE_IN), + TextureFactory.of(ITEM_IN_SIGN), + getCoverTexture(side)); + } + if (mode == getModeOrdinal(ITEM_OUT)) { + return TextureFactory.of( + texture, + TextureFactory.of(OVERLAY_PIPE_OUT), + TextureFactory.of(ITEM_OUT_SIGN), + getCoverTexture(side)); + } + if (mode == getModeOrdinal(FLUID_IN)) { + return TextureFactory.of( + texture, + TextureFactory.of(OVERLAY_PIPE_IN), + TextureFactory.of(FLUID_IN_SIGN), + getCoverTexture(side)); + } + if (mode == getModeOrdinal(FLUID_OUT)) { + return TextureFactory.of( + texture, + TextureFactory.of(OVERLAY_PIPE_OUT), + TextureFactory.of(FLUID_OUT_SIGN), + getCoverTexture(side)); + } + if (mode == getModeOrdinal(ENERGY_IN)) { + return TextureFactory.of(texture, TextureFactory.of(OVERLAY_ENERGY_IN_MULTI), getCoverTexture(side)); + } + if (mode == getModeOrdinal(ENERGY_OUT)) { + return TextureFactory.of(texture, TextureFactory.of(OVERLAY_ENERGY_OUT_MULTI), getCoverTexture(side)); + } + } + + return TextureFactory.of(texture, getCoverTexture(side)); + } + + protected String getModeName(int aMode) { + if (aMode == NOTHING) return "Nothing"; + if (aMode == getModeOrdinal(ITEM_IN)) return "Item Input"; + if (aMode == getModeOrdinal(ITEM_OUT)) return "Item Output"; + if (aMode == getModeOrdinal(FLUID_IN)) return "Fluid Input"; + if (aMode == getModeOrdinal(FLUID_OUT)) return "Fluid Output"; + if (aMode == getModeOrdinal(ENERGY_IN)) return "Energy Input"; + if (aMode == getModeOrdinal(ENERGY_OUT)) return "Energy Output"; + return "Unknown"; + } + + protected byte getModeOrdinal(int aMode) { + // log2 returns the bit position of the only bit set, add 1 to account for 0 being NOTHING + // NOTE: Must be a power of 2 (single bit) + return (byte) (log2(aMode, RoundingMode.UNNECESSARY) + 1); + } + + protected byte getNextAllowedMode(List<Integer> allowedModes) { + if (this.allowedModes == NOTHING) return NOTHING; + + final int numModes = allowedModes.size(); + for (byte i = 1; i <= numModes; i++) { + final byte curMode = (byte) ((mode + i) % numModes); + if (curMode == NOTHING || hasMode(1 << (curMode - 1))) return curMode; + } + // Nothing valid found + return 0; + } + + @Override + public boolean onMalletRightClick(EntityPlayer aPlayer, ItemStack tCurrentItem, ForgeDirection wrenchSide, float aX, + float aY, float aZ) { + if (allowedModes == NOTHING) return true; + if (mode == NOTHING) { + facing = wrenchSide; + } + setMode(getNextAllowedMode(BASIC_MODES)); + if (aPlayer.isSneaking()) { + facing = wrenchSide; + } + GT_Utility.sendChatToPlayer(aPlayer, "Mode set to `" + getModeName(mode) + "' (" + mode + ")"); + sendClientData((EntityPlayerMP) aPlayer); + return true; + } + + @Override + public void setLightValue(byte aLightValue) {} + + @Override + public byte getComparatorValue(ForgeDirection side) { + return 0; + } + + @Override + public String getTileEntityName() { + return "gt.multitileentity.multiblock.part"; + } + + @Override + public boolean shouldTick(long tickTimer) { + return modeSelected(ITEM_OUT, FLUID_OUT); + } + + /** + * TODO: Make sure the energy/item/fluid hatch is facing that way! or has that mode enabled on that side Check + * SIDE_UNKNOWN for or coverbehavior + */ + + // #region Fluid - Depending on the part type - proxy it to the multiblock controller, if we have one + @Override + @Nullable + public FluidInventoryLogic getFluidLogic(@Nonnull ForgeDirection side, @Nonnull InventoryType type) { + if (side != facing && side != ForgeDirection.UNKNOWN) return null; + + if (!modeSelected(FLUID_IN, FLUID_OUT)) return null; + + IMultiBlockController controller = getTarget(false); + if (controller == null) return null; + return controller + .getFluidLogic(modeSelected(FLUID_IN) ? InventoryType.Input : InventoryType.Output, lockedInventory); + } + + // #endregion Fluid + + // #region Energy - Depending on the part type - proxy to the multiblock controller, if we have one + + @Override + @Nonnull + public PowerLogic getPowerLogic(@Nonnull ForgeDirection side) { + if (side != facing && side != ForgeDirection.UNKNOWN) { + return new NullPowerLogic(); + } + + if (!modeSelected(ENERGY_IN, ENERGY_OUT)) { + return new NullPowerLogic(); + } + + final IMultiBlockController controller = getTarget(true); + if (controller == null) { + return new NullPowerLogic(); + } + return controller.getPowerLogic(); + } + + @Override + public boolean isEnetInput() { + return modeSelected(ENERGY_IN); + } + + @Override + public boolean isEnetOutput() { + return modeSelected(ENERGY_OUT); + } + + // #endregion Energy + + // #region Item - Depending on the part type - proxy to the multiblock controller, if we have one + + @Override + @Nullable + public ItemInventoryLogic getItemLogic(@Nonnull ForgeDirection side, @Nonnull InventoryType unused) { + if (side != facing && side != ForgeDirection.UNKNOWN) return null; + + if (!modeSelected(ITEM_IN, ITEM_OUT)) return null; + + final IMultiBlockController controller = getTarget(false); + if (controller == null) return null; + + return controller + .getItemLogic(modeSelected(ITEM_IN) ? InventoryType.Input : InventoryType.Output, lockedInventory); + } + + @Override + @Nullable + public InventoryType getItemInventoryType() { + if (!modeSelected(ITEM_IN, ITEM_OUT)) return InventoryType.Both; + return modeSelected(ITEM_IN) ? InventoryType.Input : InventoryType.Output; + } + + // #endregion Item + + // === Modular UI === + @Override + public boolean useModularUI() { + return true; + } + + @Override + public String getLocalName() { + if (modeSelected(ITEM_IN)) return "Input Inventory"; + if (modeSelected(ITEM_OUT)) return "Output Inventory"; + if (modeSelected(FLUID_IN)) return "Fluid Input Hatch"; + if (modeSelected(FLUID_OUT)) return "Fluid Output Hatch"; + + return "Unknown"; + } + + @Override + public boolean hasGui(ForgeDirection side) { + if (modeSelected(ENERGY_IN, ENERGY_OUT) && facing == side) { + return false; + } + return getTarget(true) != null; + } + + protected boolean isWrongFluid(Fluid fluid) { + if (fluid == null) { + return true; + } + Fluid lockedFluid = getLockedFluid(); + if (lockedFluid != null) { + return !fluid.equals(lockedFluid); + } + return false; + } + + protected Fluid getLockedFluid() { + if (configurationTank.get() != null && configurationTank.get() + .getFluid() != null) { + return configurationTank.get() + .getFluid(); + } + return null; + } + + @Override + public void addUIWidgets(Builder builder, UIBuildContext buildContext) { + super.addUIWidgets(builder, buildContext); + IMultiBlockController controller = getTarget(false); + if (controller == null) { + return; + } + if ((modeSelected(ITEM_IN, ITEM_OUT))) { + builder.widget( + controller + .getItemLogic(modeSelected(ITEM_IN) ? InventoryType.Input : InventoryType.Output, lockedInventory) + .getGuiPart() + .setSize(18 * 4 + 4, 18 * 5) + .setPos(52, 7)); + } + + if ((modeSelected(FLUID_IN, FLUID_OUT))) { + builder.widget( + controller + .getFluidLogic(modeSelected(FLUID_IN) ? InventoryType.Input : InventoryType.Output, lockedInventory) + .getGuiPart() + .setSize(18 * 4 + 4, 18 * 5) + .setPos(52, 7)); + } + } + + protected boolean canOpenControllerGui() { + return true; + } + + @Override + protected int getGUIHeight() { + return super.getGUIHeight() + 20; + } + + @Override + public void addGregTechLogo(Builder builder) { + if (modeSelected(ITEM_IN, ITEM_OUT)) { + builder.widget( + new DrawableWidget().setDrawable(getGUITextureSet().getGregTechLogo()) + .setSize(17, 17) + .setPos(152, 74)); + } else if (modeSelected(FLUID_IN, FLUID_OUT)) { + builder.widget( + new DrawableWidget().setDrawable(getGUITextureSet().getGregTechLogo()) + .setSize(17, 17) + .setPos(152, 82)); + } else { + super.addGregTechLogo(builder); + } + } + + @Override + public void addToolTips(List<String> list, ItemStack stack, boolean f3_h) { + list.add("A MultiTileEntity Casing"); + } + + public String getInventoryName() { + IMultiBlockController controller = getTarget(false); + if (controller == null) return ""; + if (modeSelected(ITEM_IN, ITEM_OUT)) { + InventoryType type = modeSelected(ITEM_IN) ? InventoryType.Input : InventoryType.Output; + ItemInventoryLogic itemLogic = controller.getItemLogic(type, lockedInventory); + return itemLogic.getDisplayName(); + } + if (modeSelected(FLUID_IN, FLUID_OUT)) { + InventoryType type = modeSelected(FLUID_IN) ? InventoryType.Input : InventoryType.Output; + FluidInventoryLogic fluidLogic = controller.getFluidLogic(type, lockedInventory); + return fluidLogic.getDisplayName(); + } + return ""; + } + + @Override + @Nonnull + public ForgeDirection getPowerOutputSide() { + if (!modeSelected(ENERGY_OUT)) return ForgeDirection.UNKNOWN; + return facing; + } + + @Nonnull + protected GUIProvider<?> createGUIProvider() { + return new PartGUIProvider<>(this); + } + + @Override + @Nonnull + public GUIProvider<?> getGUI(@Nonnull UIBuildContext uiContext) { + IMultiBlockController controller = getTarget(false); + if (controller == null) return guiProvider; + if (!modeSelected(NOTHING, ENERGY_IN, ENERGY_OUT)) return guiProvider; + if (!canOpenControllerGui()) return guiProvider; + if (uiContext.getPlayer() + .isSneaking()) return guiProvider; + GUIProvider<?> controllerGUI = controller.getGUI(uiContext); + return controllerGUI; + } + + @Override + public ItemStack getAsItem() { + return MultiTileEntityRegistry.getRegistry(getMultiTileEntityRegistryID()) + .getItem(getMultiTileEntityID()); + } + + @Override + public String getMachineName() { + return StatCollector.translateToLocal(getAsItem().getUnlocalizedName()); + } + +} diff --git a/src/main/java/gregtech/api/multitileentity/multiblock/base/StackableController.java b/src/main/java/gregtech/api/multitileentity/multiblock/base/StackableController.java new file mode 100644 index 0000000000..51feb363dd --- /dev/null +++ b/src/main/java/gregtech/api/multitileentity/multiblock/base/StackableController.java @@ -0,0 +1,128 @@ +package gregtech.api.multitileentity.multiblock.base; + +import net.minecraft.item.ItemStack; + +import com.gtnewhorizon.structurelib.util.Vec3Impl; + +import gregtech.api.logic.MuTEProcessingLogic; + +public abstract class StackableController<C extends StackableController<C, P>, P extends MuTEProcessingLogic<P>> + extends Controller<C, P> { + + protected static String STACKABLE_STOP = "STACKABLE_STOP"; + protected static String STACKABLE_MIDDLE = "STACKABLE_MIDDLE"; + protected static String STACKABLE_START = "STACKABLE_START"; + protected int stackCount = 0; + + /** + * construct implementation for stackable multi-blocks + */ + @Override + public void construct(ItemStack trigger, boolean hintsOnly) { + final int blueprintCount = (trigger.stackSize - 1) + getMinStacks(); + final int stackCount = Math.min(blueprintCount, getMaxStacks()); + + buildState.startBuilding(getStartingStructureOffset()); + buildPiece(getStackableStart(), trigger, hintsOnly, buildState.getCurrentOffset()); + buildState.addOffset(getStartingStackOffset()); + + for (int i = 0; i < stackCount; i++) { + buildPiece(getStackableMiddle(i), trigger, hintsOnly, buildState.getCurrentOffset()); + buildState.addOffset(getPerStackOffset()); + } + if (hasTop()) { + buildState.addOffset(getAfterLastStackOffset()); + buildPiece(getStackableStop(), trigger, hintsOnly, buildState.stopBuilding()); + } else { + buildState.stopBuilding(); + } + } + + /** + * Stackable + * + * @return The minimum number of stacks required for this multi-block to form + */ + public abstract int getMinStacks(); + + /** + * Stackable + * + * @return The maximum number of stacks allowed for this multi-block to form + */ + public abstract int getMaxStacks(); + + /** + * Stackable + * + * @return The starting offset for the first stack + */ + public abstract Vec3Impl getStartingStackOffset(); + + /** + * Stackable + * + * @return The per stack offset + */ + public abstract Vec3Impl getPerStackOffset(); + + /** + * Stackable + * + * @return Whether this structure has a Top/Cap. Defaults to true. + */ + public boolean hasTop() { + return true; + } + + /** + * Stackable + * + * @return Any offset needed after the last stack + */ + public Vec3Impl getAfterLastStackOffset() { + return new Vec3Impl(0, 0, 0); + } + + /** + * checkMachine implementation for stackable multi-blocks + */ + @Override + public boolean checkMachine() { + stackCount = 0; + + buildState.startBuilding(getStartingStructureOffset()); + if (!checkPiece(getStackableStart(), buildState.getCurrentOffset())) return buildState.failBuilding(); + + buildState.addOffset(getStartingStackOffset()); + + for (int i = 0; i < getMaxStacks(); i++) { + if (!checkPiece(getStackableMiddle(i), buildState.getCurrentOffset())) { + break; + } + + buildState.addOffset(getPerStackOffset()); + stackCount++; + + } + if (stackCount < getMinStacks()) return buildState.failBuilding(); + + buildState.addOffset(getAfterLastStackOffset()); + if (!checkPiece(getStackableStop(), buildState.stopBuilding())) { + return buildState.failBuilding(); + } + return super.checkMachine(); + } + + protected String getStackableStop() { + return STACKABLE_STOP; + } + + protected String getStackableMiddle(int stackIndex) { + return STACKABLE_MIDDLE; + } + + protected String getStackableStart() { + return STACKABLE_START; + } +} diff --git a/src/main/java/gregtech/api/multitileentity/multiblock/base/StackableModularController.java b/src/main/java/gregtech/api/multitileentity/multiblock/base/StackableModularController.java new file mode 100644 index 0000000000..1dfd497151 --- /dev/null +++ b/src/main/java/gregtech/api/multitileentity/multiblock/base/StackableModularController.java @@ -0,0 +1,77 @@ +package gregtech.api.multitileentity.multiblock.base; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +import org.jetbrains.annotations.NotNull; + +import gregtech.api.logic.MuTEProcessingLogic; +import gregtech.api.multitileentity.interfaces.UpgradableModularMuTE; +import gregtech.api.util.GT_StructureUtilityMuTE.UpgradeCasings; + +public abstract class StackableModularController<C extends StackableModularController<C, P>, P extends MuTEProcessingLogic<P>> + extends StackableController<C, P> implements UpgradableModularMuTE { + + protected double durationMultiplier = 1; + protected double euTickMultiplier = 1; + + private Map<UpgradeCasings, int[]> mucMap; + + protected @NotNull Map<UpgradeCasings, int[]> getMucMap() { + if (mucMap == null) { + mucMap = createMucMap(); + } + return mucMap; + } + + protected static @NotNull Map<UpgradeCasings, int[]> createMucMap() { + Map<UpgradeCasings, int[]> mucCount = new HashMap<>(); + mucCount.put(UpgradeCasings.Heater, new int[] { 0, 0, 0, 0, 0 }); + mucCount.put(UpgradeCasings.Insulator, new int[] { 0, 0, 0, 0, 0 }); + return mucCount; + } + + @Override + public void increaseMucCount(UpgradeCasings casingType, int tier) { + Map<UpgradeCasings, int[]> mucCounters = getMucMap(); + int[] casingCount = mucCounters.get(casingType); + + switch (tier) { + case 0, 1, 2 -> casingCount[0] += 1; + case 3, 4, 5 -> casingCount[1] += 1; + case 6, 7, 8 -> casingCount[2] += 1; + case 9, 10, 11 -> casingCount[3] += 1; + default -> casingCount[4] += 1; + } + } + + @Override + public void resetMucCount() { + Map<UpgradeCasings, int[]> mucCounters = getMucMap(); + mucCounters.forEach((type, casingCount) -> { Arrays.fill(casingCount, 0); }); + } + + // Returns the cheapest MUC that is possible for the multi, which gets the minimum bonuses. + protected abstract UpgradeCasings getBaseMucType(); + + // Minimum parallel bonus per MUC. Higher tier MUCs multiply with this value for even more parallels. + protected abstract int getParallelFactor(); + + protected void calculateParallels() { + int parallelCount = 0; + int parallelFactor = getParallelFactor(); + int[] parallelCasingList = mucMap.get(getBaseMucType()); + + for (int i = 0; i < 5; i++) { + // (i * 3 + 1) -> Convert MUC tier into minimum GT tier, in groups of 3 (LV, EV, LuV, UHV, UMV) + // If higher than multi tier, upgrade casing has no effect + if (i * 3 + 1 <= tier) { + parallelCount += parallelCasingList[i] * (i + 1) * parallelFactor; + } + } + maxParallel = parallelCount == 0 ? 1 : parallelCount; + } + + protected abstract boolean calculateMucMultipliers(); +} diff --git a/src/main/java/gregtech/api/multitileentity/multiblock/base/WallShareablePart.java b/src/main/java/gregtech/api/multitileentity/multiblock/base/WallShareablePart.java new file mode 100644 index 0000000000..ccde0c49e6 --- /dev/null +++ b/src/main/java/gregtech/api/multitileentity/multiblock/base/WallShareablePart.java @@ -0,0 +1,96 @@ +package gregtech.api.multitileentity.multiblock.base; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.ChunkCoordinates; +import net.minecraftforge.common.util.ForgeDirection; + +import gregtech.api.multitileentity.interfaces.IMultiBlockController; + +public class WallShareablePart extends MultiBlockPart { + + protected List<ChunkCoordinates> targetPositions = new ArrayList<>(); + + @Override + public void setTarget(IMultiBlockController aTarget, int aAllowedModes) { + if (targetPositions.size() >= 1) { + allowedModes = 0; + setMode((byte) 0); + targetPosition = null; + } else { + allowedModes = aAllowedModes; + } + + if (aTarget == null) { + return; + } + + targetPositions.add(aTarget.getCoords()); + } + + @Override + public UUID getLockedInventory() { + if (targetPositions.size() > 1) { + return null; + } + return super.getLockedInventory(); + } + + @Override + public IMultiBlockController getTarget(boolean aCheckValidity) { + if (targetPositions.size() != 1) { + return null; + } + + targetPosition = targetPositions.get(0); + return super.getTarget(aCheckValidity); + } + + @Override + public String getTileEntityName() { + return "gt.multiTileEntity.casing.wallSharable"; + } + + @Override + public boolean breakBlock() { + for (final ChunkCoordinates coordinates : targetPositions) { + IMultiBlockController target = getTarget(coordinates, false); + if (target == null) { + continue; + } + target.onStructureChange(); + } + return false; + } + + @Override + public void onBlockAdded() { + for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) { + final TileEntity te = getTileEntityAtSide(side); + if (te instanceof MultiBlockPart part) { + final IMultiBlockController tController = part.getTarget(false); + if (tController != null) tController.onStructureChange(); + } else if (te instanceof IMultiBlockController controller) { + controller.onStructureChange(); + } + } + } + + public IMultiBlockController getTarget(ChunkCoordinates coordinates, boolean aCheckValidity) { + IMultiBlockController target = null; + if (coordinates == null) return null; + if (worldObj.blockExists(coordinates.posX, coordinates.posY, coordinates.posZ)) { + final TileEntity te = worldObj.getTileEntity(coordinates.posX, coordinates.posY, coordinates.posZ); + if (te instanceof IMultiBlockController) { + target = (IMultiBlockController) te; + } + } + if (aCheckValidity) { + return target != null && target.checkStructure(false) ? target : null; + } + return target; + } +} diff --git a/src/main/java/gregtech/api/multitileentity/multiblock/casing/BasicCasing.java b/src/main/java/gregtech/api/multitileentity/multiblock/casing/BasicCasing.java new file mode 100644 index 0000000000..84f1442a88 --- /dev/null +++ b/src/main/java/gregtech/api/multitileentity/multiblock/casing/BasicCasing.java @@ -0,0 +1,7 @@ +package gregtech.api.multitileentity.multiblock.casing; + +import gregtech.api.multitileentity.multiblock.base.MultiBlockPart; + +public class BasicCasing extends MultiBlockPart { + /* Nothing */ +} diff --git a/src/main/java/gregtech/api/multitileentity/multiblock/casing/CasingBehaviorBase.java b/src/main/java/gregtech/api/multitileentity/multiblock/casing/CasingBehaviorBase.java new file mode 100644 index 0000000000..9f0d9bd2d1 --- /dev/null +++ b/src/main/java/gregtech/api/multitileentity/multiblock/casing/CasingBehaviorBase.java @@ -0,0 +1,10 @@ +package gregtech.api.multitileentity.multiblock.casing; + +/** + * Allows for functional casings that influence the multiblock structure's behavior Examples include: - Extra Byproducts + * - Extra Speed - More parallels - Upgraded internal item/energy/fluid storage - Faster output - Voiding/Anti-Voiding + * upgrade - Ender Upgrades - etc, etc. + * + */ +public class CasingBehaviorBase { +} diff --git a/src/main/java/gregtech/api/multitileentity/multiblock/casing/FunctionalCasing.java b/src/main/java/gregtech/api/multitileentity/multiblock/casing/FunctionalCasing.java new file mode 100644 index 0000000000..bc3c857fd6 --- /dev/null +++ b/src/main/java/gregtech/api/multitileentity/multiblock/casing/FunctionalCasing.java @@ -0,0 +1,29 @@ +package gregtech.api.multitileentity.multiblock.casing; + +import net.minecraft.nbt.NBTTagCompound; + +import gregtech.api.enums.GT_Values; +import gregtech.api.multitileentity.multiblock.base.MultiBlockPart; + +public abstract class FunctionalCasing extends MultiBlockPart { + + private int tier = 0; + + @Override + public int getPartTier() { + return tier; + } + + public abstract float getPartModifier(); + + @Override + public void readMultiTileNBT(NBTTagCompound nbt) { + super.readMultiTileNBT(nbt); + tier = nbt.getInteger(GT_Values.NBT.TIER); + } + + @Override + public void writeMultiTileNBT(NBTTagCompound nbt) { + super.writeMultiTileNBT(nbt); + } +} diff --git a/src/main/java/gregtech/api/multitileentity/multiblock/casing/Glasses.java b/src/main/java/gregtech/api/multitileentity/multiblock/casing/Glasses.java new file mode 100644 index 0000000000..edc1bd0e5b --- /dev/null +++ b/src/main/java/gregtech/api/multitileentity/multiblock/casing/Glasses.java @@ -0,0 +1,32 @@ +package gregtech.api.multitileentity.multiblock.casing; + +import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlockUnlocalizedName; +import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofChain; +import static gregtech.api.enums.Mods.BartWorks; +import static gregtech.api.enums.Mods.Botania; +import static gregtech.api.enums.Mods.IndustrialCraft2; +import static gregtech.api.enums.Mods.Thaumcraft; + +import com.gtnewhorizon.structurelib.structure.IStructureElementChain; + +public class Glasses { + + /** support all Bart, Botania, Ic2, Thaumcraft glasses for multiblock structure **/ + public static <T> IStructureElementChain<T> chainAllGlasses() { + return ofChain( + // IndustrialCraft2 glass + ofBlockUnlocalizedName(IndustrialCraft2.ID, "blockAlloyGlass", 0, true), + + // Botania glass + ofBlockUnlocalizedName(Botania.ID, "manaGlass", 0, false), + ofBlockUnlocalizedName(Botania.ID, "elfGlass", 0, false), + + // BartWorks glass + ofBlockUnlocalizedName(BartWorks.ID, "BW_GlasBlocks", 0, true), + ofBlockUnlocalizedName(BartWorks.ID, "BW_GlasBlocks2", 0, true), + + // warded glass + ofBlockUnlocalizedName(Thaumcraft.ID, "blockCosmeticOpaque", 2, false)); + } + +} diff --git a/src/main/java/gregtech/api/multitileentity/multiblock/casing/UpgradeCasing.java b/src/main/java/gregtech/api/multitileentity/multiblock/casing/UpgradeCasing.java new file mode 100644 index 0000000000..fb045557e4 --- /dev/null +++ b/src/main/java/gregtech/api/multitileentity/multiblock/casing/UpgradeCasing.java @@ -0,0 +1,35 @@ +package gregtech.api.multitileentity.multiblock.casing; + +import net.minecraft.nbt.NBTTagCompound; + +import gregtech.api.enums.GT_Values; +import gregtech.api.multitileentity.interfaces.IMultiBlockController; +import gregtech.api.multitileentity.multiblock.base.MultiBlockPart; + +public abstract class UpgradeCasing extends MultiBlockPart { + + protected int tier = 0; + + @Override + public int getPartTier() { + return tier; + } + + @Override + public void setTarget(IMultiBlockController newTarget, int aAllowedModes) { + super.setTarget(newTarget, aAllowedModes); + + if (getTarget(false) != null) { + customWork(getTarget(false)); + } + } + + @Override + public void readMultiTileNBT(NBTTagCompound aNBT) { + super.readMultiTileNBT(aNBT); + tier = aNBT.getInteger(GT_Values.NBT.TIER); + } + + protected abstract void customWork(IMultiBlockController aTarget); + +} diff --git a/src/main/java/gregtech/api/net/GT_Packet.java b/src/main/java/gregtech/api/net/GT_Packet.java new file mode 100644 index 0000000000..d06ea7d0d3 --- /dev/null +++ b/src/main/java/gregtech/api/net/GT_Packet.java @@ -0,0 +1,59 @@ +package gregtech.api.net; + +import net.minecraft.network.INetHandler; +import net.minecraft.world.IBlockAccess; + +import com.google.common.io.ByteArrayDataInput; + +import io.netty.buffer.ByteBuf; + +/** + * @deprecated Use {@link GT_Packet_New} instead + */ +@Deprecated +public abstract class GT_Packet { + + public GT_Packet(boolean aIsReference) { + // + } + + /** + * I use constant IDs instead of Dynamic ones, since that is much more fail safe + * + * @return a Packet ID for this Class + */ + public abstract byte getPacketID(); + + /** + * @return encoded byte Stream + * @deprecated Use {@link #encode(ByteBuf)} instead + */ + @Deprecated + public abstract byte[] encode(); + + /** + * Encode the data into given byte buffer without creating an intermediate byte array. Default implementation just + * throw {@link UnsupportedOperationException}. + */ + public void encode(ByteBuf aOut) { + throw new UnsupportedOperationException(); + } + + /** + * @return encoded byte Stream + */ + public abstract GT_Packet decode(ByteArrayDataInput aData); + + /** + * Process the packet + * + * @param aWorld null if message is received on server side, the client world if message is received on client side + */ + public abstract void process(IBlockAccess aWorld); + + /** + * This will be called just before {@link #process(IBlockAccess)} to inform the handler about the source and type of + * connection + */ + public void setINetHandler(INetHandler aHandler) {} +} diff --git a/src/main/java/gregtech/api/net/GT_Packet_Block_Event.java b/src/main/java/gregtech/api/net/GT_Packet_Block_Event.java new file mode 100644 index 0000000000..98dc8a5c4f --- /dev/null +++ b/src/main/java/gregtech/api/net/GT_Packet_Block_Event.java @@ -0,0 +1,63 @@ +package gregtech.api.net; + +import net.minecraft.tileentity.TileEntity; +import net.minecraft.world.IBlockAccess; + +import com.google.common.io.ByteArrayDataInput; + +import io.netty.buffer.ByteBuf; + +/** + * Used to transfer Block Events in a much better fashion + */ +public class GT_Packet_Block_Event extends GT_Packet_New { + + private int mX, mZ; + private short mY; + private byte mID, mValue; + + public GT_Packet_Block_Event() { + super(true); + } + + public GT_Packet_Block_Event(int aX, short aY, int aZ, byte aID, byte aValue) { + super(false); + mX = aX; + mY = aY; + mZ = aZ; + mID = aID; + mValue = aValue; + } + + @Override + public void encode(ByteBuf aOut) { + aOut.writeInt(mX); + aOut.writeShort(mY); + aOut.writeInt(mZ); + aOut.writeByte(mID); + aOut.writeByte(mValue); + } + + @Override + public GT_Packet_New decode(ByteArrayDataInput aData) { + return new GT_Packet_Block_Event( + aData.readInt(), + aData.readShort(), + aData.readInt(), + aData.readByte(), + aData.readByte()); + } + + @Override + public void process(IBlockAccess aWorld) { + if (aWorld != null) { + final TileEntity tTileEntity = aWorld.getTileEntity(mX, mY, mZ); + if (tTileEntity != null) tTileEntity.receiveClientEvent(mID, mValue); + } + } + + @Override + public byte getPacketID() { + return 2; + } +} diff --git a/src/main/java/gregtech/api/net/GT_Packet_ClientPreference.java b/src/main/java/gregtech/api/net/GT_Packet_ClientPreference.java new file mode 100644 index 0000000000..0835676c6f --- /dev/null +++ b/src/main/java/gregtech/api/net/GT_Packet_ClientPreference.java @@ -0,0 +1,62 @@ +package gregtech.api.net; + +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.network.INetHandler; +import net.minecraft.network.NetHandlerPlayServer; +import net.minecraft.world.IBlockAccess; + +import com.google.common.io.ByteArrayDataInput; + +import gregtech.GT_Mod; +import gregtech.api.util.GT_ClientPreference; +import io.netty.buffer.ByteBuf; + +public class GT_Packet_ClientPreference extends GT_Packet_New { + + private GT_ClientPreference mPreference; + private EntityPlayerMP mPlayer; + + public GT_Packet_ClientPreference() { + super(true); + } + + public GT_Packet_ClientPreference(GT_ClientPreference mPreference) { + super(false); + this.mPreference = mPreference; + } + + @Override + public byte getPacketID() { + return 9; + } + + @Override + public void setINetHandler(INetHandler aHandler) { + if (aHandler instanceof NetHandlerPlayServer) { + mPlayer = ((NetHandlerPlayServer) aHandler).playerEntity; + } + } + + @Override + public void process(IBlockAccess aWorld) { + if (mPlayer != null) GT_Mod.gregtechproxy.setClientPreference(mPlayer.getUniqueID(), mPreference); + } + + @Override + public void encode(ByteBuf aOut) { + aOut.writeBoolean(mPreference.isSingleBlockInitialFilterEnabled()); + aOut.writeBoolean(mPreference.isSingleBlockInitialMultiStackEnabled()); + aOut.writeBoolean(mPreference.isInputBusInitialFilterEnabled()); + aOut.writeBoolean(mPreference.isWailaAverageNSEnabled()); + } + + @Override + public GT_Packet_New decode(ByteArrayDataInput aData) { + return new GT_Packet_ClientPreference( + new GT_ClientPreference( + aData.readBoolean(), + aData.readBoolean(), + aData.readBoolean(), + aData.readBoolean())); + } +} diff --git a/src/main/java/gregtech/api/net/GT_Packet_GtTileEntityGuiRequest.java b/src/main/java/gregtech/api/net/GT_Packet_GtTileEntityGuiRequest.java new file mode 100644 index 0000000000..dc2f88316d --- /dev/null +++ b/src/main/java/gregtech/api/net/GT_Packet_GtTileEntityGuiRequest.java @@ -0,0 +1,122 @@ +package gregtech.api.net; + +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.world.IBlockAccess; +import net.minecraft.world.World; +import net.minecraftforge.common.DimensionManager; +import net.minecraftforge.common.util.ForgeDirection; + +import com.google.common.io.ByteArrayDataInput; + +import gregtech.api.enums.GT_Values; +import gregtech.api.gui.modularui.GT_UIInfos; +import gregtech.api.metatileentity.BaseTileEntity; +import gregtech.api.metatileentity.CoverableTileEntity; +import gregtech.common.GT_Proxy; +import io.netty.buffer.ByteBuf; + +/** + * Client -> Server: Request that the server opens a Gregtech GUI for us after providing us with the required data. + */ +public class GT_Packet_GtTileEntityGuiRequest extends GT_Packet_New { + + protected int mX; + protected short mY; + protected int mZ; + + protected int guiId; + protected int dimId, playerId; + + protected int parentGuiId; + + public GT_Packet_GtTileEntityGuiRequest() { + super(true); + } + + public GT_Packet_GtTileEntityGuiRequest(int mX, short mY, int mZ, int guiId, int dimID, int playerID, + int parentGuiId) { + super(false); + this.mX = mX; + this.mY = mY; + this.mZ = mZ; + + this.guiId = guiId; + + this.dimId = dimID; + this.playerId = playerID; + + this.parentGuiId = parentGuiId; + } + + public GT_Packet_GtTileEntityGuiRequest(int mX, short mY, int mZ, int guiId, int dimID, int playerID) { + this(mX, mY, mZ, guiId, dimID, playerID, -1); + } + + @Override + public void encode(ByteBuf aOut) { + aOut.writeInt(mX); + aOut.writeShort(mY); + aOut.writeInt(mZ); + + aOut.writeInt(guiId); + + aOut.writeInt(dimId); + aOut.writeInt(playerId); + + aOut.writeInt(parentGuiId); + } + + @Override + public GT_Packet_New decode(ByteArrayDataInput aData) { + return new GT_Packet_GtTileEntityGuiRequest( + aData.readInt(), + aData.readShort(), + aData.readInt(), + aData.readInt(), + aData.readInt(), + aData.readInt(), + aData.readInt()); + } + + @Override + public byte getPacketID() { + return 15; + } + + @Override + public void process(IBlockAccess aWorld) { + final World world = DimensionManager.getWorld(this.dimId); + if (world == null) return; + final TileEntity tile = world.getTileEntity(this.mX, this.mY, this.mZ); + if (!(tile instanceof BaseTileEntity baseTile) || baseTile.isDead()) return; + + final EntityPlayerMP player = (EntityPlayerMP) world.getEntityByID(playerId); + final CoverableTileEntity coverableTile = (baseTile instanceof CoverableTileEntity) + ? (CoverableTileEntity) baseTile + : null; + // If the requested Gui ID corresponds to a cover, send the cover data to the client so they can open it. + if (GT_Proxy.GUI_ID_COVER_SIDE_BASE <= guiId && guiId < GT_Proxy.GUI_ID_COVER_SIDE_BASE + 6 + && coverableTile != null) { + final ForgeDirection coverSide = ForgeDirection + .getOrientation((byte) (guiId - GT_Proxy.GUI_ID_COVER_SIDE_BASE)); + final GT_Packet_TileEntityCoverGUI packet = new GT_Packet_TileEntityCoverGUI( + this.mX, + this.mY, + this.mZ, + coverSide, + coverableTile.getCoverIDAtSide(coverSide), + coverableTile.getComplexCoverDataAtSide(coverSide), + this.dimId, + this.playerId, + parentGuiId); + GT_Values.NW.sendToPlayer(packet, player); + } else if (guiId == 0) { + if (baseTile.useModularUI()) { + GT_UIInfos.openGTTileEntityUI(baseTile, player); + } else { + baseTile.openGUI(player); + } + } + } +} diff --git a/src/main/java/gregtech/api/net/GT_Packet_MultiTileEntity.java b/src/main/java/gregtech/api/net/GT_Packet_MultiTileEntity.java new file mode 100644 index 0000000000..096f21df29 --- /dev/null +++ b/src/main/java/gregtech/api/net/GT_Packet_MultiTileEntity.java @@ -0,0 +1,254 @@ +package gregtech.api.net; + +import static gregtech.api.enums.GT_Values.B; + +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +import net.minecraft.world.IBlockAccess; + +import com.google.common.io.ByteArrayDataInput; + +import gregtech.api.net.data.CasingData; +import gregtech.api.net.data.CommonData; +import gregtech.api.net.data.CoordinateData; +import gregtech.api.net.data.MultiTileEntityData; +import gregtech.api.net.data.MultiTileEntityProcess; +import gregtech.api.net.data.PacketData; +import io.netty.buffer.ByteBuf; + +public class GT_Packet_MultiTileEntity extends GT_Packet_New { + + private final Set<PacketData<MultiTileEntityProcess>> data = new HashSet<>(); + public static final int COVERS = B[0], REDSTONE = B[1], MODES = B[2], CONTROLLER = B[3], INVENTORY_INDEX = B[4], + INVENTORY_NAME_ID = B[5], BOOLEANS = B[6], SOUND = B[7]; + + public GT_Packet_MultiTileEntity(boolean reference) { + super(reference); + } + + @Override + public void encode(ByteBuf aOut) { + Set<PacketData<MultiTileEntityProcess>> set = data.stream() + .sorted() + .collect( + HashSet<PacketData<MultiTileEntityProcess>>::new, + HashSet<PacketData<MultiTileEntityProcess>>::add, + HashSet<PacketData<MultiTileEntityProcess>>::addAll); + clearData(); + data.addAll(set); + int features = 0; + for (PacketData<MultiTileEntityProcess> data : data) { + features |= 1 << data.getId(); + } + + aOut.writeInt(features); + + for (PacketData<MultiTileEntityProcess> data : data) { + data.encode(aOut); + } + /* + * TODO Move to new system + * if ((features & COVERS) == COVERS) { + * aOut.writeInt(mC0); + * aOut.writeInt(mC1); + * aOut.writeInt(mC2); + * aOut.writeInt(mC3); + * aOut.writeInt(mC4); + * aOut.writeInt(mC5); + * } + * if ((features & MODES) == MODES) { + * aOut.writeInt(mode); + * aOut.writeInt(allowedModes); + * } + * if ((features & CONTROLLER) == CONTROLLER) { + * aOut.writeInt(mTargetPos.posX); + * aOut.writeShort(mTargetPos.posY); + * aOut.writeInt(mTargetPos.posZ); + * } + * if ((features & INVENTORY_INDEX) == INVENTORY_INDEX) { + * aOut.writeInt(mLockedInventoryIndex); + * } + * if ((features & INVENTORY_NAME_ID) == INVENTORY_NAME_ID) { + * if (mInventoryName != null && mInventoryName.length() > 0) { + * byte[] bytes = mInventoryName.getBytes(); + * aOut.writeInt(bytes.length); + * aOut.writeBytes(bytes); + * } else { + * aOut.writeInt(0); + * } + * if (inventoryID != null && inventoryID.length() > 0) { + * byte[] bytes = inventoryID.getBytes(); + * aOut.writeInt(bytes.length); + * aOut.writeBytes(bytes); + * } else { + * aOut.writeInt(0); + * } + * } + * if ((features & BOOLEANS) == BOOLEANS) { + * aOut.writeInt(booleans); + * } + * if ((features & SOUND) == SOUND) { + * aOut.writeByte(soundEvent); + * aOut.writeInt(soundEventValue); + * } + */ + } + + @Override + public GT_Packet_New decode(ByteArrayDataInput in) { + Objects.requireNonNull(in); + final int packetFeatures = in.readInt(); + + final GT_Packet_MultiTileEntity packet = new GT_Packet_MultiTileEntity(false); + + if (containsBit(packetFeatures, CoordinateData.COORDINATE_DATA_ID)) { + packet.addData(new CoordinateData()); + } + if (containsBit(packetFeatures, MultiTileEntityData.MULTI_TILE_ENTITY_DATA_ID)) { + packet.addData(new MultiTileEntityData()); + } + if (containsBit(packetFeatures, CommonData.COMMON_DATA_ID)) { + packet.addData(new CommonData()); + } + if (containsBit(packetFeatures, CasingData.CASING_DATA_ID)) { + packet.addData(new CasingData()); + } + + Set<PacketData<MultiTileEntityProcess>> set = packet.data.stream() + .sorted() + .collect( + HashSet<PacketData<MultiTileEntityProcess>>::new, + HashSet<PacketData<MultiTileEntityProcess>>::add, + HashSet<PacketData<MultiTileEntityProcess>>::addAll); + packet.clearData(); + packet.data.addAll(set); + for (PacketData<MultiTileEntityProcess> data : packet.data) { + data.decode(in); + } + /* + * if ((packetFeatures & COVERS) == COVERS) { + * packet.setCoverData( + * in.readInt(), + * in.readInt(), + * in.readInt(), + * in.readInt(), + * in.readInt(), + * in.readInt()); + * } + * if ((packetFeatures & INVENTORY_INDEX) == INVENTORY_INDEX) { + * packet.setInventoryIndex(aData.readInt()); + * } + * if ((packetFeatures & INVENTORY_NAME_ID) == INVENTORY_NAME_ID) { + * int nameLength = aData.readInt(); + * String inventoryName; + * if (nameLength > 0) { + * byte[] bytes = new byte[nameLength]; + * for (int i = 0; i < nameLength; i++) { + * bytes[i] = aData.readByte(); + * } + * inventoryName = new String(bytes); + * } else { + * inventoryName = null; + * } + * int idLength = aData.readInt(); + * String inventoryID; + * if (idLength > 0) { + * byte[] bytes = new byte[idLength]; + * for (int i = 0; i < idLength; i++) { + * bytes[i] = aData.readByte(); + * } + * inventoryID = new String(bytes); + * } else { + * inventoryID = null; + * } + * packet.setInventoryName(inventoryName, inventoryID); + * } + * if ((packetFeatures & BOOLEANS) == BOOLEANS) { + * packet.setBooleans(aData.readInt()); + * } + * if ((packetFeatures & SOUND) == SOUND) { + * packet.setSoundEvent(aData.readByte(), aData.readInt()); + * } + */ + return packet; + } + + @Override + public void process(IBlockAccess aWorld) { + if (aWorld == null) return; + MultiTileEntityProcess process = new MultiTileEntityProcess(aWorld); + for (PacketData<MultiTileEntityProcess> data : data) { + data.process(process); + } + process.process(); + /* + * final TileEntity tTileEntity = aWorld.getTileEntity(mX, mY, mZ); + * try { + * final Block tBlock = aWorld.getBlock(mX, mY, mZ); + * if (tBlock instanceof MultiTileEntityBlock mteBlock) { + * final IMultiTileEntity mte = mteBlock.receiveMultiTileEntityData(aWorld, mX, mY, mZ, mRID, mID); + * if (mte == null) return; + * mte.receiveClientData(GregTechTileClientEvents.CHANGE_COMMON_DATA, mCommonData); + * mte.receiveClientData(GregTechTileClientEvents.CHANGE_COLOR, mColor); + * if ((features & COVERS) == COVERS) { + * mteBlock.receiveCoverData(mte, mC0, mC1, mC2, mC3, mC4, mC5); + * } + * if ((features & REDSTONE) == REDSTONE) { + * mte.receiveClientData(GregTechTileClientEvents.CHANGE_REDSTONE_OUTPUT, mRedstone); + * } + * if ((features & MODES) == MODES && mte instanceof IMultiTileEntity.IMTE_HasModes mteModes) { + * mteModes.setMode(mode); + * mteModes.setAllowedModes(allowedModes); + * } + * if ((features & INVENTORY_NAME_ID) == INVENTORY_NAME_ID && mte instanceof Inventory invUpg) { + * invUpg.setInventoryName(mInventoryName); + * invUpg.setInventoryId(inventoryID); + * } + * if ((features & CONTROLLER) == CONTROLLER && mte instanceof IMultiBlockPart) { + * final IMultiBlockPart mtePart = (IMultiBlockPart) mte; + * mtePart.setTargetPos(mTargetPos); + * } + * if ((features & INVENTORY_INDEX) == INVENTORY_INDEX && mte instanceof IMultiBlockPart) { + * final IMultiBlockPart mtePart = (IMultiBlockPart) mte; + * mtePart.setLockedInventoryIndex(mLockedInventoryIndex); + * } + * if ((features & BOOLEANS) == BOOLEANS && mte instanceof IMultiTileMachine) { + * final IMultiTileMachine machine = (IMultiTileMachine) mte; + * machine.setBooleans(booleans); + * } + * if ((features & SOUND) == SOUND && mte instanceof IMultiTileMachine) { + * final IMultiTileMachine machine = (IMultiTileMachine) mte; + * machine.setSound(soundEvent, soundEventValue); + * } + * } + * } catch (Exception e) { + * e.printStackTrace(); + * GT_Mod.GT_FML_LOGGER.error( + * "Exception setting tile entity data for tile entity {} at ({}, {}, {})", + * tTileEntity, + * mX, + * mY, + * mZ); + * } + */ + } + + @Override + public byte getPacketID() { + return 18; + } + + public void clearData() { + data.clear(); + } + + public void addData(PacketData<MultiTileEntityProcess> data) { + this.data.add(data); + } + + private static boolean containsBit(int toCheck, int bit) { + return (toCheck & (1 << bit)) > 0; + } +} diff --git a/src/main/java/gregtech/api/net/GT_Packet_New.java b/src/main/java/gregtech/api/net/GT_Packet_New.java new file mode 100644 index 0000000000..41eb1740b3 --- /dev/null +++ b/src/main/java/gregtech/api/net/GT_Packet_New.java @@ -0,0 +1,30 @@ +package gregtech.api.net; + +import com.google.common.io.ByteArrayDataInput; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; + +@SuppressWarnings("deprecation") +public abstract class GT_Packet_New extends GT_Packet { + + public GT_Packet_New(boolean aIsReference) { + super(aIsReference); + } + + @Override + @Deprecated + public final byte[] encode() { + final ByteBuf tOut = Unpooled.buffer(); + encode(tOut); + final byte[] bytes = new byte[tOut.readableBytes()]; + tOut.readBytes(bytes); + return bytes; + } + + @Override + public abstract void encode(ByteBuf aOut); + + @Override + public abstract GT_Packet_New decode(ByteArrayDataInput aData); +} diff --git a/src/main/java/gregtech/api/net/GT_Packet_Pollution.java b/src/main/java/gregtech/api/net/GT_Packet_Pollution.java new file mode 100644 index 0000000000..c298af2db4 --- /dev/null +++ b/src/main/java/gregtech/api/net/GT_Packet_Pollution.java @@ -0,0 +1,47 @@ +package gregtech.api.net; + +import net.minecraft.world.ChunkCoordIntPair; +import net.minecraft.world.IBlockAccess; + +import com.google.common.io.ByteArrayDataInput; + +import gregtech.common.GT_Client; +import io.netty.buffer.ByteBuf; + +public class GT_Packet_Pollution extends GT_Packet_New { + + private ChunkCoordIntPair chunk; + private int pollution; + + public GT_Packet_Pollution() { + super(true); + } + + public GT_Packet_Pollution(ChunkCoordIntPair chunk, int pollution) { + super(false); + this.chunk = chunk; + this.pollution = pollution; + } + + @Override + public void encode(ByteBuf aOut) { + aOut.writeInt(chunk.chunkXPos) + .writeInt(chunk.chunkZPos) + .writeInt(pollution); + } + + @Override + public GT_Packet_New decode(ByteArrayDataInput aData) { + return new GT_Packet_Pollution(new ChunkCoordIntPair(aData.readInt(), aData.readInt()), aData.readInt()); + } + + @Override + public void process(IBlockAccess aWorld) { + GT_Client.recieveChunkPollutionPacket(chunk, pollution); + } + + @Override + public byte getPacketID() { + return 4; + } +} diff --git a/src/main/java/gregtech/api/net/GT_Packet_RequestCoverData.java b/src/main/java/gregtech/api/net/GT_Packet_RequestCoverData.java new file mode 100644 index 0000000000..94ae86c2d9 --- /dev/null +++ b/src/main/java/gregtech/api/net/GT_Packet_RequestCoverData.java @@ -0,0 +1,113 @@ +package gregtech.api.net; + +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.network.INetHandler; +import net.minecraft.network.NetHandlerPlayServer; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.world.IBlockAccess; +import net.minecraft.world.World; +import net.minecraftforge.common.DimensionManager; +import net.minecraftforge.common.util.ForgeDirection; + +import com.google.common.io.ByteArrayDataInput; + +import gregtech.api.interfaces.tileentity.ICoverable; +import gregtech.api.metatileentity.CoverableTileEntity; +import gregtech.common.covers.CoverInfo; +import io.netty.buffer.ByteBuf; + +/** + * Client -> Server : ask for cover data + */ +public class GT_Packet_RequestCoverData extends GT_Packet_New { + + protected int mX; + protected short mY; + protected int mZ; + + protected ForgeDirection side; + protected int coverID; + + protected EntityPlayerMP mPlayer; + + public GT_Packet_RequestCoverData() { + super(true); + } + + public GT_Packet_RequestCoverData(CoverInfo info, ICoverable tile) { + super(false); + this.mX = tile.getXCoord(); + this.mY = tile.getYCoord(); + this.mZ = tile.getZCoord(); + + this.side = info.getSide(); + this.coverID = info.getCoverID(); + } + + public GT_Packet_RequestCoverData(int mX, short mY, int mZ, ForgeDirection coverSide, int coverID) { + super(false); + this.mX = mX; + this.mY = mY; + this.mZ = mZ; + + this.side = coverSide; + this.coverID = coverID; + } + + public GT_Packet_RequestCoverData(ForgeDirection coverSide, int coverID, ICoverable tile) { + super(false); + this.mX = tile.getXCoord(); + this.mY = tile.getYCoord(); + this.mZ = tile.getZCoord(); + + this.side = coverSide; + this.coverID = coverID; + } + + @Override + public byte getPacketID() { + return 17; + } + + @Override + public void encode(ByteBuf aOut) { + aOut.writeInt(mX); + aOut.writeShort(mY); + aOut.writeInt(mZ); + + aOut.writeByte(side.ordinal()); + aOut.writeInt(coverID); + } + + @Override + public GT_Packet_New decode(ByteArrayDataInput aData) { + return new GT_Packet_RequestCoverData( + aData.readInt(), + aData.readShort(), + aData.readInt(), + ForgeDirection.getOrientation(aData.readByte()), + aData.readInt()); + } + + @Override + public void setINetHandler(INetHandler aHandler) { + if (aHandler instanceof NetHandlerPlayServer) { + mPlayer = ((NetHandlerPlayServer) aHandler).playerEntity; + } + } + + @Override + public void process(IBlockAccess aWorld) { + // impossible, but who knows + if (mPlayer == null) return; + final World world = DimensionManager.getWorld(mPlayer.dimension); + if (world != null) { + final TileEntity tile = world.getTileEntity(mX, mY, mZ); + if (tile instanceof CoverableTileEntity te) { + if (!te.isDead() && te.getCoverIDAtSide(side) == coverID) { + te.issueCoverUpdate(side); + } + } + } + } +} diff --git a/src/main/java/gregtech/api/net/GT_Packet_SendCoverData.java b/src/main/java/gregtech/api/net/GT_Packet_SendCoverData.java new file mode 100644 index 0000000000..47f549b5b4 --- /dev/null +++ b/src/main/java/gregtech/api/net/GT_Packet_SendCoverData.java @@ -0,0 +1,107 @@ +package gregtech.api.net; + +import net.minecraft.tileentity.TileEntity; +import net.minecraft.world.IBlockAccess; +import net.minecraftforge.common.util.ForgeDirection; + +import com.google.common.io.ByteArrayDataInput; + +import gregtech.api.GregTech_API; +import gregtech.api.interfaces.tileentity.ICoverable; +import gregtech.api.metatileentity.CoverableTileEntity; +import gregtech.api.util.ISerializableObject; +import gregtech.common.covers.CoverInfo; +import io.netty.buffer.ByteBuf; + +/** + * Server -> Client : Update cover data + */ +public class GT_Packet_SendCoverData extends GT_Packet_New { + + protected int mX; + protected short mY; + protected int mZ; + + protected ForgeDirection side; + protected int coverID; + protected ISerializableObject coverData; + + public GT_Packet_SendCoverData() { + super(true); + } + + public GT_Packet_SendCoverData(int mX, short mY, int mZ, ForgeDirection coverSide, int coverID, + ISerializableObject coverData) { + super(false); + this.mX = mX; + this.mY = mY; + this.mZ = mZ; + + this.side = coverSide; + this.coverID = coverID; + this.coverData = coverData; + } + + public GT_Packet_SendCoverData(CoverInfo info, ICoverable tile) { + super(false); + this.mX = tile.getXCoord(); + this.mY = tile.getYCoord(); + this.mZ = tile.getZCoord(); + + this.side = info.getSide(); + this.coverID = info.getCoverID(); + this.coverData = info.getCoverData(); + } + + public GT_Packet_SendCoverData(ForgeDirection coverSide, int coverID, ISerializableObject coverData, + ICoverable tile) { + super(false); + this.mX = tile.getXCoord(); + this.mY = tile.getYCoord(); + this.mZ = tile.getZCoord(); + + this.side = coverSide; + this.coverID = coverID; + this.coverData = coverData; + } + + @Override + public byte getPacketID() { + return 16; + } + + @Override + public void encode(ByteBuf aOut) { + aOut.writeInt(mX); + aOut.writeShort(mY); + aOut.writeInt(mZ); + + aOut.writeByte(side.ordinal()); + aOut.writeInt(coverID); + coverData.writeToByteBuf(aOut); + } + + @Override + public GT_Packet_New decode(ByteArrayDataInput aData) { + final int coverId; + return new GT_Packet_SendCoverData( + aData.readInt(), + aData.readShort(), + aData.readInt(), + ForgeDirection.getOrientation(aData.readByte()), + coverId = aData.readInt(), + GregTech_API.getCoverBehaviorNew(coverId) + .createDataObject() + .readFromPacket(aData, null)); + } + + @Override + public void process(IBlockAccess aWorld) { + if (aWorld != null) { + final TileEntity tile = aWorld.getTileEntity(mX, mY, mZ); + if (tile instanceof CoverableTileEntity coverable && !coverable.isDead()) { + coverable.receiveCoverData(side, coverID, coverData, null); + } + } + } +} diff --git a/src/main/java/gregtech/api/net/GT_Packet_SendOregenPattern.java b/src/main/java/gregtech/api/net/GT_Packet_SendOregenPattern.java new file mode 100644 index 0000000000..d03fd1d7f0 --- /dev/null +++ b/src/main/java/gregtech/api/net/GT_Packet_SendOregenPattern.java @@ -0,0 +1,56 @@ +package gregtech.api.net; + +import net.minecraft.world.IBlockAccess; + +import com.google.common.io.ByteArrayDataInput; + +import gregtech.api.util.GT_Log; +import gregtech.common.GT_Worldgenerator; +import gregtech.common.GT_Worldgenerator.OregenPattern; +import io.netty.buffer.ByteBuf; + +public class GT_Packet_SendOregenPattern extends GT_Packet_New { + + protected OregenPattern pattern = OregenPattern.AXISSYMMETRICAL; + + public GT_Packet_SendOregenPattern() { + super(true); + } + + public GT_Packet_SendOregenPattern(OregenPattern pattern) { + super(false); + this.pattern = pattern; + } + + @Override + public void encode(ByteBuf aOut) { + aOut.writeInt(this.pattern.ordinal()); + } + + @Override + public GT_Packet_New decode(ByteArrayDataInput aData) { + int ordinal = aData.readInt(); + // make sure we get valid data: + if (ordinal >= 0 && ordinal < OregenPattern.values().length) { + return new GT_Packet_SendOregenPattern(OregenPattern.values()[ordinal]); + } + // invalid data, default to AXISSYMMETRICAL: + GT_Log.err.println( + String.format( + "Received invalid data! Received %d but value must be between 0 and %d! Default (0) will be used.", + ordinal, + OregenPattern.values().length - 1)); + return new GT_Packet_SendOregenPattern(); + } + + @Override + public byte getPacketID() { + return 19; + } + + @Override + public void process(IBlockAccess aWorld) { + GT_Worldgenerator.oregenPattern = this.pattern; + } + +} diff --git a/src/main/java/gregtech/api/net/GT_Packet_SetConfigurationCircuit.java b/src/main/java/gregtech/api/net/GT_Packet_SetConfigurationCircuit.java new file mode 100644 index 0000000000..b2d9a59438 --- /dev/null +++ b/src/main/java/gregtech/api/net/GT_Packet_SetConfigurationCircuit.java @@ -0,0 +1,110 @@ +package gregtech.api.net; + +import net.minecraft.item.ItemStack; +import net.minecraft.network.INetHandler; +import net.minecraft.network.NetHandlerPlayServer; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.world.IBlockAccess; +import net.minecraft.world.World; +import net.minecraftforge.common.DimensionManager; + +import com.google.common.io.ByteArrayDataInput; + +import cpw.mods.fml.common.network.ByteBufUtils; +import gregtech.api.interfaces.IConfigurationCircuitSupport; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.interfaces.tileentity.IHasInventory; +import gregtech.api.metatileentity.BaseTileEntity; +import gregtech.api.util.GT_Utility; +import gregtech.api.util.ISerializableObject; +import io.netty.buffer.ByteBuf; + +/** + * Client -> Server: Update machine configuration data + */ +public class GT_Packet_SetConfigurationCircuit extends GT_Packet_New { + + protected int mX; + protected short mY; + protected int mZ; + protected int dimId; + + protected ItemStack circuit; + + public GT_Packet_SetConfigurationCircuit() { + super(true); + } + + public GT_Packet_SetConfigurationCircuit(IGregTechTileEntity tile, ItemStack circuit) { + this(tile.getXCoord(), tile.getYCoord(), tile.getZCoord(), circuit); + } + + public GT_Packet_SetConfigurationCircuit(BaseTileEntity tile, ItemStack circuit) { + this(tile.getXCoord(), tile.getYCoord(), tile.getZCoord(), circuit); + } + + public GT_Packet_SetConfigurationCircuit(int x, short y, int z, ItemStack circuit) { + super(false); + + this.mX = x; + this.mY = y; + this.mZ = z; + + this.circuit = circuit; + } + + @Override + public byte getPacketID() { + return 12; + } + + @Override + public void encode(ByteBuf aOut) { + aOut.writeInt(mX); + aOut.writeShort(mY); + aOut.writeInt(mZ); + + // no null check needed. ByteBufUtils will handle it + ByteBufUtils.writeItemStack(aOut, this.circuit); + } + + @Override + public void setINetHandler(INetHandler aHandler) { + if (aHandler instanceof NetHandlerPlayServer) { + dimId = ((NetHandlerPlayServer) aHandler).playerEntity.dimension; + } else { + // packet sent to wrong side, so we need to ignore this one + // but there is no way to disrupt packet pipeline + // so we will instead go find world -2, which (hopefully) doesn't exist + // then we will fail silently in process() + dimId = -2; + } + } + + @Override + public GT_Packet_New decode(ByteArrayDataInput aData) { + return new GT_Packet_SetConfigurationCircuit( + aData.readInt(), + aData.readShort(), + aData.readInt(), + ISerializableObject.readItemStackFromGreggyByteBuf(aData)); + } + + @Override + public void process(IBlockAccess aWorld) { + final World world = DimensionManager.getWorld(dimId); + if (world == null) return; + + final TileEntity tile = world.getTileEntity(mX, mY, mZ); + if (!(tile instanceof BaseTileEntity) || ((BaseTileEntity) tile).isDead()) return; + + final IConfigurationCircuitSupport machine = ((BaseTileEntity) tile).getConfigurationCircuitSupport(); + if (machine == null) return; + if (!machine.allowSelectCircuit()) return; + machine.getConfigurationCircuits() + .stream() + .filter(stack -> GT_Utility.areStacksEqual(stack, circuit)) + .findFirst() + .ifPresent(stack -> ((IHasInventory) tile).setInventorySlotContents(machine.getCircuitSlot(), stack)); + } +} diff --git a/src/main/java/gregtech/api/net/GT_Packet_Sound.java b/src/main/java/gregtech/api/net/GT_Packet_Sound.java new file mode 100644 index 0000000000..fdaf5b3979 --- /dev/null +++ b/src/main/java/gregtech/api/net/GT_Packet_Sound.java @@ -0,0 +1,70 @@ +package gregtech.api.net; + +import java.io.IOException; + +import net.minecraft.world.IBlockAccess; + +import com.google.common.io.ByteArrayDataInput; + +import gregtech.api.util.GT_Log; +import gregtech.api.util.GT_Utility; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufOutputStream; + +public class GT_Packet_Sound extends GT_Packet_New { + + private int mX, mZ; + private short mY; + private String mSoundName; + private float mSoundStrength, mSoundPitch; + + public GT_Packet_Sound() { + super(true); + } + + public GT_Packet_Sound(String aSoundName, float aSoundStrength, float aSoundPitch, int aX, short aY, int aZ) { + super(false); + mX = aX; + mY = aY; + mZ = aZ; + mSoundName = aSoundName; + mSoundStrength = aSoundStrength; + mSoundPitch = aSoundPitch; + } + + @Override + public void encode(ByteBuf aOut) { + try (ByteBufOutputStream byteOutputStream = new ByteBufOutputStream(aOut)) { + byteOutputStream.writeUTF(mSoundName); + byteOutputStream.writeFloat(mSoundStrength); + byteOutputStream.writeFloat(mSoundPitch); + byteOutputStream.writeInt(mX); + byteOutputStream.writeShort(mY); + byteOutputStream.writeInt(mZ); + } catch (IOException e) { + // this really shouldn't happen, but whatever + e.printStackTrace(GT_Log.err); + } + } + + @Override + public GT_Packet_New decode(ByteArrayDataInput aData) { + return new GT_Packet_Sound( + aData.readUTF(), + aData.readFloat(), + aData.readFloat(), + aData.readInt(), + aData.readShort(), + aData.readInt()); + } + + @Override + public void process(IBlockAccess aWorld) { + GT_Utility.doSoundAtClient(mSoundName, 1, mSoundStrength, mSoundPitch, mX, mY, mZ); + } + + @Override + public byte getPacketID() { + return 1; + } +} diff --git a/src/main/java/gregtech/api/net/GT_Packet_TileEntity.java b/src/main/java/gregtech/api/net/GT_Packet_TileEntity.java new file mode 100644 index 0000000000..29562e9b4d --- /dev/null +++ b/src/main/java/gregtech/api/net/GT_Packet_TileEntity.java @@ -0,0 +1,157 @@ +package gregtech.api.net; + +import net.minecraft.block.Block; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.world.IBlockAccess; + +import com.google.common.io.ByteArrayDataInput; + +import gregtech.GT_Mod; +import gregtech.api.metatileentity.BaseMetaPipeEntity; +import gregtech.api.metatileentity.BaseMetaTileEntity; +import io.netty.buffer.ByteBuf; + +public class GT_Packet_TileEntity extends GT_Packet_New { + + private int mX, mZ, mC0, mC1, mC2, mC3, mC4, mC5; + private short mY, mID, mRID; + private byte mTexture, mTexturePage, mUpdate, mRedstone, mColor; + + public GT_Packet_TileEntity() { + super(true); + } + + // For multi tiles + public GT_Packet_TileEntity(int aX, short aY, int aZ, short aRID, short aID, int aC0, int aC1, int aC2, int aC3, + int aC4, int aC5, byte aTexture, byte aTexturePage, byte aUpdate, byte aRedstone, byte aColor) { + super(false); + mX = aX; + mY = aY; + mZ = aZ; + mC0 = aC0; + mC1 = aC1; + mC2 = aC2; + mC3 = aC3; + mC4 = aC4; + mC5 = aC5; + mRID = aRID; + mID = aID; + mTexture = aTexture; + mTexturePage = aTexturePage; + mUpdate = aUpdate; + mRedstone = aRedstone; + mColor = aColor; + } + + // For meta tiles + public GT_Packet_TileEntity(int aX, short aY, int aZ, short aID, int aC0, int aC1, int aC2, int aC3, int aC4, + int aC5, byte aTexture, byte aTexturePage, byte aUpdate, byte aRedstone, byte aColor) { + this( + aX, + aY, + aZ, + (short) 0, + aID, + aC0, + aC1, + aC2, + aC3, + aC4, + aC5, + aTexture, + aTexturePage, + aUpdate, + aRedstone, + aColor); + } + + // For pipes + public GT_Packet_TileEntity(int aX, short aY, int aZ, short aID, int aC0, int aC1, int aC2, int aC3, int aC4, + int aC5, byte aTexture, byte aUpdate, byte aRedstone, byte aColor) { + this(aX, aY, aZ, (short) 0, aID, aC0, aC1, aC2, aC3, aC4, aC5, aTexture, (byte) 0, aUpdate, aRedstone, aColor); + } + + @Override + public void encode(ByteBuf aOut) { + aOut.writeInt(mX); + aOut.writeShort(mY); + aOut.writeInt(mZ); + + aOut.writeShort(mRID); + aOut.writeShort(mID); + + aOut.writeInt(mC0); + aOut.writeInt(mC1); + aOut.writeInt(mC2); + aOut.writeInt(mC3); + aOut.writeInt(mC4); + aOut.writeInt(mC5); + + aOut.writeByte(mTexture); + aOut.writeByte(mTexturePage); + aOut.writeByte(mUpdate); + aOut.writeByte(mRedstone); + aOut.writeByte(mColor); + } + + @Override + public GT_Packet_New decode(ByteArrayDataInput aData) { + return new GT_Packet_TileEntity( + // Coords + aData.readInt(), + aData.readShort(), + aData.readInt(), + // Registry & ID + aData.readShort(), + aData.readShort(), + // Covers + aData.readInt(), + aData.readInt(), + aData.readInt(), + aData.readInt(), + aData.readInt(), + aData.readInt(), + // Everything else + aData.readByte(), + aData.readByte(), + aData.readByte(), + aData.readByte(), + aData.readByte()); + } + + @Override + public void process(IBlockAccess aWorld) { + if (aWorld == null) return; + final TileEntity tTileEntity = aWorld.getTileEntity(mX, mY, mZ); + try { + final Block tBlock; + if (tTileEntity instanceof BaseMetaTileEntity) ((BaseMetaTileEntity) tTileEntity).receiveMetaTileEntityData( + mID, + mC0, + mC1, + mC2, + mC3, + mC4, + mC5, + mTexture, + mTexturePage, + mUpdate, + mRedstone, + mColor); + else if (tTileEntity instanceof BaseMetaPipeEntity) ((BaseMetaPipeEntity) tTileEntity) + .receiveMetaTileEntityData(mID, mC0, mC1, mC2, mC3, mC4, mC5, mTexture, mUpdate, mRedstone, mColor); + } catch (Exception e) { + GT_Mod.GT_FML_LOGGER.error( + "Exception setting tile entity data for tile entity {} at ({}, {}, {})", + tTileEntity, + mX, + mY, + mZ); + } + } + + @Override + public byte getPacketID() { + return 0; + } +} diff --git a/src/main/java/gregtech/api/net/GT_Packet_TileEntityCover.java b/src/main/java/gregtech/api/net/GT_Packet_TileEntityCover.java new file mode 100644 index 0000000000..d3642b62e8 --- /dev/null +++ b/src/main/java/gregtech/api/net/GT_Packet_TileEntityCover.java @@ -0,0 +1,98 @@ +package gregtech.api.net; + +import net.minecraft.tileentity.TileEntity; +import net.minecraft.world.IBlockAccess; +import net.minecraft.world.World; +import net.minecraftforge.common.DimensionManager; +import net.minecraftforge.common.util.ForgeDirection; + +import com.google.common.io.ByteArrayDataInput; + +import gregtech.api.interfaces.tileentity.ICoverable; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import io.netty.buffer.ByteBuf; + +/** + * Client -> Server: Update cover data. use this only if you are using the legacy data storage + */ +public class GT_Packet_TileEntityCover extends GT_Packet_New { + + protected int mX; + protected short mY; + protected int mZ; + + protected ForgeDirection side; + protected int coverID, coverData, dimID; + + public GT_Packet_TileEntityCover() { + super(true); + } + + public GT_Packet_TileEntityCover(int mX, short mY, int mZ, ForgeDirection coverSide, int coverID, int coverData, + int dimID) { + super(false); + this.mX = mX; + this.mY = mY; + this.mZ = mZ; + + this.side = coverSide; + this.coverID = coverID; + this.coverData = coverData; + + this.dimID = dimID; + } + + public GT_Packet_TileEntityCover(ForgeDirection coverSide, int coverID, int coverData, ICoverable tile) { + super(false); + this.mX = tile.getXCoord(); + this.mY = tile.getYCoord(); + this.mZ = tile.getZCoord(); + + this.side = coverSide; + this.coverID = coverID; + this.coverData = coverData; + + this.dimID = tile.getWorld().provider.dimensionId; + } + + @Override + public byte getPacketID() { + return 6; + } + + @Override + public void encode(ByteBuf aOut) { + aOut.writeInt(mX); + aOut.writeShort(mY); + aOut.writeInt(mZ); + + aOut.writeByte(side.ordinal()); + aOut.writeInt(coverID); + aOut.writeInt(coverData); + + aOut.writeInt(dimID); + } + + @Override + public GT_Packet_New decode(ByteArrayDataInput aData) { + return new GT_Packet_TileEntityCover( + aData.readInt(), + aData.readShort(), + aData.readInt(), + ForgeDirection.getOrientation(aData.readByte()), + aData.readInt(), + aData.readInt(), + aData.readInt()); + } + + @Override + public void process(IBlockAccess aWorld) { + World world = DimensionManager.getWorld(dimID); + if (world != null) { + TileEntity tile = world.getTileEntity(mX, mY, mZ); + if (tile instanceof IGregTechTileEntity && !((IGregTechTileEntity) tile).isDead()) { + ((IGregTechTileEntity) tile).receiveCoverData(side, coverID, coverData); + } + } + } +} diff --git a/src/main/java/gregtech/api/net/GT_Packet_TileEntityCoverGUI.java b/src/main/java/gregtech/api/net/GT_Packet_TileEntityCoverGUI.java new file mode 100644 index 0000000000..1b61f87541 --- /dev/null +++ b/src/main/java/gregtech/api/net/GT_Packet_TileEntityCoverGUI.java @@ -0,0 +1,219 @@ +package gregtech.api.net; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.world.IBlockAccess; +import net.minecraft.world.World; +import net.minecraftforge.common.util.ForgeDirection; + +import com.google.common.io.ByteArrayDataInput; + +import gregtech.api.GregTech_API; +import gregtech.api.gui.GT_GUICover; +import gregtech.api.interfaces.tileentity.ICoverable; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.util.GT_CoverBehaviorBase; +import gregtech.api.util.ISerializableObject; +import gregtech.common.covers.CoverInfo; +import io.netty.buffer.ByteBuf; + +/** + * Server -> Client: Show GUI + */ +public class GT_Packet_TileEntityCoverGUI extends GT_Packet_New { + + protected int mX; + protected short mY; + protected int mZ; + + protected ForgeDirection side; + protected int coverID, dimID, playerID; + protected ISerializableObject coverData; + + protected int parentGuiId; + + public GT_Packet_TileEntityCoverGUI() { + super(true); + } + + public GT_Packet_TileEntityCoverGUI(int mX, short mY, int mZ, ForgeDirection coverSide, int coverID, int coverData, + int dimID, int playerID) { + super(false); + this.mX = mX; + this.mY = mY; + this.mZ = mZ; + + this.side = coverSide; + this.coverID = coverID; + this.coverData = new ISerializableObject.LegacyCoverData(coverData); + + this.dimID = dimID; + this.playerID = playerID; + this.parentGuiId = -1; + } + + public GT_Packet_TileEntityCoverGUI(int mX, short mY, int mZ, ForgeDirection coverSide, int coverID, + ISerializableObject coverData, int dimID, int playerID) { + super(false); + this.mX = mX; + this.mY = mY; + this.mZ = mZ; + + this.side = coverSide; + this.coverID = coverID; + this.coverData = coverData; + this.dimID = dimID; + this.playerID = playerID; + this.parentGuiId = -1; + } + + public GT_Packet_TileEntityCoverGUI(CoverInfo coverInfo, int dimID, int playerID, int parentGuiId) { + super(false); + final ICoverable tile = coverInfo.getTile(); + this.mX = tile.getXCoord(); + this.mY = tile.getYCoord(); + this.mZ = tile.getZCoord(); + + this.side = coverInfo.getSide(); + this.coverID = coverInfo.getCoverID(); + this.coverData = coverInfo.getCoverData(); + + this.dimID = dimID; + this.playerID = playerID; + this.parentGuiId = parentGuiId; + } + + public GT_Packet_TileEntityCoverGUI(int mX, short mY, int mZ, ForgeDirection coverSide, int coverID, + ISerializableObject coverData, int dimID, int playerID, int parentGuiId) { + super(false); + this.mX = mX; + this.mY = mY; + this.mZ = mZ; + + this.side = coverSide; + this.coverID = coverID; + this.coverData = coverData; + this.dimID = dimID; + this.playerID = playerID; + this.parentGuiId = parentGuiId; + } + + public GT_Packet_TileEntityCoverGUI(ForgeDirection side, int coverID, int coverData, ICoverable tile, + EntityPlayerMP aPlayer) { + super(false); + + this.mX = tile.getXCoord(); + this.mY = tile.getYCoord(); + this.mZ = tile.getZCoord(); + + this.side = side; + this.coverID = coverID; + this.coverData = new ISerializableObject.LegacyCoverData(coverData); + + this.dimID = tile.getWorld().provider.dimensionId; + this.playerID = aPlayer.getEntityId(); + this.parentGuiId = -1; + } + + public GT_Packet_TileEntityCoverGUI(ForgeDirection coverSide, int coverID, int coverData, + IGregTechTileEntity tile) { + super(false); + this.mX = tile.getXCoord(); + this.mY = tile.getYCoord(); + this.mZ = tile.getZCoord(); + + this.side = coverSide; + this.coverID = coverID; + this.coverData = new ISerializableObject.LegacyCoverData(coverData); + + this.dimID = tile.getWorld().provider.dimensionId; + this.parentGuiId = -1; + } + + public GT_Packet_TileEntityCoverGUI(ForgeDirection side, int coverID, ISerializableObject coverData, + ICoverable tile, EntityPlayerMP aPlayer) { + super(false); + this.mX = tile.getXCoord(); + this.mY = tile.getYCoord(); + this.mZ = tile.getZCoord(); + + this.side = side; + this.coverID = coverID; + this.coverData = coverData.copy(); // make a copy so we don't get a race condition + + this.dimID = tile.getWorld().provider.dimensionId; + this.playerID = aPlayer.getEntityId(); + this.parentGuiId = -1; + } + + @Override + public byte getPacketID() { + return 7; + } + + @Override + public void encode(ByteBuf aOut) { + aOut.writeInt(mX); + aOut.writeShort(mY); + aOut.writeInt(mZ); + + aOut.writeByte(side.ordinal()); + aOut.writeInt(coverID); + coverData.writeToByteBuf(aOut); + + aOut.writeInt(dimID); + aOut.writeInt(playerID); + + aOut.writeInt(parentGuiId); + } + + @Override + public GT_Packet_New decode(ByteArrayDataInput aData) { + final int coverID; + return new GT_Packet_TileEntityCoverGUI( + aData.readInt(), + aData.readShort(), + aData.readInt(), + ForgeDirection.getOrientation(aData.readByte()), + coverID = aData.readInt(), + GregTech_API.getCoverBehaviorNew(coverID) + .createDataObject() + .readFromPacket(aData, null), + aData.readInt(), + aData.readInt(), + aData.readInt()); + } + + @Override + public void process(IBlockAccess aWorld) { + if (aWorld instanceof World) { + // Using EntityPlayer instead of EntityClientPlayerMP so both client and server can load this + final EntityPlayer thePlayer = ((EntityPlayer) ((World) aWorld).getEntityByID(playerID)); + final TileEntity tile = aWorld.getTileEntity(mX, mY, mZ); + if (tile instanceof IGregTechTileEntity gtTile && !gtTile.isDead()) { + gtTile.setCoverDataAtSide(side, coverData); // Set it client side to read later. + + GT_CoverBehaviorBase<?> cover = gtTile.getCoverBehaviorAtSideNew(side); + if (cover.hasCoverGUI()) { + final GuiScreen gui = (GuiScreen) cover.getClientGUI( + side, + gtTile.getCoverIDAtSide(side), + gtTile.getComplexCoverDataAtSide(side), + gtTile, + thePlayer, + thePlayer.worldObj); + // If it's one of this mod's covers, tell it to exit to the GUI with the specified ID (-1 is + // ignored) + if (gui instanceof GT_GUICover guiCover) { + guiCover.setParentGuiId(parentGuiId); + } + Minecraft.getMinecraft() + .displayGuiScreen(gui); + } + } + } + } +} diff --git a/src/main/java/gregtech/api/net/GT_Packet_TileEntityCoverNew.java b/src/main/java/gregtech/api/net/GT_Packet_TileEntityCoverNew.java new file mode 100644 index 0000000000..8fd7348b24 --- /dev/null +++ b/src/main/java/gregtech/api/net/GT_Packet_TileEntityCoverNew.java @@ -0,0 +1,119 @@ +package gregtech.api.net; + +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.network.INetHandler; +import net.minecraft.network.NetHandlerPlayServer; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.world.IBlockAccess; +import net.minecraft.world.World; +import net.minecraftforge.common.DimensionManager; +import net.minecraftforge.common.util.ForgeDirection; + +import com.google.common.io.ByteArrayDataInput; + +import gregtech.api.GregTech_API; +import gregtech.api.interfaces.tileentity.ICoverable; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.util.ISerializableObject; +import io.netty.buffer.ByteBuf; + +/** + * Client -> Server: Update cover data + */ +public class GT_Packet_TileEntityCoverNew extends GT_Packet_New { + + protected int mX; + protected short mY; + protected int mZ; + + protected ForgeDirection side; + protected int coverID, dimID; + protected ISerializableObject coverData; + + protected EntityPlayerMP mPlayer; + + public GT_Packet_TileEntityCoverNew() { + super(true); + } + + public GT_Packet_TileEntityCoverNew(int mX, short mY, int mZ, ForgeDirection coverSide, int coverID, + ISerializableObject coverData, int dimID) { + super(false); + this.mX = mX; + this.mY = mY; + this.mZ = mZ; + + this.side = coverSide; + this.coverID = coverID; + this.coverData = coverData; + + this.dimID = dimID; + } + + public GT_Packet_TileEntityCoverNew(ForgeDirection coverSide, int coverID, ISerializableObject coverData, + ICoverable tile) { + super(false); + this.mX = tile.getXCoord(); + this.mY = tile.getYCoord(); + this.mZ = tile.getZCoord(); + + this.side = coverSide; + this.coverID = coverID; + this.coverData = coverData; + + this.dimID = tile.getWorld().provider.dimensionId; + } + + @Override + public byte getPacketID() { + return 11; + } + + @Override + public void setINetHandler(INetHandler aHandler) { + if (aHandler instanceof NetHandlerPlayServer) { + mPlayer = ((NetHandlerPlayServer) aHandler).playerEntity; + } + } + + @Override + public void encode(ByteBuf aOut) { + aOut.writeInt(mX); + aOut.writeShort(mY); + aOut.writeInt(mZ); + + aOut.writeByte(side.ordinal()); + aOut.writeInt(coverID); + coverData.writeToByteBuf(aOut); + + aOut.writeInt(dimID); + } + + @Override + public GT_Packet_New decode(ByteArrayDataInput aData) { + int coverId; + return new GT_Packet_TileEntityCoverNew( + aData.readInt(), + aData.readShort(), + aData.readInt(), + ForgeDirection.getOrientation(aData.readByte()), + coverId = aData.readInt(), + GregTech_API.getCoverBehaviorNew(coverId) + .createDataObject() + .readFromPacket(aData, mPlayer), + aData.readInt()); + } + + @Override + public void process(IBlockAccess aWorld) { + if (mPlayer == null) // impossible, but who knows + return; + World world = DimensionManager.getWorld(dimID); + if (world != null) { + TileEntity tile = world.getTileEntity(mX, mY, mZ); + if (tile instanceof IGregTechTileEntity && !((IGregTechTileEntity) tile).isDead()) { + ((IGregTechTileEntity) tile).receiveCoverData(side, coverID, coverData, mPlayer); + } + } + } +} diff --git a/src/main/java/gregtech/api/net/GT_Packet_UpdateItem.java b/src/main/java/gregtech/api/net/GT_Packet_UpdateItem.java new file mode 100644 index 0000000000..e8ec9be80b --- /dev/null +++ b/src/main/java/gregtech/api/net/GT_Packet_UpdateItem.java @@ -0,0 +1,64 @@ +package gregtech.api.net; + +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.network.INetHandler; +import net.minecraft.network.NetHandlerPlayServer; +import net.minecraft.world.IBlockAccess; + +import com.google.common.io.ByteArrayDataInput; + +import cpw.mods.fml.common.network.ByteBufUtils; +import gregtech.api.interfaces.INetworkUpdatableItem; +import gregtech.api.util.ISerializableObject; +import io.netty.buffer.ByteBuf; + +/** + * Client -> Server: send arbitrary data to server and update the currently held item. + */ +public class GT_Packet_UpdateItem extends GT_Packet_New { + + private NBTTagCompound tag; + private EntityPlayerMP mPlayer; + + public GT_Packet_UpdateItem() { + super(true); + } + + public GT_Packet_UpdateItem(NBTTagCompound tag) { + super(false); + this.tag = tag; + } + + @Override + public byte getPacketID() { + return 13; + } + + @Override + public void setINetHandler(INetHandler aHandler) { + if (aHandler instanceof NetHandlerPlayServer) { + mPlayer = ((NetHandlerPlayServer) aHandler).playerEntity; + } + } + + @Override + public void encode(ByteBuf aOut) { + ByteBufUtils.writeTag(aOut, tag); + } + + @Override + public GT_Packet_New decode(ByteArrayDataInput aData) { + return new GT_Packet_UpdateItem(ISerializableObject.readCompoundTagFromGreggyByteBuf(aData)); + } + + @Override + public void process(IBlockAccess aWorld) { + if (mPlayer == null) return; + ItemStack stack = mPlayer.inventory.getCurrentItem(); + if (stack != null && stack.getItem() instanceof INetworkUpdatableItem) { + ((INetworkUpdatableItem) stack.getItem()).receive(stack, mPlayer, tag); + } + } +} diff --git a/src/main/java/gregtech/api/net/GT_Packet_WirelessRedstoneCover.java b/src/main/java/gregtech/api/net/GT_Packet_WirelessRedstoneCover.java new file mode 100644 index 0000000000..08628ace2b --- /dev/null +++ b/src/main/java/gregtech/api/net/GT_Packet_WirelessRedstoneCover.java @@ -0,0 +1,99 @@ +package gregtech.api.net; + +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.network.INetHandler; +import net.minecraft.network.NetHandlerPlayServer; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.world.IBlockAccess; +import net.minecraft.world.World; +import net.minecraftforge.common.DimensionManager; +import net.minecraftforge.common.util.ForgeDirection; + +import com.google.common.io.ByteArrayDataInput; + +import gregtech.api.interfaces.tileentity.ICoverable; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import io.netty.buffer.ByteBuf; + +public class GT_Packet_WirelessRedstoneCover extends GT_Packet_TileEntityCover { + + private static final int PRIVATE_MASK = 0xFFFE0000; + private static final int PUBLIC_MASK = 0x0000FFFF; + private static final int CHECKBOX_MASK = 0x00010000; + + private EntityPlayerMP mPlayer; + private int mPublicChannel; + private int mCheckBoxValue; + + public GT_Packet_WirelessRedstoneCover() { + super(); + } + + public GT_Packet_WirelessRedstoneCover(int mX, short mY, int mZ, ForgeDirection coverSide, int coverID, int dimID, + int publicChannel, int checkBoxValue) { + super(mX, mY, mZ, coverSide, coverID, 0, dimID); + mPublicChannel = publicChannel; + mCheckBoxValue = checkBoxValue; + } + + public GT_Packet_WirelessRedstoneCover(ForgeDirection coverSide, int coverID, ICoverable tile, int publicChannel, + int checkBoxValue) { + super(coverSide, coverID, 0, tile); + mPublicChannel = publicChannel; + mCheckBoxValue = checkBoxValue; + } + + @Override + public byte getPacketID() { + return 10; + } + + @Override + public void setINetHandler(INetHandler aHandler) { + if (aHandler instanceof NetHandlerPlayServer) { + mPlayer = ((NetHandlerPlayServer) aHandler).playerEntity; + } + } + + @Override + public void encode(ByteBuf aOut) { + aOut.writeInt(mX); + aOut.writeShort(mY); + aOut.writeInt(mZ); + + aOut.writeByte(side.ordinal()); + aOut.writeInt(coverID); + + aOut.writeInt(dimID); + + aOut.writeInt(mPublicChannel); + aOut.writeInt(mCheckBoxValue); + } + + @Override + public GT_Packet_New decode(ByteArrayDataInput aData) { + return new GT_Packet_WirelessRedstoneCover( + aData.readInt(), + aData.readShort(), + aData.readInt(), + ForgeDirection.getOrientation(aData.readByte()), + aData.readInt(), + aData.readInt(), + aData.readInt(), + aData.readInt()); + } + + @Override + public void process(IBlockAccess aWorld) { + World world = DimensionManager.getWorld(dimID); + if (world != null && world.blockExists(mX, mY, mZ)) { + TileEntity tile = world.getTileEntity(mX, mY, mZ); + if (tile instanceof IGregTechTileEntity && !((IGregTechTileEntity) tile).isDead()) { + int tPrivateChannel = (mCheckBoxValue > 0) ? mPlayer.getUniqueID() + .hashCode() & PRIVATE_MASK : 0; + int tCoverData = tPrivateChannel | (mCheckBoxValue & CHECKBOX_MASK) | (mPublicChannel & PUBLIC_MASK); + ((IGregTechTileEntity) tile).receiveCoverData(side, coverID, tCoverData); + } + } + } +} diff --git a/src/main/java/gregtech/api/net/IGT_NetworkHandler.java b/src/main/java/gregtech/api/net/IGT_NetworkHandler.java new file mode 100644 index 0000000000..07c4a37030 --- /dev/null +++ b/src/main/java/gregtech/api/net/IGT_NetworkHandler.java @@ -0,0 +1,18 @@ +package gregtech.api.net; + +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.world.World; + +import cpw.mods.fml.common.network.NetworkRegistry.TargetPoint; + +@SuppressWarnings("deprecation") +public interface IGT_NetworkHandler { + + void sendToPlayer(GT_Packet aPacket, EntityPlayerMP aPlayer); + + void sendToAllAround(GT_Packet aPacket, TargetPoint aPosition); + + void sendToServer(GT_Packet aPacket); + + void sendPacketToAllPlayersInRange(World aWorld, GT_Packet aPacket, int aX, int aZ); +} diff --git a/src/main/java/gregtech/api/net/data/CasingData.java b/src/main/java/gregtech/api/net/data/CasingData.java new file mode 100644 index 0000000000..627c1eacf2 --- /dev/null +++ b/src/main/java/gregtech/api/net/data/CasingData.java @@ -0,0 +1,53 @@ +package gregtech.api.net.data; + +import javax.annotation.Nonnull; + +import net.minecraft.util.ChunkCoordinates; + +import com.google.common.io.ByteArrayDataInput; + +import io.netty.buffer.ByteBuf; + +public class CasingData extends PacketData<MultiTileEntityProcess> { + + public static final int CASING_DATA_ID = 4; + + private int currentMode; + private int allowedModes; + private ChunkCoordinates controllerCoords; + + public CasingData() {} + + public CasingData(int currentMode, int allowedModes, ChunkCoordinates controllerCoords) { + this.currentMode = currentMode; + this.allowedModes = allowedModes; + this.controllerCoords = controllerCoords; + } + + @Override + public void decode(@Nonnull ByteArrayDataInput in) { + currentMode = in.readInt(); + allowedModes = in.readInt(); + controllerCoords = new ChunkCoordinates(in.readInt(), in.readInt(), in.readInt()); + } + + @Override + public void encode(@Nonnull ByteBuf out) { + out.writeInt(currentMode); + out.writeInt(allowedModes); + out.writeInt(controllerCoords.posX); + out.writeInt(controllerCoords.posY); + out.writeInt(controllerCoords.posZ); + } + + @Override + public int getId() { + return CASING_DATA_ID; + } + + @Override + public void process(MultiTileEntityProcess processData) { + + } + +} diff --git a/src/main/java/gregtech/api/net/data/CommonData.java b/src/main/java/gregtech/api/net/data/CommonData.java new file mode 100644 index 0000000000..294cca134b --- /dev/null +++ b/src/main/java/gregtech/api/net/data/CommonData.java @@ -0,0 +1,50 @@ +package gregtech.api.net.data; + +import javax.annotation.Nonnull; + +import com.google.common.io.ByteArrayDataInput; + +import io.netty.buffer.ByteBuf; + +public class CommonData extends PacketData<MultiTileEntityProcess> { + + public static final int COMMON_DATA_ID = 2; + + private byte redstone; + private byte color; + private byte commonData; + + public CommonData() {} + + public CommonData(byte redstone, byte color, byte commonData) { + this.redstone = redstone; + this.color = color; + this.commonData = commonData; + } + + @Override + public void decode(@Nonnull ByteArrayDataInput in) { + redstone = in.readByte(); + color = in.readByte(); + commonData = in.readByte(); + } + + @Override + public void encode(@Nonnull ByteBuf out) { + out.writeByte(redstone); + out.writeByte(color); + out.writeByte(commonData); + } + + @Override + public int getId() { + return COMMON_DATA_ID; + } + + @Override + public void process(MultiTileEntityProcess processData) { + // TODO Auto-generated method stub + + } + +} diff --git a/src/main/java/gregtech/api/net/data/CoordinateData.java b/src/main/java/gregtech/api/net/data/CoordinateData.java new file mode 100644 index 0000000000..ad8ebbd210 --- /dev/null +++ b/src/main/java/gregtech/api/net/data/CoordinateData.java @@ -0,0 +1,50 @@ +package gregtech.api.net.data; + +import javax.annotation.Nonnull; + +import net.minecraft.util.ChunkCoordinates; + +import com.google.common.io.ByteArrayDataInput; + +import io.netty.buffer.ByteBuf; + +public class CoordinateData extends PacketData<MultiTileEntityProcess> { + + public final static int COORDINATE_DATA_ID = 0; + + private ChunkCoordinates coords; + + public CoordinateData(ChunkCoordinates coords) { + this.coords = coords; + } + + public CoordinateData(int x, int y, int z) { + this(new ChunkCoordinates(x, y, z)); + } + + public CoordinateData() {} + + @Override + public int getId() { + return COORDINATE_DATA_ID; + } + + @Override + public void encode(@Nonnull ByteBuf out) { + out.writeInt(coords.posX); + out.writeInt(coords.posY); + out.writeInt(coords.posZ); + } + + @Override + public void decode(@Nonnull ByteArrayDataInput in) { + coords = new ChunkCoordinates(in.readInt(), in.readInt(), in.readInt()); + } + + @Override + public void process(MultiTileEntityProcess processData) { + if (coords == null) return; + processData.giveCoordinates(coords); + } + +} diff --git a/src/main/java/gregtech/api/net/data/MultiTileEntityData.java b/src/main/java/gregtech/api/net/data/MultiTileEntityData.java new file mode 100644 index 0000000000..2bdbf4acc9 --- /dev/null +++ b/src/main/java/gregtech/api/net/data/MultiTileEntityData.java @@ -0,0 +1,45 @@ +package gregtech.api.net.data; + +import javax.annotation.Nonnull; + +import com.google.common.io.ByteArrayDataInput; + +import io.netty.buffer.ByteBuf; + +public class MultiTileEntityData extends PacketData<MultiTileEntityProcess> { + + public static final int MULTI_TILE_ENTITY_DATA_ID = 1; + + private int registryId; + private int metaId; + + public MultiTileEntityData() {} + + public MultiTileEntityData(int registryId, int metaId) { + this.registryId = registryId; + this.metaId = metaId; + } + + @Override + public int getId() { + return MULTI_TILE_ENTITY_DATA_ID; + } + + @Override + public void encode(@Nonnull ByteBuf out) { + out.writeInt(registryId); + out.writeInt(metaId); + } + + @Override + public void decode(@Nonnull ByteArrayDataInput in) { + registryId = in.readInt(); + metaId = in.readInt(); + } + + @Override + public void process(MultiTileEntityProcess processData) { + processData.giveMultiTileEntityData(registryId, metaId); + } + +} diff --git a/src/main/java/gregtech/api/net/data/MultiTileEntityProcess.java b/src/main/java/gregtech/api/net/data/MultiTileEntityProcess.java new file mode 100644 index 0000000000..02e04981c0 --- /dev/null +++ b/src/main/java/gregtech/api/net/data/MultiTileEntityProcess.java @@ -0,0 +1,54 @@ +package gregtech.api.net.data; + +import javax.annotation.Nonnull; + +import net.minecraft.block.Block; +import net.minecraft.util.ChunkCoordinates; +import net.minecraft.world.IBlockAccess; + +import gregtech.api.multitileentity.MultiTileEntityBlock; +import gregtech.api.multitileentity.interfaces.IMultiTileEntity; + +public class MultiTileEntityProcess extends Process { + + @Nonnull + private final IBlockAccess world; + private ChunkCoordinates coords; + private int registryId; + private int metaId; + private byte redstone; + private byte color; + private byte commonData; + + public MultiTileEntityProcess(@Nonnull IBlockAccess world) { + this.world = world; + } + + @Override + public void process() { + if (coords == null) return; + Block block = world.getBlock(coords.posX, coords.posY, coords.posZ); + if (!(block instanceof MultiTileEntityBlock muteBlock)) { + return; + } + IMultiTileEntity mute = muteBlock + .receiveMultiTileEntityData(world, coords.posX, coords.posY, coords.posZ, registryId, metaId); + if (mute == null) return; + mute.setColorization(color); + } + + public void giveCoordinates(@Nonnull ChunkCoordinates coords) { + this.coords = coords; + } + + public void giveMultiTileEntityData(int registryId, int metaId) { + this.registryId = registryId; + this.metaId = metaId; + } + + public void giveCommonData(byte redstone, byte color, byte commonData) { + this.redstone = redstone; + this.color = color; + this.commonData = commonData; + } +} diff --git a/src/main/java/gregtech/api/net/data/PacketData.java b/src/main/java/gregtech/api/net/data/PacketData.java new file mode 100644 index 0000000000..bde2b9fb76 --- /dev/null +++ b/src/main/java/gregtech/api/net/data/PacketData.java @@ -0,0 +1,48 @@ +package gregtech.api.net.data; + +import javax.annotation.Nonnull; + +import com.google.common.io.ByteArrayDataInput; + +import io.netty.buffer.ByteBuf; + +public abstract class PacketData<T extends Process> implements Comparable<PacketData<T>> { + + /** + * This should return the Id of the packet. The Id is is used to bit-shift to be added a header for the packet its + * used in + */ + public abstract int getId(); + + /** + * Called by the packet it is held by to store the data it needs + */ + public abstract void encode(@Nonnull ByteBuf out); + + /** + * Called by the packet it is held by to decode the data to later be used in {@link #process()} + */ + public abstract void decode(@Nonnull ByteArrayDataInput in); + + /** + * Called by the packet it is held by to process the data it decoded. + */ + public abstract void process(T processData); + + @Override + public boolean equals(Object other) { + if (this == other) return true; + if (!(other instanceof PacketData otherData)) return false; + return this.getId() == otherData.getId(); + } + + @Override + public int hashCode() { + return getId(); + } + + @Override + public int compareTo(PacketData<T> other) { + return Integer.compare(this.getId(), other.getId()); + } +} diff --git a/src/main/java/gregtech/api/net/data/Process.java b/src/main/java/gregtech/api/net/data/Process.java new file mode 100644 index 0000000000..d5bc1317b7 --- /dev/null +++ b/src/main/java/gregtech/api/net/data/Process.java @@ -0,0 +1,6 @@ +package gregtech.api.net.data; + +public abstract class Process { + + public abstract void process(); +} diff --git a/src/main/java/gregtech/api/objects/AE2DigitalChestHandler.java b/src/main/java/gregtech/api/objects/AE2DigitalChestHandler.java new file mode 100644 index 0000000000..c2e0556de9 --- /dev/null +++ b/src/main/java/gregtech/api/objects/AE2DigitalChestHandler.java @@ -0,0 +1,26 @@ +package gregtech.api.objects; + +import net.minecraft.tileentity.TileEntity; +import net.minecraftforge.common.util.ForgeDirection; + +import gregtech.api.metatileentity.BaseMetaTileEntity; +import gregtech.common.tileentities.storage.GT_MetaTileEntity_DigitalChestBase; + +public class AE2DigitalChestHandler implements appeng.api.storage.IExternalStorageHandler { + + @Override + public boolean canHandle(final TileEntity te, final ForgeDirection d, final appeng.api.storage.StorageChannel chan, + final appeng.api.networking.security.BaseActionSource mySrc) { + return chan == appeng.api.storage.StorageChannel.ITEMS && te instanceof BaseMetaTileEntity + && ((BaseMetaTileEntity) te).getMetaTileEntity() instanceof GT_MetaTileEntity_DigitalChestBase; + } + + @Override + public appeng.api.storage.IMEInventory<?> getInventory(final TileEntity te, final ForgeDirection d, + final appeng.api.storage.StorageChannel chan, final appeng.api.networking.security.BaseActionSource src) { + if (chan == appeng.api.storage.StorageChannel.ITEMS) { + return ((GT_MetaTileEntity_DigitalChestBase) (((BaseMetaTileEntity) te).getMetaTileEntity())); + } + return null; + } +} diff --git a/src/main/java/gregtech/api/objects/CollectorUtils.java b/src/main/java/gregtech/api/objects/CollectorUtils.java new file mode 100644 index 0000000000..7265076683 --- /dev/null +++ b/src/main/java/gregtech/api/objects/CollectorUtils.java @@ -0,0 +1,30 @@ +package gregtech.api.objects; + +import java.util.Map; +import java.util.function.BiFunction; +import java.util.function.BinaryOperator; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Collector; +import java.util.stream.Collectors; + +public class CollectorUtils { + + /** + * Returns a merge function, suitable for use in {@link Map#merge(Object, Object, BiFunction) Map.merge()} or + * {@link Collectors#toMap(Function, Function, BinaryOperator) toMap()}, which always throws + * {@code IllegalStateException}. This can be used to enforce the assumption that the elements being collected are + * distinct. + * + * @param <T> the type of input arguments to the merge function + * @return a merge function which always throw {@code IllegalStateException} + */ + public static <T> BinaryOperator<T> throwingMerger() { + return (u, v) -> { throw new IllegalStateException(String.format("Duplicate key %s", u)); }; + } + + public static <K, V, E extends Map.Entry<K, V>, M extends Map<K, V>> Collector<E, ?, M> entriesToMap( + Supplier<M> mapSupplier) { + return Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, CollectorUtils.throwingMerger(), mapSupplier); + } +} diff --git a/src/main/java/gregtech/api/objects/ElementStack.java b/src/main/java/gregtech/api/objects/ElementStack.java new file mode 100644 index 0000000000..58fffd475a --- /dev/null +++ b/src/main/java/gregtech/api/objects/ElementStack.java @@ -0,0 +1,47 @@ +package gregtech.api.objects; + +import gregtech.api.enums.Element; + +public class ElementStack implements Cloneable { + + public int mAmount; + public Element mElement; + + public ElementStack(Element aElement, int aAmount) { + mElement = aElement == null ? Element._NULL : aElement; + mAmount = aAmount; + } + + public ElementStack copy(int aAmount) { + return new ElementStack(mElement, aAmount); + } + + @Override + public ElementStack clone() { + try { + return (ElementStack) super.clone(); + } catch (Exception e) { + return new ElementStack(mElement, mAmount); + } + } + + @Override + public boolean equals(Object aObject) { + if (aObject == this) return true; + if (aObject == null) return false; + if (aObject instanceof Element) return aObject == mElement; + if (aObject instanceof ElementStack) return ((ElementStack) aObject).mElement == mElement + && (mAmount < 0 || ((ElementStack) aObject).mAmount < 0 || ((ElementStack) aObject).mAmount == mAmount); + return false; + } + + @Override + public String toString() { + return mElement.toString() + mAmount; + } + + @Override + public int hashCode() { + return mElement.hashCode(); + } +} diff --git a/src/main/java/gregtech/api/objects/GT_ArrayList.java b/src/main/java/gregtech/api/objects/GT_ArrayList.java new file mode 100644 index 0000000000..9124ef8616 --- /dev/null +++ b/src/main/java/gregtech/api/objects/GT_ArrayList.java @@ -0,0 +1,73 @@ +package gregtech.api.objects; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Objects; + +import com.google.common.collect.Collections2; + +public class GT_ArrayList<E> extends ArrayList<E> { + + private static final long serialVersionUID = 1L; + private int size_sS; + + private final boolean mAllowNulls; + + public GT_ArrayList(boolean aAllowNulls, int aCapacity) { + super(aCapacity); + mAllowNulls = aAllowNulls; + } + + @SafeVarargs + public GT_ArrayList(boolean aAllowNulls, E... aArray) { + super(Arrays.asList(aArray)); + mAllowNulls = aAllowNulls; + if (!mAllowNulls) { + size_sS = size(); + for (int i = 0; i < size_sS; i++) if (get(i) == null) { + remove(i--); + size_sS = size(); + } + } + } + + public GT_ArrayList(boolean aAllowNulls, Collection<? extends E> aList) { + super(aList); + mAllowNulls = aAllowNulls; + if (!mAllowNulls) { + size_sS = size(); + for (int i = 0; i < size_sS; i++) if (get(i) == null) { + remove(i--); + size_sS = size(); + } + } + } + + @Override + public E set(int aIndex, E aElement) { + if (mAllowNulls || aElement != null) return super.set(aIndex, aElement); + return null; + } + + @Override + public boolean add(E aElement) { + if (mAllowNulls || aElement != null) return super.add(aElement); + return false; + } + + @Override + public void add(int aIndex, E aElement) { + if (mAllowNulls || aElement != null) super.add(aIndex, aElement); + } + + @Override + public boolean addAll(Collection<? extends E> aList) { + return super.addAll(Collections2.filter(aList, Objects::nonNull)); + } + + @Override + public boolean addAll(int aIndex, Collection<? extends E> aList) { + return super.addAll(aIndex, Collections2.filter(aList, Objects::nonNull)); + } +} diff --git a/src/main/java/gregtech/api/objects/GT_ChunkManager.java b/src/main/java/gregtech/api/objects/GT_ChunkManager.java new file mode 100644 index 0000000000..14baaddd3d --- /dev/null +++ b/src/main/java/gregtech/api/objects/GT_ChunkManager.java @@ -0,0 +1,204 @@ +package gregtech.api.objects; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.world.ChunkCoordIntPair; +import net.minecraft.world.World; +import net.minecraftforge.common.ForgeChunkManager; +import net.minecraftforge.common.ForgeChunkManager.Ticket; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.ListMultimap; + +import gregtech.GT_Mod; +import gregtech.api.enums.GT_Values; +import gregtech.api.interfaces.IChunkLoader; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.util.GT_Log; + +/** + * Handles re-initialization of chunks after a server restart. + */ +public class GT_ChunkManager + implements ForgeChunkManager.OrderedLoadingCallback, ForgeChunkManager.PlayerOrderedLoadingCallback { + + private final Map<TileEntity, Ticket> registeredTickets = new HashMap<>(); + public static GT_ChunkManager instance = new GT_ChunkManager(); + + public static void init() { + ForgeChunkManager.setForcedChunkLoadingCallback(GT_Mod.instance, instance); + } + + @Override + public void ticketsLoaded(List<Ticket> tickets, World world) {} + + /** + * Determines if tickets should be kept. Based on if the ticket is a machine or a working-chunk ticket. + * Working-chunk tickets are tossed and recreated when the machine reactivates. + * Machine tickets are kept only if the config {@code alwaysReloadChunkloaders} is true. + * Otherwise, machine chunks are tossed and recreated only when the machine reactivates, + * similarly to a Passive Anchor. + * + * @param tickets The tickets that you will want to select from. + * The list is immutable and cannot be manipulated directly. Copy it first. + * @param world The world + * @param maxTicketCount The maximum number of tickets that will be allowed. + * @return list of tickets + */ + + @Override + public List<Ticket> ticketsLoaded(List<Ticket> tickets, World world, int maxTicketCount) { + List<Ticket> validTickets = new ArrayList<>(); + if (GT_Values.alwaysReloadChunkloaders) { + for (Ticket ticket : tickets) { + int x = ticket.getModData() + .getInteger("OwnerX"); + int y = ticket.getModData() + .getInteger("OwnerY"); + int z = ticket.getModData() + .getInteger("OwnerZ"); + if (y > 0) { + TileEntity tile = world.getTileEntity(x, y, z); + if (tile instanceof IGregTechTileEntity && ((IGregTechTileEntity) tile).isAllowedToWork()) { + ForgeChunkManager.forceChunk(ticket, new ChunkCoordIntPair(x >> 4, z >> 4)); + if (!registeredTickets.containsKey(tile)) { + registeredTickets.put(tile, ticket); + if (((IGregTechTileEntity) tile).getMetaTileEntity() instanceof IChunkLoader) + ForgeChunkManager.forceChunk( + ticket, + ((IChunkLoader) ((IGregTechTileEntity) tile).getMetaTileEntity()).getActiveChunk()); + validTickets.add(ticket); + } + } + } + } + } + return validTickets; + } + + /** + * Determines if player tickets should be kept. This is where a ticket list per-player would be created and + * maintained. When a player joins, an event occurs, their name/UUID/etc is compared against tickets on this list + * and those tickets are reactivated. + * Since that info would be maintained/dealt with on a per-player startup, the list returned back to Forge is empty. + * + * @param tickets The tickets that you will want to select from. + * The list is immutable and cannot be manipulated directly. Copy it first. + * @param world The world + * @return the list of string-ticket paris + */ + @Override + public ListMultimap<String, Ticket> playerTicketsLoaded(ListMultimap<String, Ticket> tickets, World world) { + // Not currently used, so just return an empty list. + return ArrayListMultimap.create(); + } + + /** + * Requests a chunk to be loaded for this machine. May pass a {@code null} chunk to load just the machine itself if + * {@code alwaysReloadChunkloaders} is enabled in config. + * + * @param owner owner of the TileEntity + * @param chunkXZ chunk coordinates + * @param player player + * @return if the chunk was loaded successfully + */ + public static boolean requestPlayerChunkLoad(TileEntity owner, ChunkCoordIntPair chunkXZ, String player) { + if (!GT_Values.enableChunkloaders) return false; + if (!GT_Values.alwaysReloadChunkloaders && chunkXZ == null) return false; + if (GT_Values.debugChunkloaders && chunkXZ != null) GT_Log.out + .println("GT_ChunkManager: Chunk request: (" + chunkXZ.chunkXPos + ", " + chunkXZ.chunkZPos + ")"); + if (instance.registeredTickets.containsKey(owner)) { + ForgeChunkManager.forceChunk(instance.registeredTickets.get(owner), chunkXZ); + } else { + Ticket ticket; + if (player.equals("")) ticket = ForgeChunkManager + .requestTicket(GT_Mod.instance, owner.getWorldObj(), ForgeChunkManager.Type.NORMAL); + else ticket = ForgeChunkManager + .requestPlayerTicket(GT_Mod.instance, player, owner.getWorldObj(), ForgeChunkManager.Type.NORMAL); + if (ticket == null) { + if (GT_Values.debugChunkloaders) + GT_Log.out.println("GT_ChunkManager: ForgeChunkManager.requestTicket failed"); + return false; + } + if (GT_Values.debugChunkloaders) GT_Log.out.println( + "GT_ChunkManager: ticket issued for machine at: (" + owner.xCoord + + ", " + + owner.yCoord + + ", " + + owner.zCoord + + ")"); + NBTTagCompound tag = ticket.getModData(); + tag.setInteger("OwnerX", owner.xCoord); + tag.setInteger("OwnerY", owner.yCoord); + tag.setInteger("OwnerZ", owner.zCoord); + ForgeChunkManager.forceChunk(ticket, chunkXZ); + if (GT_Values.alwaysReloadChunkloaders) + ForgeChunkManager.forceChunk(ticket, new ChunkCoordIntPair(owner.xCoord >> 4, owner.zCoord >> 4)); + instance.registeredTickets.put(owner, ticket); + } + return true; + } + + @SuppressWarnings("UnusedReturnValue") + public static boolean requestChunkLoad(TileEntity owner, ChunkCoordIntPair chunkXZ) { + return requestPlayerChunkLoad(owner, chunkXZ, ""); + } + + public static void releaseChunk(TileEntity owner, ChunkCoordIntPair chunkXZ) { + if (!GT_Values.enableChunkloaders) return; + Ticket ticket = instance.registeredTickets.get(owner); + if (ticket != null) { + if (GT_Values.debugChunkloaders) GT_Log.out + .println("GT_ChunkManager: Chunk release: (" + chunkXZ.chunkXPos + ", " + chunkXZ.chunkZPos + ")"); + ForgeChunkManager.unforceChunk(ticket, chunkXZ); + } + } + + public static void releaseTicket(TileEntity owner) { + if (!GT_Values.enableChunkloaders) return; + Ticket ticket = instance.registeredTickets.get(owner); + if (ticket != null) { + if (GT_Values.debugChunkloaders) { + GT_Log.out.println( + "GT_ChunkManager: ticket released by machine at: (" + owner.xCoord + + ", " + + owner.yCoord + + ", " + + owner.zCoord + + ")"); + for (ChunkCoordIntPair chunk : ticket.getChunkList()) GT_Log.out + .println("GT_ChunkManager: Chunk release: (" + chunk.chunkXPos + ", " + chunk.chunkZPos + ")"); + } + ForgeChunkManager.releaseTicket(ticket); + instance.registeredTickets.remove(owner); + } + } + + public static void printTickets() { + GT_Log.out.println("GT_ChunkManager: Start forced chunks dump:"); + instance.registeredTickets.forEach((machine, ticket) -> { + GT_Log.out.print( + "GT_ChunkManager: Chunks forced by the machine at (" + machine.xCoord + + ", " + + machine.yCoord + + ", " + + machine.zCoord + + ")"); + if (ticket.isPlayerTicket()) GT_Log.out.print(" Owner: " + ticket.getPlayerName()); + GT_Log.out.print(" :"); + for (ChunkCoordIntPair c : ticket.getChunkList()) { + GT_Log.out.print("("); + GT_Log.out.print(c.chunkXPos); + GT_Log.out.print(", "); + GT_Log.out.print(c.chunkZPos); + GT_Log.out.print("), "); + } + }); + GT_Log.out.println("GT_ChunkManager: End forced chunks dump:"); + } +} diff --git a/src/main/java/gregtech/api/objects/GT_CopiedBlockTexture.java b/src/main/java/gregtech/api/objects/GT_CopiedBlockTexture.java new file mode 100644 index 0000000000..c5307b4803 --- /dev/null +++ b/src/main/java/gregtech/api/objects/GT_CopiedBlockTexture.java @@ -0,0 +1,35 @@ +package gregtech.api.objects; + +import net.minecraft.block.Block; + +import gregtech.api.enums.Dyes; +import gregtech.api.interfaces.ITexture; + +/** + * @deprecated Replaced by the {@link gregtech.api.render.TextureFactory} API. + */ +@Deprecated +public class GT_CopiedBlockTexture extends gregtech.common.render.GT_CopiedBlockTexture implements ITexture { + + // Backwards Compat + @Deprecated + public short[] mRGBa; + + public GT_CopiedBlockTexture(Block aBlock, int ordinalSide, int aMeta, short[] aRGBa, boolean aAllowAlpha) { + super(aBlock, ordinalSide, aMeta, aRGBa, aAllowAlpha); + GT_CopiedBlockTexture.this.mRGBa = aRGBa; + } + + public GT_CopiedBlockTexture(Block aBlock, int ordinalSide, int aMeta, short[] aRGBa) { + this(aBlock, ordinalSide, aMeta, aRGBa, true); + } + + public GT_CopiedBlockTexture(Block aBlock, int ordinalSide, int aMeta) { + this(aBlock, ordinalSide, aMeta, Dyes._NULL.mRGBa); + } + + @Override + public boolean isOldTexture() { + return true; + } +} diff --git a/src/main/java/gregtech/api/objects/GT_Cover_Default.java b/src/main/java/gregtech/api/objects/GT_Cover_Default.java new file mode 100644 index 0000000000..cc5f96eef3 --- /dev/null +++ b/src/main/java/gregtech/api/objects/GT_Cover_Default.java @@ -0,0 +1,81 @@ +package gregtech.api.objects; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraftforge.common.util.ForgeDirection; +import net.minecraftforge.fluids.Fluid; + +import gregtech.api.interfaces.tileentity.ICoverable; +import gregtech.api.util.GT_CoverBehavior; +import gregtech.api.util.GT_Utility; + +public class GT_Cover_Default extends GT_CoverBehavior { + + /** + * This is the Dummy, if there is a generic Cover without behavior + */ + public GT_Cover_Default() { + super(); + } + + @Override + public boolean isSimpleCover() { + return true; + } + + @Override + public int onCoverScrewdriverclick(ForgeDirection side, int aCoverID, int aCoverVariable, ICoverable aTileEntity, + EntityPlayer aPlayer, float aX, float aY, float aZ) { + aCoverVariable = ((aCoverVariable + 1) & 15); + GT_Utility.sendChatToPlayer( + aPlayer, + ((aCoverVariable & 1) != 0 ? GT_Utility.trans("128.1", "Redstone ") : "") + + ((aCoverVariable & 2) != 0 ? GT_Utility.trans("129.1", "Energy ") : "") + + ((aCoverVariable & 4) != 0 ? GT_Utility.trans("130.1", "Fluids ") : "") + + ((aCoverVariable & 8) != 0 ? GT_Utility.trans("131.1", "Items ") : "")); + return aCoverVariable; + } + + @Override + public boolean letsRedstoneGoIn(ForgeDirection side, int aCoverID, int aCoverVariable, ICoverable aTileEntity) { + return (aCoverVariable & 1) != 0; + } + + @Override + public boolean letsRedstoneGoOut(ForgeDirection side, int aCoverID, int aCoverVariable, ICoverable aTileEntity) { + return (aCoverVariable & 1) != 0; + } + + @Override + public boolean letsEnergyIn(ForgeDirection side, int aCoverID, int aCoverVariable, ICoverable aTileEntity) { + return (aCoverVariable & 2) != 0; + } + + @Override + public boolean letsEnergyOut(ForgeDirection side, int aCoverID, int aCoverVariable, ICoverable aTileEntity) { + return (aCoverVariable & 2) != 0; + } + + @Override + public boolean letsFluidIn(ForgeDirection side, int aCoverID, int aCoverVariable, Fluid aFluid, + ICoverable aTileEntity) { + return (aCoverVariable & 4) != 0; + } + + @Override + public boolean letsFluidOut(ForgeDirection side, int aCoverID, int aCoverVariable, Fluid aFluid, + ICoverable aTileEntity) { + return (aCoverVariable & 4) != 0; + } + + @Override + public boolean letsItemsIn(ForgeDirection side, int aCoverID, int aCoverVariable, int aSlot, + ICoverable aTileEntity) { + return (aCoverVariable & 8) != 0; + } + + @Override + public boolean letsItemsOut(ForgeDirection side, int aCoverID, int aCoverVariable, int aSlot, + ICoverable aTileEntity) { + return (aCoverVariable & 8) != 0; + } +} diff --git a/src/main/java/gregtech/api/objects/GT_Cover_None.java b/src/main/java/gregtech/api/objects/GT_Cover_None.java new file mode 100644 index 0000000000..279efe63d8 --- /dev/null +++ b/src/main/java/gregtech/api/objects/GT_Cover_None.java @@ -0,0 +1,237 @@ +package gregtech.api.objects; + +import static gregtech.api.enums.GT_Values.E; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraftforge.common.util.ForgeDirection; +import net.minecraftforge.fluids.Fluid; + +import gregtech.api.interfaces.tileentity.ICoverable; +import gregtech.api.util.GT_CoverBehavior; +import gregtech.api.util.ISerializableObject; + +public class GT_Cover_None extends GT_CoverBehavior { + + /** + * This is the Dummy, if there is no Cover + */ + public GT_Cover_None() {} + + @Override + public float getBlastProofLevel(ForgeDirection side, int aCoverID, int aCoverVariable, ICoverable aTileEntity) { + return 10.0F; + } + + @Override + public boolean letsRedstoneGoIn(ForgeDirection side, int aCoverID, int aCoverVariable, ICoverable aTileEntity) { + return true; + } + + @Override + public boolean letsRedstoneGoOut(ForgeDirection side, int aCoverID, int aCoverVariable, ICoverable aTileEntity) { + return true; + } + + @Override + public boolean letsEnergyIn(ForgeDirection side, int aCoverID, int aCoverVariable, ICoverable aTileEntity) { + return true; + } + + @Override + public boolean letsEnergyOut(ForgeDirection side, int aCoverID, int aCoverVariable, ICoverable aTileEntity) { + return true; + } + + @Override + public boolean letsFluidIn(ForgeDirection side, int aCoverID, int aCoverVariable, Fluid aFluid, + ICoverable aTileEntity) { + return true; + } + + @Override + public boolean letsFluidOut(ForgeDirection side, int aCoverID, int aCoverVariable, Fluid aFluid, + ICoverable aTileEntity) { + return true; + } + + @Override + public boolean letsItemsIn(ForgeDirection side, int aCoverID, int aCoverVariable, int aSlot, + ICoverable aTileEntity) { + return true; + } + + @Override + public boolean letsItemsOut(ForgeDirection side, int aCoverID, int aCoverVariable, int aSlot, + ICoverable aTileEntity) { + return true; + } + + @Override + public boolean isGUIClickable(ForgeDirection side, int aCoverID, int aCoverVariable, ICoverable aTileEntity) { + return true; + } + + @Override + public boolean manipulatesSidedRedstoneOutput(ForgeDirection side, int aCoverID, int aCoverVariable, + ICoverable aTileEntity) { + return false; + } + + @Override + public boolean onCoverRightclick(ForgeDirection side, int aCoverID, int aCoverVariable, ICoverable aTileEntity, + EntityPlayer aPlayer, float aX, float aY, float aZ) { + return false; + } + + @Override + public boolean onCoverRemoval(ForgeDirection side, int aCoverID, int aCoverVariable, ICoverable aTileEntity, + boolean aForced) { + return true; + } + + @Override + public int doCoverThings(ForgeDirection side, byte aInputRedstone, int aCoverID, int aCoverVariable, + ICoverable aTileEntity, long aTimer) { + return 0; + } + + @Override + public boolean isSimpleCover() { + return true; + } + + @Override + protected boolean isRedstoneSensitiveImpl(ForgeDirection side, int aCoverID, + ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity, long aTimer) { + return false; + } + + @Override + protected ISerializableObject.LegacyCoverData doCoverThingsImpl(ForgeDirection side, byte aInputRedstone, + int aCoverID, ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity, long aTimer) { + return aCoverVariable; + } + + @Override + protected boolean onCoverRightClickImpl(ForgeDirection side, int aCoverID, + ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity, EntityPlayer aPlayer, float aX, + float aY, float aZ) { + return false; + } + + @Override + protected ISerializableObject.LegacyCoverData onCoverScrewdriverClickImpl(ForgeDirection side, int aCoverID, + ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity, EntityPlayer aPlayer, float aX, + float aY, float aZ) { + return aCoverVariable; + } + + @Override + protected boolean onCoverShiftRightClickImpl(ForgeDirection side, int aCoverID, + ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity, EntityPlayer aPlayer) { + return false; + } + + @Override + protected boolean onCoverRemovalImpl(ForgeDirection side, int aCoverID, + ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity, boolean aForced) { + return true; + } + + @Override + protected String getDescriptionImpl(ForgeDirection side, int aCoverID, + ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity) { + return E; + } + + @Override + protected float getBlastProofLevelImpl(ForgeDirection side, int aCoverID, + ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity) { + return 10.0F; + } + + @Override + protected boolean letsRedstoneGoInImpl(ForgeDirection side, int aCoverID, + ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity) { + return true; + } + + @Override + protected boolean letsRedstoneGoOutImpl(ForgeDirection side, int aCoverID, + ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity) { + return true; + } + + @Override + protected boolean letsEnergyInImpl(ForgeDirection side, int aCoverID, + ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity) { + return true; + } + + @Override + protected boolean letsEnergyOutImpl(ForgeDirection side, int aCoverID, + ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity) { + return true; + } + + @Override + protected boolean letsFluidInImpl(ForgeDirection side, int aCoverID, + ISerializableObject.LegacyCoverData aCoverVariable, Fluid aFluid, ICoverable aTileEntity) { + return true; + } + + @Override + protected boolean letsFluidOutImpl(ForgeDirection side, int aCoverID, + ISerializableObject.LegacyCoverData aCoverVariable, Fluid aFluid, ICoverable aTileEntity) { + return true; + } + + @Override + protected boolean letsItemsInImpl(ForgeDirection side, int aCoverID, + ISerializableObject.LegacyCoverData aCoverVariable, int aSlot, ICoverable aTileEntity) { + return true; + } + + @Override + protected boolean letsItemsOutImpl(ForgeDirection side, int aCoverID, + ISerializableObject.LegacyCoverData aCoverVariable, int aSlot, ICoverable aTileEntity) { + return true; + } + + @Override + protected boolean manipulatesSidedRedstoneOutputImpl(ForgeDirection side, int aCoverID, + ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity) { + return false; + } + + @Override + protected boolean alwaysLookConnectedImpl(ForgeDirection side, int aCoverID, + ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity) { + return false; + } + + @Override + protected byte getRedstoneInputImpl(ForgeDirection side, byte aInputRedstone, int aCoverID, + ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity) { + return aInputRedstone; + } + + @Override + protected int getTickRateImpl(ForgeDirection side, int aCoverID, ISerializableObject.LegacyCoverData aCoverVariable, + ICoverable aTileEntity) { + return 0; + } + + @Override + protected byte getLensColorImpl(ForgeDirection side, int aCoverID, + ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity) { + return -1; + } + + @Override + protected ItemStack getDropImpl(ForgeDirection side, int aCoverID, + ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity) { + return null; + } +} diff --git a/src/main/java/gregtech/api/objects/GT_Fluid.java b/src/main/java/gregtech/api/objects/GT_Fluid.java new file mode 100644 index 0000000000..10824d6327 --- /dev/null +++ b/src/main/java/gregtech/api/objects/GT_Fluid.java @@ -0,0 +1,36 @@ +package gregtech.api.objects; + +import static gregtech.api.enums.Mods.GregTech; + +import net.minecraftforge.fluids.Fluid; + +import gregtech.api.GregTech_API; +import gregtech.api.fluid.GT_FluidFactory; + +/** + * @deprecated use {@link GT_FluidFactory#builder} + */ +@Deprecated +public class GT_Fluid extends Fluid implements Runnable { + + public final String mTextureName; + private final short[] mRGBa; + + public GT_Fluid(String aName, String aTextureName, short[] aRGBa) { + super(aName); + mRGBa = aRGBa; + mTextureName = aTextureName; + GregTech_API.sGTBlockIconload.add(this); + } + + @Override + public int getColor() { + return (Math.max(0, Math.min(255, mRGBa[0])) << 16) | (Math.max(0, Math.min(255, mRGBa[1])) << 8) + | Math.max(0, Math.min(255, mRGBa[2])); + } + + @Override + public void run() { + setIcons(GregTech_API.sBlockIcons.registerIcon(GregTech.getResourcePath("fluids", "fluid." + mTextureName))); + } +} diff --git a/src/main/java/gregtech/api/objects/GT_HashSet.java b/src/main/java/gregtech/api/objects/GT_HashSet.java new file mode 100644 index 0000000000..d42f194e5d --- /dev/null +++ b/src/main/java/gregtech/api/objects/GT_HashSet.java @@ -0,0 +1,91 @@ +package gregtech.api.objects; + +import java.util.AbstractSet; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; + +import net.minecraft.item.ItemStack; + +import gregtech.api.GregTech_API; +import gregtech.api.util.GT_Utility; + +public class GT_HashSet<E extends GT_ItemStack> extends AbstractSet<E> { + + private static final Object OBJECT = new Object(); + private final transient HashMap<GT_ItemStack, Object> map; + + public GT_HashSet() { + map = new HashMap<>(); + GregTech_API.sItemStackMappings.add(map); + } + + public GT_HashSet(Collection<? extends E> c) { + map = new HashMap<>(Math.max((int) (c.size() / .75f) + 1, 16)); + addAll(c); + GregTech_API.sItemStackMappings.add(map); + } + + public GT_HashSet(int initialCapacity, float loadFactor) { + map = new HashMap<>(initialCapacity, loadFactor); + GregTech_API.sItemStackMappings.add(map); + } + + public GT_HashSet(int initialCapacity) { + map = new HashMap<>(initialCapacity); + GregTech_API.sItemStackMappings.add(map); + } + + GT_HashSet(int initialCapacity, float loadFactor, boolean dummy) { + map = new LinkedHashMap<>(initialCapacity, loadFactor); + GregTech_API.sItemStackMappings.add(map); + } + + public Map<GT_ItemStack, Object> getMap() { + return map; + } + + @SuppressWarnings("unchecked") // The downcasting below will throw ClassCastException unless E is GT_ItemStack. + @Override + public Iterator<E> iterator() { + return (Iterator<E>) map.keySet() + .iterator(); + } + + @Override + public int size() { + return map.size(); + } + + @Override + public boolean isEmpty() { + return map.isEmpty(); + } + + @Override + public boolean contains(Object o) { + return map.containsKey(o); + } + + public boolean add(ItemStack aStack) { + if (GT_Utility.isStackInvalid(aStack)) return false; + return map.put(new GT_ItemStack(aStack), OBJECT) == null; + } + + @Override + public boolean add(E e) { + return map.put(e, OBJECT) == null; + } + + @Override + public boolean remove(Object o) { + return map.remove(o) == OBJECT; + } + + @Override + public void clear() { + map.clear(); + } +} diff --git a/src/main/java/gregtech/api/objects/GT_ItemStack.java b/src/main/java/gregtech/api/objects/GT_ItemStack.java new file mode 100644 index 0000000000..492655740d --- /dev/null +++ b/src/main/java/gregtech/api/objects/GT_ItemStack.java @@ -0,0 +1,107 @@ +package gregtech.api.objects; + +import net.minecraft.init.Items; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; + +import gregtech.api.enums.GT_Values; +import gregtech.api.util.GT_Utility; +import gregtech.api.util.item.ItemHolder; +import it.unimi.dsi.fastutil.Hash; + +/** + * An optimization of {@link ItemStack} to have a better {@code hashcode} and {@code equals} in order to improve + * {@code HashMap} and {@code Set} performance + */ +public class GT_ItemStack extends ItemHolder { + + /** + * A better {@link Hash.Strategy} for {@link ItemStack}. Implementation originally from {@code GT_ItemStack2}. + */ + public static final Hash.Strategy<ItemStack> ITEMSTACK_HASH_STRATEGY2 = new Hash.Strategy<>() { + + @Override + public int hashCode(ItemStack o) { + return o.getItem() + .hashCode() * 38197 + Items.feather.getDamage(o); + } + + @Override + public boolean equals(ItemStack a, ItemStack b) { + if (a == b) return true; + if (a == null || b == null) return false; + return a.getItem() == b.getItem() && Items.feather.getDamage(a) == Items.feather.getDamage(b); + } + }; + + public final Item mItem; + public final byte mStackSize; + public final short mMetaData; + + public GT_ItemStack(Item aItem, long aStackSize, long aMetaData) { + super(new ItemStack(aItem, 1, (int) aMetaData)); + mItem = aItem; + mStackSize = (byte) aStackSize; + mMetaData = (short) aMetaData; + } + + public GT_ItemStack(ItemStack aStack) { + this(aStack, false); + } + + public GT_ItemStack(ItemStack aStack, boolean wildcard) { + this( + aStack == null ? null : aStack.getItem(), + aStack == null ? 0 : aStack.stackSize, + aStack == null ? 0 : wildcard ? GT_Values.W : Items.feather.getDamage(aStack)); + } + + public GT_ItemStack(int aHashCode) { + this(GT_Utility.intToStack(aHashCode)); + } + + public final ItemStack toStack() { + if (mItem == null) return null; + return new ItemStack(mItem, 1, mMetaData); + } + + public final boolean isStackEqual(ItemStack aStack) { + return GT_Utility.areStacksEqual(toStack(), aStack); + } + + public final boolean isStackEqual(GT_ItemStack aStack) { + return GT_Utility.areStacksEqual(toStack(), aStack.toStack()); + } + + @Override + public boolean equals(Object aStack) { + if (aStack == this) return true; + if (aStack instanceof GT_ItemStack) { + return ((GT_ItemStack) aStack).mItem == mItem && ((GT_ItemStack) aStack).mMetaData == mMetaData; + } + return false; + } + + @Override + public int hashCode() { + return GT_Utility.stackToInt(toStack()); + } + + /** + * @see #internalCopyStack(ItemStack, boolean) + */ + public static ItemStack internalCopyStack(ItemStack aStack) { + return internalCopyStack(aStack, false); + } + + /** + * Replicates the copy behavior of {@link #toStack()} but for normal {@link ItemStack}s. + * + * @param aStack the stack to copy + * @param wildcard whether to use wildcard damage value + * @return a copy of the stack with stack size 1 and no NBT + */ + public static ItemStack internalCopyStack(ItemStack aStack, boolean wildcard) { + return new ItemStack(aStack.getItem(), 1, wildcard ? GT_Values.W : Items.feather.getDamage(aStack)); + } +} diff --git a/src/main/java/gregtech/api/objects/GT_ItemStack2.java b/src/main/java/gregtech/api/objects/GT_ItemStack2.java new file mode 100644 index 0000000000..aa93876830 --- /dev/null +++ b/src/main/java/gregtech/api/objects/GT_ItemStack2.java @@ -0,0 +1,41 @@ +package gregtech.api.objects; + +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; + +/** + * GT_ItemStack, but with a better hashCode(). Due to this change, it should not be placed in the same hash based data + * structure with GT_ItemStack. It also shouldn't be used to construct search query into a hash based data structure + * that contains GT_ItemStack. + * + * @deprecated See {@link GT_ItemStack#ITEMSTACK_HASH_STRATEGY2} + */ +@Deprecated +public class GT_ItemStack2 extends GT_ItemStack { + + public GT_ItemStack2(Item aItem, long aStackSize, long aMetaData) { + super(aItem, aStackSize, aMetaData); + } + + public GT_ItemStack2(ItemStack aStack) { + super(aStack); + } + + public GT_ItemStack2(ItemStack aStack, boolean wildcard) { + super(aStack, wildcard); + } + + @Override + public boolean equals(Object aStack) { + if (aStack == this) return true; + if (aStack instanceof GT_ItemStack) { + return ((GT_ItemStack) aStack).mItem == mItem && ((GT_ItemStack) aStack).mMetaData == mMetaData; + } + return false; + } + + @Override + public int hashCode() { + return mItem.hashCode() * 38197 + mMetaData; + } +} diff --git a/src/main/java/gregtech/api/objects/GT_MultiTexture.java b/src/main/java/gregtech/api/objects/GT_MultiTexture.java new file mode 100644 index 0000000000..9748ecb934 --- /dev/null +++ b/src/main/java/gregtech/api/objects/GT_MultiTexture.java @@ -0,0 +1,26 @@ +package gregtech.api.objects; + +import gregtech.api.interfaces.ITexture; + +/** + * <p> + * Lets Multiple ITextures Render overlay over each other.< + * </p> + * <p> + * I should have done this much earlier... + * </p> + * + * @deprecated Replaced by the {@link gregtech.api.render.TextureFactory} API. + */ +@Deprecated +public class GT_MultiTexture extends gregtech.common.render.GT_MultiTexture implements ITexture { + + public GT_MultiTexture(ITexture... aTextures) { + super(aTextures); + } + + @Override + public boolean isOldTexture() { + return true; + } +} diff --git a/src/main/java/gregtech/api/objects/GT_RenderedTexture.java b/src/main/java/gregtech/api/objects/GT_RenderedTexture.java new file mode 100644 index 0000000000..bbb22e7d36 --- /dev/null +++ b/src/main/java/gregtech/api/objects/GT_RenderedTexture.java @@ -0,0 +1,33 @@ +package gregtech.api.objects; + +import gregtech.api.enums.Dyes; +import gregtech.api.interfaces.IColorModulationContainer; +import gregtech.api.interfaces.IIconContainer; +import gregtech.api.interfaces.ITexture; + +@Deprecated +public class GT_RenderedTexture extends gregtech.common.render.GT_RenderedTexture + implements ITexture, IColorModulationContainer { + + @Deprecated + public short[] mRGBa; + + public GT_RenderedTexture(IIconContainer aIcon, short[] aRGBa, boolean aAllowAlpha) { + super(aIcon, aRGBa, aAllowAlpha, false, true, false); + if (aRGBa.length != 4) throw new IllegalArgumentException("RGBa doesn't have 4 Values @ GT_RenderedTexture"); + mRGBa = aRGBa; + } + + public GT_RenderedTexture(IIconContainer aIcon, short[] aRGBa) { + this(aIcon, aRGBa, true); + } + + public GT_RenderedTexture(IIconContainer aIcon) { + this(aIcon, Dyes._NULL.mRGBa); + } + + @Override + public boolean isOldTexture() { + return true; + } +} diff --git a/src/main/java/gregtech/api/objects/GT_SidedTexture.java b/src/main/java/gregtech/api/objects/GT_SidedTexture.java new file mode 100644 index 0000000000..d042ebede9 --- /dev/null +++ b/src/main/java/gregtech/api/objects/GT_SidedTexture.java @@ -0,0 +1,48 @@ +package gregtech.api.objects; + +import gregtech.api.enums.Dyes; +import gregtech.api.interfaces.IColorModulationContainer; +import gregtech.api.interfaces.IIconContainer; +import gregtech.api.interfaces.ITexture; + +/** + * @deprecated Replaced by the {@link gregtech.api.render.TextureFactory} API. + */ +@Deprecated +public class GT_SidedTexture extends gregtech.common.render.GT_SidedTexture + implements ITexture, IColorModulationContainer { + + @Deprecated + public short[] mRGBa; + + public GT_SidedTexture(IIconContainer aIcon0, IIconContainer aIcon1, IIconContainer aIcon2, IIconContainer aIcon3, + IIconContainer aIcon4, IIconContainer aIcon5, short[] aRGBa, boolean aAllowAlpha) { + super(aIcon0, aIcon1, aIcon2, aIcon3, aIcon4, aIcon5, aRGBa, aAllowAlpha); + + // Backwards Compat + GT_SidedTexture.this.mRGBa = aRGBa; + } + + public GT_SidedTexture(IIconContainer aIcon0, IIconContainer aIcon1, IIconContainer aIcon2, IIconContainer aIcon3, + IIconContainer aIcon4, IIconContainer aIcon5, short[] aRGBa) { + this(aIcon0, aIcon1, aIcon2, aIcon3, aIcon4, aIcon5, aRGBa, true); + } + + public GT_SidedTexture(IIconContainer aIcon0, IIconContainer aIcon1, IIconContainer aIcon2, IIconContainer aIcon3, + IIconContainer aIcon4, IIconContainer aIcon5) { + this(aIcon0, aIcon1, aIcon2, aIcon3, aIcon4, aIcon5, Dyes._NULL.mRGBa); + } + + public GT_SidedTexture(IIconContainer aBottom, IIconContainer aTop, IIconContainer aSides, short[] aRGBa) { + this(aBottom, aTop, aSides, aSides, aSides, aSides, aRGBa); + } + + public GT_SidedTexture(IIconContainer aBottom, IIconContainer aTop, IIconContainer aSides) { + this(aBottom, aTop, aSides, Dyes._NULL.mRGBa); + } + + @Override + public boolean isOldTexture() { + return true; + } +} diff --git a/src/main/java/gregtech/api/objects/GT_StdRenderedTexture.java b/src/main/java/gregtech/api/objects/GT_StdRenderedTexture.java new file mode 100644 index 0000000000..d4b8d16da6 --- /dev/null +++ b/src/main/java/gregtech/api/objects/GT_StdRenderedTexture.java @@ -0,0 +1,46 @@ +package gregtech.api.objects; + +import net.minecraft.block.Block; +import net.minecraft.client.renderer.RenderBlocks; +import net.minecraftforge.common.util.ForgeDirection; + +import gregtech.api.enums.Dyes; +import gregtech.api.interfaces.IIconContainer; +import gregtech.api.util.LightingHelper; + +/** + * This ITexture implementation extends the GT_RenderedTexture class to render with bottom side flipped as with dumb + * blocks rendering. It is used in Ore blocks rendering so they better blends with dumb block ores from vanilla or other + * mods, when seen from bottom. + * + * @deprecated Replaced by the {@link gregtech.api.render.TextureFactory} API. + */ +@Deprecated +public class GT_StdRenderedTexture extends GT_RenderedTexture { + + @SuppressWarnings("unused") + public GT_StdRenderedTexture(IIconContainer aIcon, short[] aRGBa, boolean aAllowAlpha) { + super(aIcon, aRGBa, aAllowAlpha); + } + + public GT_StdRenderedTexture(IIconContainer aIcon, short[] aRGBa) { + super(aIcon, aRGBa, true); + } + + @SuppressWarnings("unused") + public GT_StdRenderedTexture(IIconContainer aIcon) { + super(aIcon, Dyes._NULL.mRGBa); + } + + @Override + public void renderYNeg(RenderBlocks aRenderer, Block aBlock, int aX, int aY, int aZ) { + LightingHelper lighting = new LightingHelper(aRenderer); + lighting.setupLightingYNeg(aBlock, aX, aY, aZ) + .setupColor(ForgeDirection.DOWN, mRGBa); + aRenderer.renderFaceYNeg(aBlock, aX, aY, aZ, mIconContainer.getIcon()); + if (mIconContainer.getOverlayIcon() != null) { + lighting.setupColor(ForgeDirection.DOWN, 0xffffff); + aRenderer.renderFaceYNeg(aBlock, aX, aY, aZ, mIconContainer.getOverlayIcon()); + } + } +} diff --git a/src/main/java/gregtech/api/objects/GT_UO_Dimension.java b/src/main/java/gregtech/api/objects/GT_UO_Dimension.java new file mode 100644 index 0000000000..af82c35dab --- /dev/null +++ b/src/main/java/gregtech/api/objects/GT_UO_Dimension.java @@ -0,0 +1,54 @@ +package gregtech.api.objects; + +import java.util.Random; + +import net.minecraftforge.common.config.ConfigCategory; + +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; + +public class GT_UO_Dimension { + + private final BiMap<String, GT_UO_Fluid> fFluids; + private int maxChance; + public String Dimension = "null"; + + public GT_UO_Dimension(ConfigCategory aConfigCategory) { // TODO CONFIGURE + fFluids = HashBiMap.create(); + if (aConfigCategory.containsKey("Dimension")) { + aConfigCategory.get("Dimension").comment = "Dimension ID or Class Name"; + Dimension = aConfigCategory.get("Dimension") + .getString(); + } + maxChance = 0; + // GT_FML_LOGGER.info("GT UO "+aConfigCategory.getName()+" Dimension:"+Dimension); + for (int i = 0; i < aConfigCategory.getChildren() + .size(); i++) { + GT_UO_Fluid fluid = new GT_UO_Fluid( + (ConfigCategory) aConfigCategory.getChildren() + .toArray()[i]); + fFluids.put(fluid.Registry, fluid); + maxChance += fluid.Chance; + } + } + + public GT_UO_Fluid getRandomFluid(Random aRandom) { + int random = aRandom.nextInt(1000); + for (BiMap.Entry<String, GT_UO_Fluid> fl : fFluids.entrySet()) { + int chance = fl.getValue().Chance * 1000 / maxChance; + if (random <= chance) return fl.getValue(); + // GT_FML_LOGGER.info("GT UO "+fl.getValue().Registry+" Chance:"+chance+" Random:"+random); + random -= chance; + } + return null; + } + + public String getUOFluidKey(GT_UO_Fluid uoFluid) { + return fFluids.inverse() + .get(uoFluid); + } + + public GT_UO_Fluid getUOFluid(String key) { + return fFluids.get(key); + } +} diff --git a/src/main/java/gregtech/api/objects/GT_UO_DimensionList.java b/src/main/java/gregtech/api/objects/GT_UO_DimensionList.java new file mode 100644 index 0000000000..95d4246cb6 --- /dev/null +++ b/src/main/java/gregtech/api/objects/GT_UO_DimensionList.java @@ -0,0 +1,98 @@ +package gregtech.api.objects; + +import net.minecraftforge.common.DimensionManager; +import net.minecraftforge.common.config.ConfigCategory; +import net.minecraftforge.common.config.Configuration; + +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; + +public class GT_UO_DimensionList { + + private Configuration fConfig; + private String fCategory; + private final BiMap<String, GT_UO_Dimension> fDimensionList; + + public int[] blackList = new int[0]; + + public GT_UO_DimensionList() { + fDimensionList = HashBiMap.create(); + } + + public GT_UO_Dimension GetDimension(int aDimension) { + if (CheckBlackList(aDimension)) return null; + if (fDimensionList.containsKey(Integer.toString(aDimension))) + return fDimensionList.get(Integer.toString(aDimension)); + for (BiMap.Entry<String, GT_UO_Dimension> dl : fDimensionList.entrySet()) + if (DimensionManager.getProvider(aDimension) + .getClass() + .getName() + .contains(dl.getValue().Dimension)) return dl.getValue(); + return fDimensionList.get("Default"); + } + + private boolean CheckBlackList(int aDimensionId) { + try { + return java.util.Arrays.binarySearch(blackList, aDimensionId) >= 0; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + public void SetConfigValues(String aDimensionName, String aDimension, String aName, String aRegistry, + int aMinAmount, int aMaxAmount, int aChance, int aDecreasePerOperationAmount) { + String Category = fCategory + "." + aDimensionName; + fConfig.get(Category, "Dimension", aDimension) + .getString(); + Category += "." + aName; + fConfig.get(Category, "Registry", aRegistry) + .getString(); + fConfig.get(Category, "MinAmount", aMinAmount) + .getInt(aMinAmount); + fConfig.get(Category, "MaxAmount", aMaxAmount) + .getInt(aMaxAmount); + fConfig.get(Category, "Chance", aChance) + .getInt(aChance); + fConfig.get(Category, "DecreasePerOperationAmount", aDecreasePerOperationAmount) + .getInt(aDecreasePerOperationAmount); + // IT IS IN BUCKETS!!! + } + + public void SetDafultValues() { + SetConfigValues("Overworld", "0", "gas_natural_gas", "gas_natural_gas", 0, 700, 20, 7); + SetConfigValues("Overworld", "0", "liquid_light_oil", "liquid_light_oil", 0, 650, 20, 6); + SetConfigValues("Overworld", "0", "liquid_medium_oil", "liquid_medium_oil", 0, 600, 20, 5); + SetConfigValues("Overworld", "0", "liquid_heavy_oil", "liquid_heavy_oil", 0, 550, 20, 4); + SetConfigValues("Overworld", "0", "oil", "oil", 0, 600, 20, 5); + SetConfigValues("Moon", "Moon", "helium-3", "helium-3", 24, 128, 100, 1); + } + + public void getConfig(Configuration aConfig, String aCategory) { + fCategory = aCategory; + fConfig = aConfig; + if (!fConfig.hasCategory(fCategory)) SetDafultValues(); + + fConfig.setCategoryComment(fCategory, "Config Underground Fluids (Delete this Category for regenerate)"); + fConfig.setCategoryComment( + fCategory + ".Default", + "Set Default Generating (Use this Category for Default settings)"); + fConfig.setCategoryComment(fCategory + ".Overworld", "Set Overworld Generating"); + fConfig.setCategoryComment(fCategory + ".Moon", "Set Moon Generating"); + + blackList = new int[] { -1, 1 }; + blackList = aConfig.get(fCategory, "DimBlackList", blackList, "Dimension IDs Black List") + .getIntList(); + java.util.Arrays.sort(blackList); + + for (int i = 0; i < fConfig.getCategory(fCategory) + .getChildren() + .size(); i++) { + GT_UO_Dimension Dimension = new GT_UO_Dimension( + (ConfigCategory) fConfig.getCategory(fCategory) + .getChildren() + .toArray()[i]); + fDimensionList.put(Dimension.Dimension, Dimension); + } + } +} diff --git a/src/main/java/gregtech/api/objects/GT_UO_Fluid.java b/src/main/java/gregtech/api/objects/GT_UO_Fluid.java new file mode 100644 index 0000000000..7f9898e02e --- /dev/null +++ b/src/main/java/gregtech/api/objects/GT_UO_Fluid.java @@ -0,0 +1,69 @@ +package gregtech.api.objects; + +import static gregtech.common.GT_UndergroundOil.DIVIDER; + +import java.util.Random; + +import net.minecraftforge.common.config.ConfigCategory; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidRegistry; + +public class GT_UO_Fluid { + + public String Registry = "null"; + public int MaxAmount = 0; + public int MinAmount = 0; + public int Chance = 0; + public int DecreasePerOperationAmount = 5; + + public GT_UO_Fluid(ConfigCategory aConfigCategory) { // TODO CONFIGURE + if (aConfigCategory.containsKey("Registry")) { + aConfigCategory.get("Registry").comment = "Fluid registry name"; + Registry = aConfigCategory.get("Registry") + .getString(); + } + if (aConfigCategory.containsKey("MaxAmount")) { + aConfigCategory + .get("MaxAmount").comment = "Max amount generation (per operation, sets the VeinData) 80000 MAX"; + MaxAmount = aConfigCategory.get("MaxAmount") + .getInt(0); + } + if (aConfigCategory.containsKey("MinAmount")) { + aConfigCategory.get("MinAmount").comment = "Min amount generation (per operation, sets the VeinData) 0 MIN"; + MinAmount = aConfigCategory.get("MinAmount") + .getInt(0); + } + if (aConfigCategory.containsKey("Chance")) { + aConfigCategory + .get("Chance").comment = "Chance generating (weighted chance!, there will be a fluid in chunk always!)"; + Chance = aConfigCategory.get("Chance") + .getInt(0); + } + if (aConfigCategory.containsKey("DecreasePerOperationAmount")) { + aConfigCategory.get( + "DecreasePerOperationAmount").comment = "Decrease per operation (actual fluid gained works like (Litre)VeinData/5000)"; + DecreasePerOperationAmount = aConfigCategory.get("DecreasePerOperationAmount") + .getInt(5); + } + // GT_FML_LOGGER.info("GT UO "+aConfigCategory.getName()+" Fluid:"+Registry+" Max:"+MaxAmount+" + // Min:"+MinAmount+" Chance:"+Chance); + } + + public Fluid getFluid() { + try { + return FluidRegistry.getFluid(this.Registry); + } catch (Exception e) { + return null; + } + } + + public int getRandomAmount(Random aRandom) { // generates some random ass number that correlates to extraction + // speeds + int smax = (int) Math.floor(Math.pow(MaxAmount * 100.d * DIVIDER, 0.2d)); // use scaled max and min values for + // the randomness to make high values + // more rare. + double smin = Math.pow(MinAmount * 100.d * DIVIDER, 0.2d); + double samount = Math.max(smin, aRandom.nextInt(smax) + aRandom.nextDouble()); + return (int) (Math.pow(samount, 5) / 100); // reverses the computation above + } +} diff --git a/src/main/java/gregtech/api/objects/ItemData.java b/src/main/java/gregtech/api/objects/ItemData.java new file mode 100644 index 0000000000..779e45ac8b --- /dev/null +++ b/src/main/java/gregtech/api/objects/ItemData.java @@ -0,0 +1,122 @@ +package gregtech.api.objects; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import net.minecraft.item.ItemStack; + +import gregtech.api.enums.Materials; +import gregtech.api.enums.OrePrefixes; + +public class ItemData { + + private static final MaterialStack[] EMPTY_MATERIALSTACK_ARRAY = new MaterialStack[0]; + + public final List<Object> mExtraData = new GT_ArrayList<>(false, 1); + public final OrePrefixes mPrefix; + public final MaterialStack mMaterial; + public final MaterialStack[] mByProducts; + public boolean mBlackListed = false; + public ItemStack mUnificationTarget = null; + + public ItemData(OrePrefixes aPrefix, Materials aMaterial, boolean aBlackListed) { + mPrefix = aPrefix; + mMaterial = aMaterial == null ? null : new MaterialStack(aMaterial, aPrefix.mMaterialAmount); + mBlackListed = aBlackListed; + mByProducts = aPrefix.mSecondaryMaterial == null || aPrefix.mSecondaryMaterial.mMaterial == null + ? EMPTY_MATERIALSTACK_ARRAY + : new MaterialStack[] { aPrefix.mSecondaryMaterial.clone() }; + } + + public ItemData(OrePrefixes aPrefix, Materials aMaterial) { + this(aPrefix, aMaterial, false); + } + + public ItemData(MaterialStack aMaterial, MaterialStack... aByProducts) { + mPrefix = null; + mMaterial = aMaterial.mMaterial == null ? null : aMaterial.clone(); + mBlackListed = true; + if (aByProducts == null) { + mByProducts = EMPTY_MATERIALSTACK_ARRAY; + } else { + MaterialStack[] tByProducts = aByProducts.length < 1 ? EMPTY_MATERIALSTACK_ARRAY + : new MaterialStack[aByProducts.length]; + int j = 0; + for (MaterialStack aByProduct : aByProducts) + if (aByProduct != null && aByProduct.mMaterial != null) tByProducts[j++] = aByProduct.clone(); + mByProducts = j > 0 ? new MaterialStack[j] : EMPTY_MATERIALSTACK_ARRAY; + System.arraycopy(tByProducts, 0, mByProducts, 0, mByProducts.length); + } + } + + public ItemData(Materials aMaterial, long aAmount, MaterialStack... aByProducts) { + this(new MaterialStack(aMaterial, aAmount), aByProducts); + } + + public ItemData(Materials aMaterial, long aAmount, Materials aByProduct, long aByProductAmount) { + this(new MaterialStack(aMaterial, aAmount), new MaterialStack(aByProduct, aByProductAmount)); + } + + public ItemData(ItemData... aData) { + mPrefix = null; + mBlackListed = true; + + ArrayList<MaterialStack> aList = new ArrayList<>(), rList = new ArrayList<>(); + + for (ItemData tData : aData) if (tData != null) { + if (tData.hasValidMaterialData() && tData.mMaterial.mAmount > 0) aList.add(tData.mMaterial.clone()); + for (MaterialStack tMaterial : tData.mByProducts) if (tMaterial.mAmount > 0) aList.add(tMaterial.clone()); + } + + for (MaterialStack aMaterial : aList) { + boolean temp = true; + for (MaterialStack tMaterial : rList) if (aMaterial.mMaterial == tMaterial.mMaterial) { + tMaterial.mAmount += aMaterial.mAmount; + temp = false; + break; + } + if (temp) rList.add(aMaterial.clone()); + } + + rList.sort((a, b) -> Long.compare(b.mAmount, a.mAmount)); + + if (rList.isEmpty()) { + mMaterial = null; + } else { + mMaterial = rList.get(0); + rList.remove(0); + } + + mByProducts = rList.toArray(new MaterialStack[0]); + } + + public final boolean hasValidPrefixMaterialData() { + return mPrefix != null && mMaterial != null && mMaterial.mMaterial != null; + } + + public final boolean hasValidPrefixData() { + return mPrefix != null; + } + + public final boolean hasValidMaterialData() { + return mMaterial != null && mMaterial.mMaterial != null; + } + + public final ArrayList<MaterialStack> getAllMaterialStacks() { + ArrayList<MaterialStack> rList = new ArrayList<>(); + if (hasValidMaterialData()) rList.add(mMaterial); + rList.addAll(Arrays.asList(mByProducts)); + return rList; + } + + public final MaterialStack getByProduct(int aIndex) { + return aIndex >= 0 && aIndex < mByProducts.length ? mByProducts[aIndex] : null; + } + + @Override + public String toString() { + if (mPrefix == null || mMaterial == null || mMaterial.mMaterial == null) return ""; + return String.valueOf(mPrefix.name() + mMaterial.mMaterial.mName); + } +} diff --git a/src/main/java/gregtech/api/objects/MaterialStack.java b/src/main/java/gregtech/api/objects/MaterialStack.java new file mode 100644 index 0000000000..0a433e0d99 --- /dev/null +++ b/src/main/java/gregtech/api/objects/MaterialStack.java @@ -0,0 +1,70 @@ +package gregtech.api.objects; + +import gregtech.api.enums.Materials; +import gregtech.api.util.GT_Utility; + +public class MaterialStack implements Cloneable { + + public long mAmount; + public Materials mMaterial; + + public MaterialStack(Materials aMaterial, long aAmount) { + mMaterial = aMaterial == null ? Materials._NULL : aMaterial; + mAmount = aAmount; + } + + public MaterialStack copy(long aAmount) { + return new MaterialStack(mMaterial, aAmount); + } + + @Override + public MaterialStack clone() { + try { + return (MaterialStack) super.clone(); + } catch (Exception e) { + return new MaterialStack(mMaterial, mAmount); + } + } + + @Override + public boolean equals(Object aObject) { + if (aObject == this) return true; + if (aObject == null) return false; + if (aObject instanceof Materials) return aObject == mMaterial; + if (aObject instanceof MaterialStack) return ((MaterialStack) aObject).mMaterial == mMaterial + && (mAmount < 0 || ((MaterialStack) aObject).mAmount < 0 || ((MaterialStack) aObject).mAmount == mAmount); + return false; + } + + @Override + public String toString() { + return toString(false); + } + + public String toString(boolean single) { + String temp1 = "", temp2 = mMaterial.getToolTip(true), temp3 = "", temp4 = ""; + if (mAmount > 1) { + temp4 = GT_Utility.toSubscript(mAmount); + } + if ((!single || mAmount > 1) && isMaterialListComplex(this)) { + temp1 = "("; + temp3 = ")"; + } + return temp1 + temp2 + temp3 + temp4; + } + + private boolean isMaterialListComplex(MaterialStack materialStack) { + if (materialStack.mMaterial.mMaterialList.size() > 1) { + return true; + } + if (materialStack.mMaterial.mMaterialList.size() == 0) { + return false; + } + return isMaterialListComplex(materialStack.mMaterial.mMaterialList.get(0)); + } + + @Override + public int hashCode() { + return mMaterial.hashCode(); + } +} diff --git a/src/main/java/gregtech/api/objects/ObjMap.java b/src/main/java/gregtech/api/objects/ObjMap.java new file mode 100644 index 0000000000..badc673464 --- /dev/null +++ b/src/main/java/gregtech/api/objects/ObjMap.java @@ -0,0 +1,239 @@ +package gregtech.api.objects; + +import java.util.Arrays; + +/** + * Object-2-object map based on IntIntMap4a + */ +public class ObjMap<K, V> { + + private static final Object FREE_KEY = new Object(); + private static final Object REMOVED_KEY = new Object(); + + /** Keys and values */ + private Object[] m_data; + + /** Value for the null key (if inserted into a map) */ + private Object m_nullValue; + + private boolean m_hasNull; + + /** Fill factor, must be between (0 and 1) */ + private final float m_fillFactor; + /** We will resize a map once it reaches this size */ + private int m_threshold; + /** Current map size */ + private int m_size; + /** Mask to calculate the original position */ + private int m_mask; + /** Mask to wrap the actual array pointer */ + private int m_mask2; + + public ObjMap(final int size, final float fillFactor) { + if (fillFactor <= 0 || fillFactor >= 1) throw new IllegalArgumentException("FillFactor must be in (0, 1)"); + if (size <= 0) throw new IllegalArgumentException("Size must be positive!"); + final int capacity = arraySize(size, fillFactor); + m_mask = capacity - 1; + m_mask2 = capacity * 2 - 1; + m_fillFactor = fillFactor; + + m_data = new Object[capacity * 2]; + Arrays.fill(m_data, FREE_KEY); + + m_threshold = (int) (capacity * fillFactor); + } + + @SuppressWarnings("unchecked") + public V get(final K key) { + if (key == null) return (V) m_nullValue; // we null it on remove, so safe not to check a flag here + + int ptr = (key.hashCode() & m_mask) << 1; + Object k = m_data[ptr]; + + if (k == FREE_KEY) return null; // end of chain already + if (k.equals(key)) // we check FREE and REMOVED prior to this call + return (V) m_data[ptr + 1]; + while (true) { + ptr = (ptr + 2) & m_mask2; // that's next index + k = m_data[ptr]; + if (k == FREE_KEY) return null; + if (k.equals(key)) return (V) m_data[ptr + 1]; + } + } + + @SuppressWarnings("unchecked") + public V put(final K key, final V value) { + if (key == null) return insertNullKey(value); + + int ptr = getStartIndex(key) << 1; + Object k = m_data[ptr]; + + if (k == FREE_KEY) // end of chain already + { + m_data[ptr] = key; + m_data[ptr + 1] = value; + if (m_size >= m_threshold) rehash(m_data.length * 2); // size is set inside + else++m_size; + return null; + } else if (k.equals(key)) // we check FREE and REMOVED prior to this call + { + final Object ret = m_data[ptr + 1]; + m_data[ptr + 1] = value; + return (V) ret; + } + + int firstRemoved = -1; + if (k == REMOVED_KEY) firstRemoved = ptr; // we may find a key later + + while (true) { + ptr = (ptr + 2) & m_mask2; // that's next index calculation + k = m_data[ptr]; + if (k == FREE_KEY) { + if (firstRemoved != -1) ptr = firstRemoved; + m_data[ptr] = key; + m_data[ptr + 1] = value; + if (m_size >= m_threshold) rehash(m_data.length * 2); // size is set inside + else++m_size; + return null; + } else if (k.equals(key)) { + final Object ret = m_data[ptr + 1]; + m_data[ptr + 1] = value; + return (V) ret; + } else if (k == REMOVED_KEY) { + if (firstRemoved == -1) firstRemoved = ptr; + } + } + } + + @SuppressWarnings("unchecked") + public V remove(final K key) { + if (key == null) return removeNullKey(); + + int ptr = getStartIndex(key) << 1; + Object k = m_data[ptr]; + if (k == FREE_KEY) return null; // end of chain already + else if (k.equals(key)) // we check FREE and REMOVED prior to this call + { + --m_size; + if (m_data[(ptr + 2) & m_mask2] == FREE_KEY) m_data[ptr] = FREE_KEY; + else m_data[ptr] = REMOVED_KEY; + final V ret = (V) m_data[ptr + 1]; + m_data[ptr + 1] = null; + return ret; + } + while (true) { + ptr = (ptr + 2) & m_mask2; // that's next index calculation + k = m_data[ptr]; + if (k == FREE_KEY) return null; + else if (k.equals(key)) { + --m_size; + if (m_data[(ptr + 2) & m_mask2] == FREE_KEY) m_data[ptr] = FREE_KEY; + else m_data[ptr] = REMOVED_KEY; + final V ret = (V) m_data[ptr + 1]; + m_data[ptr + 1] = null; + return ret; + } + } + } + + @SuppressWarnings("unchecked") + private V insertNullKey(final V value) { + if (m_hasNull) { + final Object ret = m_nullValue; + m_nullValue = value; + return (V) ret; + } else { + m_nullValue = value; + ++m_size; + return null; + } + } + + @SuppressWarnings("unchecked") + private V removeNullKey() { + if (m_hasNull) { + final Object ret = m_nullValue; + m_nullValue = null; + m_hasNull = false; + --m_size; + return (V) ret; + } else { + return null; + } + } + + public int size() { + return m_size; + } + + @SuppressWarnings("unchecked") + private void rehash(final int newCapacity) { + m_threshold = (int) (newCapacity / 2 * m_fillFactor); + m_mask = newCapacity / 2 - 1; + m_mask2 = newCapacity - 1; + + final int oldCapacity = m_data.length; + final Object[] oldData = m_data; + + m_data = new Object[newCapacity]; + Arrays.fill(m_data, FREE_KEY); + + m_size = m_hasNull ? 1 : 0; + + for (int i = 0; i < oldCapacity; i += 2) { + final Object oldKey = oldData[i]; + if (oldKey != FREE_KEY && oldKey != REMOVED_KEY) put((K) oldKey, (V) oldData[i + 1]); + } + } + + public int getStartIndex(final Object key) { + // key is not null here + return key.hashCode() & m_mask; + } + + /* Taken from FastUtil implementation */ + + /** + * Return the least power of two greater than or equal to the specified value. + * + * <p> + * Note that this function will return 1 when the argument is 0. + * + * @param x a long integer smaller than or equal to 2<sup>62</sup>. + * @return the least power of two greater than or equal to the specified value. + */ + public static long nextPowerOfTwo(long x) { + if (x == 0) return 1; + x--; + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + x |= x >> 8; + x |= x >> 16; + return (x | x >> 32) + 1; + } + + /** + * Returns the least power of two smaller than or equal to 2<sup>30</sup> and larger than or equal to + * <code>Math.ceil( expected / f )</code>. + * + * @param expected the expected number of elements in a hash table. + * @param f the load factor. + * @return the minimum possible size for a backing array. + * @throws IllegalArgumentException if the necessary size is larger than 2<sup>30</sup>. + */ + public static int arraySize(final int expected, final float f) { + final long s = Math.max(2, nextPowerOfTwo((long) Math.ceil(expected / f))); + if (s > (1 << 30)) throw new IllegalArgumentException( + "Too large (" + expected + " expected elements with load factor " + f + ")"); + return (int) s; + } + + // taken from FastUtil + private static final int INT_PHI = 0x9E3779B9; + + public static int phiMix(final int x) { + final int h = x * INT_PHI; + return h ^ (h >> 16); + } +} diff --git a/src/main/java/gregtech/api/objects/XSTR.java b/src/main/java/gregtech/api/objects/XSTR.java new file mode 100644 index 0000000000..33823d3ebd --- /dev/null +++ b/src/main/java/gregtech/api/objects/XSTR.java @@ -0,0 +1,246 @@ +package gregtech.api.objects; + +import java.util.Random; +import java.util.concurrent.atomic.AtomicLong; + +/* + * TODO: Check the validity of the algorithm. + * There is a claim that this particular implementation is not faithful to the articles it links, skewing the + * distribution. + */ +/** + * XSTR - Xorshift ThermiteRandom Modified by Bogdan-G 03.06.2016 version 0.0.4 + * <p> + * A subclass of java.util.random that implements the Xorshift random number generator + * <p> + * - it is 30% faster than the generator from Java's library - it produces random sequences of higher quality than + * java.util.Random - this class also provides a clone() function + * <p> + * Usage: XSRandom rand = new XSRandom(); //Instantiation x = rand.nextInt(); //pull a random number + * <p> + * To use the class in legacy code, you may also instantiate an XSRandom object and assign it to a java.util.Random + * object: java.util.Random rand = new XSRandom(); + * <p> + * for an explanation of the algorithm, see http://demesos.blogspot.com/2011/09/pseudo-random-number-generators.html + * + * @author Wilfried Elmenreich University of Klagenfurt/Lakeside Labs http://www.elmenreich.tk + * <p> + * This code is released under the GNU Lesser General Public License Version 3 + * http://www.gnu.org/licenses/lgpl-3.0.txt + */ +public class XSTR extends Random { + + private static final long serialVersionUID = 6208727693524452904L; + private long seed; + private long last; + private static final long GAMMA = 0x9e3779b97f4a7c15L; + private static final int PROBE_INCREMENT = 0x9e3779b9; + private static final long SEEDER_INCREMENT = 0xbb67ae8584caa73bL; + private static final double DOUBLE_UNIT = 0x1.0p-53; // 1.0 / (1L << 53) + private static final float FLOAT_UNIT = 0x1.0p-24f; // 1.0f / (1 << 24) + private static final AtomicLong seedUniquifier = new AtomicLong(8682522807148012L); + public static final XSTR XSTR_INSTANCE = new XSTR() { + + @Override + public synchronized void setSeed(long seed) { + if (!Thread.currentThread() + .getStackTrace()[2].getClassName() + .equals(Random.class.getName())) + throw new NoSuchMethodError("This is meant to be shared!, leave seed state alone!"); + } + }; + + /* + * MODIFIED BY: Robotia Modification: Implemented Random class seed generator + */ + /** + * Creates a new pseudo random number generator. The seed is initialized to the current time, as if by + * <code>setSeed(System.currentTimeMillis());</code>. + */ + public XSTR() { + this(seedUniquifier() ^ System.nanoTime()); + } + + private static long seedUniquifier() { + // L'Ecuyer, "Tables of Linear Congruential Generators of + // Different Sizes and Good Lattice Structure", 1999 + for (;;) { + long current = seedUniquifier.get(); + long next = current * 181783497276652981L; + if (seedUniquifier.compareAndSet(current, next)) { + return next; + } + } + } + + /** + * Creates a new pseudo random number generator, starting with the specified seed, using + * <code>setSeed(seed);</code>. + * + * @param seed the initial seed + */ + public XSTR(long seed) { + this.seed = seed; + } + + @Override + public boolean nextBoolean() { + return next(1) != 0; + } + + @Override + public double nextDouble() { + return (((long) (next(26)) << 27) + next(27)) * DOUBLE_UNIT; + } + + /** + * Returns the current state of the seed, can be used to clone the object + * + * @return the current seed + */ + public synchronized long getSeed() { + return seed; + } + + /** + * Sets the seed for this pseudo random number generator. As described above, two instances of the same random + * class, starting with the same seed, produce the same results, if the same methods are called. + * + * @param seed the new seed + */ + @Override + public synchronized void setSeed(long seed) { + this.seed = seed; + } + + /** + * @return Returns an XSRandom object with the same state as the original + */ + @Override + public XSTR clone() { + return new XSTR(getSeed()); + } + + /** + * Implementation of George Marsaglia's Xorshift random generator that is 30% faster and better quality than the + * built-in java.util.random. + * + * @param nbits number of bits to shift the result for + * @return a random integer + * @see <a href="https://www.javamex.com/tutorials/random_numbers/xorshift.shtml">the Xorshift article</a> + */ + @Override + public int next(int nbits) { + long x = seed; + x ^= (x << 21); + x ^= (x >>> 35); + x ^= (x << 4); + seed = x; + x &= ((1L << nbits) - 1); + return (int) x; + } + + boolean haveNextNextGaussian = false; + double nextNextGaussian = 0; + + @Override + public synchronized double nextGaussian() { + // See Knuth, ACP, Section 3.4.1 Algorithm C. + if (haveNextNextGaussian) { + haveNextNextGaussian = false; + return nextNextGaussian; + } else { + double v1, v2, s; + do { + v1 = 2 * nextDouble() - 1; // between -1 and 1 + v2 = 2 * nextDouble() - 1; // between -1 and 1 + s = v1 * v1 + v2 * v2; + } while (s >= 1 || s == 0); + double multiplier = StrictMath.sqrt(-2 * StrictMath.log(s) / s); + nextNextGaussian = v2 * multiplier; + haveNextNextGaussian = true; + return v1 * multiplier; + } + } + + /** + * Returns a pseudorandom, uniformly distributed {@code int} value between 0 (inclusive) and the specified value + * (exclusive), drawn from this random number generator's sequence. The general contract of {@code nextInt} is that + * one {@code int} value in the specified range is pseudorandomly generated and returned. All {@code bound} possible + * {@code int} values are produced with (approximately) equal probability. The method {@code nextInt(int bound)} is + * implemented by class {@code Random} as if by: + * + * <pre> + * {@code + * public int nextInt(int bound) { + * if (bound <= 0) + * throw new IllegalArgumentException("bound must be positive"); + * + * if ((bound & -bound) == bound) // i.e., bound is a power of 2 + * return (int)((bound * (long)next(31)) >> 31); + * + * int bits, val; + * do { + * bits = next(31); + * val = bits % bound; + * } while (bits - val + (bound-1) < 0); + * return val; + * }} + * </pre> + * + * <p> + * The next method is only approximately an unbiased source of independently chosen bits. If it were a perfect + * source of randomly chosen bits, then the algorithm shown would choose {@code int} values from the stated range + * with perfect uniformity. + * <p> + * The algorithm is slightly tricky. It rejects values that would result in an uneven distribution (due to the fact + * that 2^31 is not divisible by n). The probability of a value being rejected depends on n. The worst case is + * n=2^30+1, for which the probability of a reject is 1/2, and the expected number of iterations before the loop + * terminates is 2. + * <p> + * The algorithm treats the case where n is a power of two specially: it returns the correct number of high-order + * bits from the underlying pseudo-random number generator. In the absence of special treatment, the correct number + * of <i>low-order</i> bits would be returned. Linear congruential pseudo-random number generators such as the one + * implemented by this class are known to have short periods in the sequence of values of their low-order bits. + * Thus, this special case greatly increases the length of the sequence of values returned by successive calls to + * this method if n is a small power of two. + * + * @param bound the upper bound (exclusive). Must be positive. + * @return the next pseudorandom, uniformly distributed {@code int} value between zero (inclusive) and {@code bound} + * (exclusive) from this random number generator's sequence + * @throws IllegalArgumentException if bound is not positive + * @since 1.2 + */ + @Override + public int nextInt(int bound) { + last = seed ^ (seed << 21); + last ^= (last >>> 35); + last ^= (last << 4); + seed = last; + int out = (int) last % bound; + return (out < 0) ? -out : out; + } + + @Override + public int nextInt() { + return next(32); + } + + @Override + public float nextFloat() { + return next(24) * FLOAT_UNIT; + } + + @Override + public long nextLong() { + // it's okay that the bottom word remains signed. + return ((long) (next(32)) << 32) + next(32); + } + + @Override + public void nextBytes(byte[] bytes_arr) { + for (int iba = 0, lenba = bytes_arr.length; iba < lenba;) + for (int rndba = nextInt(), nba = Math.min(lenba - iba, Integer.SIZE / Byte.SIZE); nba-- + > 0; rndba >>= Byte.SIZE) bytes_arr[iba++] = (byte) rndba; + } +} diff --git a/src/main/java/gregtech/api/objects/blockupdate/BlockUpdateHandler.java b/src/main/java/gregtech/api/objects/blockupdate/BlockUpdateHandler.java new file mode 100644 index 0000000000..e8f084ea34 --- /dev/null +++ b/src/main/java/gregtech/api/objects/blockupdate/BlockUpdateHandler.java @@ -0,0 +1,117 @@ +package gregtech.api.objects.blockupdate; + +import java.util.HashMap; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.entity.EntityClientPlayerMP; +import net.minecraft.world.ChunkCoordIntPair; +import net.minecraft.world.World; +import net.minecraft.world.chunk.Chunk; + +import appeng.api.util.WorldCoord; +import cpw.mods.fml.common.FMLCommonHandler; +import cpw.mods.fml.common.eventhandler.SubscribeEvent; +import cpw.mods.fml.common.gameevent.TickEvent.ClientTickEvent; +import cpw.mods.fml.common.gameevent.TickEvent.Phase; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import gregtech.api.enums.TickTime; + +// this is a middleware for block updates +// Greg's BaseMetaTileEntity uses World::markBlockForUpdate() to update the texture of a machine +// World::markBlockForUpdate() triggers block updates of all blocks within the same chunk +// this class makes sure chunk updates are perfmormed only once even if several blocks requested update +// (valid obly for requests made using BlockUpdateHandler::enqueueBlockUpdate) +// and introduces a per chunk cooldown to slow the things a bit +// cause too frequent updates are not necessary for visual appearance of a block + +@SideOnly(Side.CLIENT) +public class BlockUpdateHandler { + + public static final int MIN_UPDATE_COOLDOWN = TickTime.SECOND / 2; + public static final int MAX_UPDATE_COOLDOWN = TickTime.SECOND; + + public final static BlockUpdateHandler Instance = new BlockUpdateHandler(); + + private BlockUpdateHandler() { + + blocksToUpdate = new HashMap<ChunkCoordIntPair, WorldCoord>(); + cooldowns = new HashMap<ChunkCoordIntPair, RandomCooldown>(); + + FMLCommonHandler.instance() + .bus() + .register(this); + } + + public void enqueueBlockUpdate(World world, WorldCoord pos) { + + var player = getPlayer(); + + if (world != player.worldObj) return; + + ResetDataIfPlayerWorldChanged(player); + + blocksToUpdate.put(getBlockChunkCoords(world, pos), pos); + } + + @SubscribeEvent + public void OnClientTickEvent(ClientTickEvent event) { + + if (event.phase != Phase.START) return; + + ResetDataIfPlayerWorldChanged(getPlayer()); + + var it = blocksToUpdate.entrySet() + .iterator(); + + while (it.hasNext()) { + + var entry = it.next(); + ChunkCoordIntPair chunkCoords = entry.getKey(); + WorldCoord blockCoords = entry.getValue(); + + RandomCooldown cooldown = cooldowns.get(chunkCoords); + + if (cooldown == null) { + cooldown = new RandomCooldown(MIN_UPDATE_COOLDOWN, MAX_UPDATE_COOLDOWN); + cooldowns.put(chunkCoords, cooldown); + } + + if (!cooldown.hasPassed(internalTickCounter)) continue; + + currWorld.markBlockForUpdate(blockCoords.x, blockCoords.y, blockCoords.z); + cooldown.set(internalTickCounter); + it.remove(); + } + + ++internalTickCounter; + } + + private EntityClientPlayerMP getPlayer() { + return Minecraft.getMinecraft().thePlayer; + } + + private void ResetDataIfPlayerWorldChanged(EntityClientPlayerMP player) { + + if (player == null) return; + + World playerWorld = player.worldObj; + + if (currWorld != playerWorld) { + blocksToUpdate.clear(); + cooldowns.clear(); + currWorld = playerWorld; + } + } + + private ChunkCoordIntPair getBlockChunkCoords(World world, WorldCoord pos) { + + Chunk chunk = world.getChunkFromBlockCoords(pos.x, pos.z); + return chunk.getChunkCoordIntPair(); + } + + private HashMap<ChunkCoordIntPair, WorldCoord> blocksToUpdate; + private HashMap<ChunkCoordIntPair, RandomCooldown> cooldowns; + private World currWorld = null; + private long internalTickCounter = 0; +} diff --git a/src/main/java/gregtech/api/objects/blockupdate/Cooldown.java b/src/main/java/gregtech/api/objects/blockupdate/Cooldown.java new file mode 100644 index 0000000000..e00fc1c770 --- /dev/null +++ b/src/main/java/gregtech/api/objects/blockupdate/Cooldown.java @@ -0,0 +1,27 @@ +package gregtech.api.objects.blockupdate; + +public class Cooldown { + + public Cooldown(int aLengthInTicks) { + + if (aLengthInTicks <= 0) throw new IllegalArgumentException("length should be a positive non-zero number"); + + this.lengthInTicks = aLengthInTicks; + this.lastTimeStarted = 0; + } + + public void set(long currTickTime) { + lastTimeStarted = currTickTime; + } + + public boolean hasPassed(long currTickTime) { + return currTickTime - lastTimeStarted >= lengthInTicks; + } + + public long getLastTimeStarted() { + return lastTimeStarted; + } + + private long lastTimeStarted; + protected int lengthInTicks; +} diff --git a/src/main/java/gregtech/api/objects/blockupdate/RandomCooldown.java b/src/main/java/gregtech/api/objects/blockupdate/RandomCooldown.java new file mode 100644 index 0000000000..d275c29744 --- /dev/null +++ b/src/main/java/gregtech/api/objects/blockupdate/RandomCooldown.java @@ -0,0 +1,31 @@ +package gregtech.api.objects.blockupdate; + +import static gregtech.api.objects.XSTR.XSTR_INSTANCE; + +public class RandomCooldown extends Cooldown { + + public RandomCooldown(int aMinLengthInTicks, int aMaxLengthInTicks) { + + super(aMinLengthInTicks); + + if (aMinLengthInTicks <= 0) + throw new IllegalArgumentException("min length should be a positive non-zero number"); + if (aMaxLengthInTicks <= 0) + throw new IllegalArgumentException("max length should be a positive non-zero number"); + if (aMinLengthInTicks > aMaxLengthInTicks) + throw new IllegalArgumentException("min length should be less or equal to max length"); + + this.minLengthInTicks = aMinLengthInTicks; + this.maxLengthInTicks = aMaxLengthInTicks; + } + + @Override + public void set(long currTickTime) { + + super.set(currTickTime); + lengthInTicks = minLengthInTicks + XSTR_INSTANCE.nextInt(maxLengthInTicks - minLengthInTicks + 1); + } + + private int minLengthInTicks; + private int maxLengthInTicks; +} diff --git a/src/main/java/gregtech/api/objects/iterators/MergedIterator.java b/src/main/java/gregtech/api/objects/iterators/MergedIterator.java new file mode 100644 index 0000000000..961c98e81a --- /dev/null +++ b/src/main/java/gregtech/api/objects/iterators/MergedIterator.java @@ -0,0 +1,31 @@ +package gregtech.api.objects.iterators; + +import java.util.Iterator; + +public class MergedIterator<T> implements Iterator<T> { + + private final Iterator<T>[] inners; + private int current; + + @SafeVarargs + public MergedIterator(Iterator<T>... iterators) { + inners = iterators; + current = 0; + } + + public boolean hasNext() { + while (current < inners.length && !inners[current].hasNext()) { + current++; + } + + return current < inners.length; + } + + public T next() { + while (current < inners.length && !inners[current].hasNext()) { + current++; + } + + return inners[current].next(); + } +} diff --git a/src/main/java/gregtech/api/objects/overclockdescriber/EUNoOverclockDescriber.java b/src/main/java/gregtech/api/objects/overclockdescriber/EUNoOverclockDescriber.java new file mode 100644 index 0000000000..1e29e2d812 --- /dev/null +++ b/src/main/java/gregtech/api/objects/overclockdescriber/EUNoOverclockDescriber.java @@ -0,0 +1,110 @@ +package gregtech.api.objects.overclockdescriber; + +import static gregtech.api.util.GT_Utility.trans; + +import javax.annotation.ParametersAreNonnullByDefault; + +import gregtech.api.util.GT_OverclockCalculator; +import gregtech.api.util.GT_Recipe; +import gregtech.api.util.GT_Utility; +import gregtech.api.util.MethodsReturnNonnullByDefault; +import gregtech.nei.RecipeDisplayInfo; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +public class EUNoOverclockDescriber extends OverclockDescriber { + + /** + * Amperage of the recipemap. + */ + protected final int amperage; + + public EUNoOverclockDescriber(byte tier, int amperage) { + super(tier); + if (amperage < 1) { + throw new IllegalArgumentException("Amperage cannot be lower than 1"); + } + this.amperage = amperage; + } + + @Override + public GT_OverclockCalculator createCalculator(GT_OverclockCalculator template, GT_Recipe recipe) { + return GT_OverclockCalculator.ofNoOverclock(recipe); + } + + @Override + public String getTierString() { + return GT_Utility.getColoredTierNameFromTier(tier); + } + + @Override + public final void drawEnergyInfo(RecipeDisplayInfo recipeInfo) { + if (recipeInfo.calculator.getDuration() > 0 && recipeInfo.calculator.getConsumption() > 0) { + recipeInfo.drawText(trans("152", "Total: ") + getTotalPowerString(recipeInfo.calculator)); + } + drawEnergyInfoImpl(recipeInfo); + } + + /** + * Override this to draw custom info about the energy this object can handle on NEI recipe GUI, minus total + * power usage. + */ + protected void drawEnergyInfoImpl(RecipeDisplayInfo recipeInfo) { + if (recipeInfo.calculator.getConsumption() <= 0) { + return; + } + recipeInfo.drawText(trans("153", "Usage: ") + getEUtDisplay(recipeInfo.calculator)); + if (shouldShowAmperage(recipeInfo.calculator)) { + recipeInfo.drawText(trans("154", "Voltage: ") + getVoltageString(recipeInfo.calculator)); + recipeInfo.drawText(trans("155", "Amperage: ") + getAmperageString(recipeInfo.calculator)); + } + } + + protected String getTotalPowerString(GT_OverclockCalculator calculator) { + return GT_Utility.formatNumbers(calculator.getConsumption() * calculator.getDuration()) + " EU"; + } + + /** + * @return If amperage should be shown on NEI. + */ + protected boolean shouldShowAmperage(GT_OverclockCalculator calculator) { + return amperage != 1; + } + + /** + * @return Whole EU/t usage, without tier display. + */ + protected String getEUtWithoutTier(GT_OverclockCalculator calculator) { + return GT_Utility.formatNumbers(calculator.getConsumption()) + " EU/t"; + } + + /** + * @return Whole EU/t usage, with tier display. + */ + protected String getEUtWithTier(GT_OverclockCalculator calculator) { + return getEUtWithoutTier(calculator) + GT_Utility.getTierNameWithParentheses(calculator.getConsumption()); + } + + /** + * @return Whole EU/t usage. Also displays voltage tier if it should be shown. + */ + protected String getEUtDisplay(GT_OverclockCalculator calculator) { + return shouldShowAmperage(calculator) ? getEUtWithoutTier(calculator) : getEUtWithTier(calculator); + } + + /** + * @return EU/t usage, divided by amperage. With tier display. + */ + protected String getVoltageString(GT_OverclockCalculator calculator) { + long voltage = computeVoltageForEURate(calculator.getConsumption()); + return GT_Utility.formatNumbers(voltage) + " EU/t" + GT_Utility.getTierNameWithParentheses(voltage); + } + + protected String getAmperageString(GT_OverclockCalculator calculator) { + return GT_Utility.formatNumbers(amperage); + } + + protected long computeVoltageForEURate(long euPerTick) { + return euPerTick / amperage; + } +} diff --git a/src/main/java/gregtech/api/objects/overclockdescriber/EUOverclockDescriber.java b/src/main/java/gregtech/api/objects/overclockdescriber/EUOverclockDescriber.java new file mode 100644 index 0000000000..9d53711515 --- /dev/null +++ b/src/main/java/gregtech/api/objects/overclockdescriber/EUOverclockDescriber.java @@ -0,0 +1,80 @@ +package gregtech.api.objects.overclockdescriber; + +import static gregtech.api.enums.GT_Values.V; +import static gregtech.api.util.GT_Utility.trans; + +import javax.annotation.ParametersAreNonnullByDefault; + +import com.google.common.primitives.Ints; + +import gregtech.GT_Mod; +import gregtech.api.util.GT_OverclockCalculator; +import gregtech.api.util.GT_Recipe; +import gregtech.api.util.GT_Utility; +import gregtech.api.util.MethodsReturnNonnullByDefault; +import gregtech.nei.RecipeDisplayInfo; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +public class EUOverclockDescriber extends EUNoOverclockDescriber { + + public EUOverclockDescriber(byte tier, int amperage) { + super(tier, amperage); + } + + @Override + public GT_OverclockCalculator createCalculator(GT_OverclockCalculator template, GT_Recipe recipe) { + return template.setEUt(Ints.saturatedCast(V[tier] * amperage)); + } + + @Override + protected void drawEnergyInfoImpl(RecipeDisplayInfo recipeInfo) { + if (!wasOverclocked(recipeInfo.calculator)) { + super.drawEnergyInfoImpl(recipeInfo); + return; + } + + recipeInfo.drawText(trans("153", "Usage: ") + getEUtDisplay(recipeInfo.calculator)); + if (shouldShowAmperage(recipeInfo.calculator)) { + recipeInfo.drawText(trans("154", "Voltage: ") + getVoltageString(recipeInfo.calculator)); + } + if (GT_Mod.gregtechproxy.mNEIOriginalVoltage) { + EUNoOverclockDescriber originalPower = new EUNoOverclockDescriber(tier, amperage); + GT_OverclockCalculator originalPowerCalculator = GT_OverclockCalculator.ofNoOverclock(recipeInfo.recipe) + .calculate(); + recipeInfo + .drawText(trans("275", "Original usage: ") + originalPower.getEUtDisplay(originalPowerCalculator)); + } + if (shouldShowAmperage(recipeInfo.calculator)) { + recipeInfo.drawText(trans("155", "Amperage: ") + getAmperageString(recipeInfo.calculator)); + } + } + + @Override + protected String getEUtWithoutTier(GT_OverclockCalculator calculator) { + return decorateWithOverclockLabel(super.getEUtWithoutTier(calculator), calculator); + } + + @Override + protected String getEUtWithTier(GT_OverclockCalculator calculator) { + return this.getEUtWithoutTier(calculator) + GT_Utility.getTierNameWithParentheses(calculator.getConsumption()); + } + + @Override + protected String getVoltageString(GT_OverclockCalculator calculator) { + long voltage = computeVoltageForEURate(calculator.getConsumption()); + return decorateWithOverclockLabel(GT_Utility.formatNumbers(voltage) + " EU/t", calculator) + + GT_Utility.getTierNameWithParentheses(voltage); + } + + protected String decorateWithOverclockLabel(String s, GT_OverclockCalculator calculator) { + if (wasOverclocked(calculator)) { + s += " (OC)"; + } + return s; + } + + protected boolean wasOverclocked(GT_OverclockCalculator calculator) { + return calculator.getPerformedOverclocks() > 0; + } +} diff --git a/src/main/java/gregtech/api/objects/overclockdescriber/FusionOverclockDescriber.java b/src/main/java/gregtech/api/objects/overclockdescriber/FusionOverclockDescriber.java new file mode 100644 index 0000000000..8f64f3146e --- /dev/null +++ b/src/main/java/gregtech/api/objects/overclockdescriber/FusionOverclockDescriber.java @@ -0,0 +1,63 @@ +package gregtech.api.objects.overclockdescriber; + +import javax.annotation.ParametersAreNonnullByDefault; + +import net.minecraft.util.EnumChatFormatting; + +import gregtech.api.enums.GT_Values; +import gregtech.api.util.GT_OverclockCalculator; +import gregtech.api.util.GT_Recipe; +import gregtech.api.util.GT_Utility; +import gregtech.api.util.MethodsReturnNonnullByDefault; +import gregtech.nei.formatter.FusionSpecialValueFormatter; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +public class FusionOverclockDescriber extends EUOverclockDescriber { + + protected final long capableStartup; + + public FusionOverclockDescriber(byte energyTier, long capableStartup) { + super(energyTier, 1); + this.capableStartup = capableStartup; + } + + @Override + public GT_OverclockCalculator createCalculator(GT_OverclockCalculator template, GT_Recipe recipe) { + return super.createCalculator(template, recipe) + .limitOverclockCount(overclock(recipe.mSpecialValue, recipe.mEUt)) + .setEUtIncreasePerOC(getEUtIncreasePerOC()) + .setDurationDecreasePerOC(getDurationDecreasePerOC()); + } + + protected int getEUtIncreasePerOC() { + return 1; + } + + protected int getDurationDecreasePerOC() { + return 1; + } + + @Override + public String getTierString() { + return GT_Values.TIER_COLORS[tier] + "MK " + getFusionTier() + EnumChatFormatting.RESET; + } + + @Override + public boolean canHandle(GT_Recipe recipe) { + byte tier = GT_Utility.getTier(recipe.mEUt); + if (this.tier < tier) { + return false; + } + return this.capableStartup >= recipe.mSpecialValue; + } + + protected int overclock(long startEnergy, long voltage) { + // Fusion Computer tier - recipe tier + return Math.max(getFusionTier() - FusionSpecialValueFormatter.getFusionTier(startEnergy, voltage), 0); + } + + protected int getFusionTier() { + return this.tier - 5; // Mk1 <-> LuV + } +} diff --git a/src/main/java/gregtech/api/objects/overclockdescriber/OverclockDescriber.java b/src/main/java/gregtech/api/objects/overclockdescriber/OverclockDescriber.java new file mode 100644 index 0000000000..0b253c95fa --- /dev/null +++ b/src/main/java/gregtech/api/objects/overclockdescriber/OverclockDescriber.java @@ -0,0 +1,106 @@ +package gregtech.api.objects.overclockdescriber; + +import static gregtech.api.util.GT_Utility.trans; + +import javax.annotation.ParametersAreNonnullByDefault; + +import gregtech.GT_Mod; +import gregtech.api.util.GT_OverclockCalculator; +import gregtech.api.util.GT_Recipe; +import gregtech.api.util.GT_Utility; +import gregtech.api.util.MethodsReturnNonnullByDefault; +import gregtech.nei.RecipeDisplayInfo; + +/** + * Provides an overclock behavior that will run on machines with the ability to draw information about it on NEI. + * <p> + * Implement {@link gregtech.api.interfaces.tileentity.IOverclockDescriptionProvider} for corresponding machine to use + * derivative of this class when looking up NEI recipe catalyst. + */ +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +public abstract class OverclockDescriber { + + /** + * Tier of the (maybe virtual) machine this object belongs to. + */ + protected final byte tier; + + public OverclockDescriber(byte tier) { + this.tier = tier; + } + + /** + * @return Tier of this object. Used to limit recipes shown on NEI, based on recipe EU/t. + */ + public final byte getTier() { + return tier; + } + + /** + * @return Tier display of this object, shown on NEI header in a form of {@code Machine Name (tier)} + */ + public abstract String getTierString(); + + /** + * Creates overclock calculator from given template. This template should be used instead of building from the + * ground to avoid issues coming from different caller using different templates, but it's not applicable when using + * {@link GT_OverclockCalculator#ofNoOverclock(GT_Recipe)}. + * + * @param template Calculator that can be used as template. Recipe EU/t and duration are already set. + * @param recipe Recipe to calculate. + */ + public abstract GT_OverclockCalculator createCalculator(GT_OverclockCalculator template, GT_Recipe recipe); + + /** + * Draws info about the energy this object can handle on NEI recipe GUI. + */ + public abstract void drawEnergyInfo(RecipeDisplayInfo recipeInfo); + + public void drawDurationInfo(RecipeDisplayInfo recipeInfo) { + if (getDurationTicks(recipeInfo.calculator) <= 0) return; + + String textToDraw = trans("158", "Time: "); + if (GT_Mod.gregtechproxy.mNEIRecipeSecondMode) { + textToDraw += getDurationStringSeconds(recipeInfo.calculator); + if (getDurationSeconds(recipeInfo.calculator) <= 1.0d) { + textToDraw += String.format(" (%s)", getDurationStringTicks(recipeInfo.calculator)); + } + } else { + textToDraw += getDurationStringTicks(recipeInfo.calculator); + } + recipeInfo.drawText(textToDraw); + } + + /** + * Used to limit the shown recipes when searching recipes with NEI recipe catalyst. Unless overridden, this method + * doesn't do anything special (except for a bit worse performance). + * <p> + * In order to make use of this method, {@link gregtech.api.recipe.RecipeMapBuilder#useCustomFilterForNEI} + * should be enabled for the recipemap. + * + * @return If this object can handle the supplied recipe + */ + public boolean canHandle(GT_Recipe recipe) { + byte tier = GT_Utility.getTier(recipe.mEUt); + return this.tier >= tier; + } + + private int getDurationTicks(GT_OverclockCalculator calculator) { + return calculator.getDuration(); + } + + private double getDurationSeconds(GT_OverclockCalculator calculator) { + return 0.05d * getDurationTicks(calculator); + } + + private String getDurationStringSeconds(GT_OverclockCalculator calculator) { + return GT_Utility.formatNumbers(getDurationSeconds(calculator)) + GT_Utility.trans("161", " secs"); + } + + private String getDurationStringTicks(GT_OverclockCalculator calculator) { + String ticksString = getDurationTicks(calculator) == 1 ? GT_Utility.trans("209.1", " tick") + : GT_Utility.trans("209", " ticks"); + return GT_Utility.formatNumbers(getDurationTicks(calculator)) + ticksString; + } +} diff --git a/src/main/java/gregtech/api/objects/overclockdescriber/SteamOverclockDescriber.java b/src/main/java/gregtech/api/objects/overclockdescriber/SteamOverclockDescriber.java new file mode 100644 index 0000000000..5da64d4028 --- /dev/null +++ b/src/main/java/gregtech/api/objects/overclockdescriber/SteamOverclockDescriber.java @@ -0,0 +1,64 @@ +package gregtech.api.objects.overclockdescriber; + +import static gregtech.api.util.GT_Utility.trans; + +import javax.annotation.ParametersAreNonnullByDefault; + +import net.minecraft.util.StatCollector; + +import gregtech.api.enums.SteamVariant; +import gregtech.api.util.GT_OverclockCalculator; +import gregtech.api.util.GT_Recipe; +import gregtech.api.util.GT_Utility; +import gregtech.api.util.MethodsReturnNonnullByDefault; +import gregtech.nei.RecipeDisplayInfo; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +public class SteamOverclockDescriber extends OverclockDescriber { + + private final SteamVariant steamVariant; + private final int euPerTickMultiplier; + private final int durationMultiplier; + + public SteamOverclockDescriber(SteamVariant steamVariant, int euPerTickMultiplier, int durationMultiplier) { + super((byte) 1); // recipe tier is always LV + this.steamVariant = steamVariant; + this.euPerTickMultiplier = euPerTickMultiplier; + this.durationMultiplier = durationMultiplier; + } + + @Override + public String getTierString() { + return StatCollector.translateToLocal("GT5U.steam_variant." + steamVariant.toString()); + } + + @Override + public GT_OverclockCalculator createCalculator(GT_OverclockCalculator template, GT_Recipe recipe) { + return GT_OverclockCalculator.ofNoOverclock(recipe) + .setEUtDiscount(euPerTickMultiplier) + .setSpeedBoost(durationMultiplier); + } + + @Override + public void drawEnergyInfo(RecipeDisplayInfo recipeInfo) { + if (recipeInfo.calculator.getConsumption() <= 0) return; + + recipeInfo.drawText(trans("152", "Total: ") + getTotalPowerString(recipeInfo.calculator)); + recipeInfo.drawText(trans("153", "Usage: ") + getSteamUsageString(recipeInfo.calculator)); + } + + private String getTotalPowerString(GT_OverclockCalculator calculator) { + return GT_Utility.formatNumbers(convertEUToSteam(calculator.getConsumption() * calculator.getDuration())) + + " Steam"; + } + + private String getSteamUsageString(GT_OverclockCalculator calculator) { + return GT_Utility.formatNumbers(20 * convertEUToSteam(calculator.getConsumption())) + " L/s Steam"; + } + + private static long convertEUToSteam(long eu) { + // 2L normal steam == 1EU + return 2 * eu; + } +} diff --git a/src/main/java/gregtech/api/recipe/BasicUIProperties.java b/src/main/java/gregtech/api/recipe/BasicUIProperties.java new file mode 100644 index 0000000000..fde86785b2 --- /dev/null +++ b/src/main/java/gregtech/api/recipe/BasicUIProperties.java @@ -0,0 +1,251 @@ +package gregtech.api.recipe; + +import java.awt.Rectangle; +import java.util.List; +import java.util.function.IntFunction; +import java.util.function.Supplier; + +import javax.annotation.Nullable; +import javax.annotation.ParametersAreNonnullByDefault; + +import org.apache.commons.lang3.tuple.Pair; + +import com.gtnewhorizons.modularui.api.drawable.FallbackableUITexture; +import com.gtnewhorizons.modularui.api.drawable.IDrawable; +import com.gtnewhorizons.modularui.api.math.Pos2d; +import com.gtnewhorizons.modularui.api.math.Size; +import com.gtnewhorizons.modularui.common.widget.ProgressBar; + +import gregtech.api.gui.modularui.FallbackableSteamTexture; +import gregtech.api.gui.modularui.SteamTexture; +import gregtech.api.util.FieldsAreNonnullByDefault; +import gregtech.api.util.MethodsReturnNonnullByDefault; + +/** + * Data object to store properties, used to draw both basic machine GUI and NEI recipe GUI, mainly GUI widgets. + * Not all the info used to draw NEI are listed here, see also {@link NEIRecipeProperties}. + * <p> + * Use {@link #builder()} for creation. + */ +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +@FieldsAreNonnullByDefault +public final class BasicUIProperties { + + /** + * Starts constructing BasicUIProperties. + */ + public static BasicUIPropertiesBuilder builder() { + return new BasicUIPropertiesBuilder(); + } + + /** + * Creates new builder from this instance. + */ + public BasicUIPropertiesBuilder toBuilder() { + return new BasicUIPropertiesBuilder().maxItemInputs(maxItemInputs) + .maxItemOutputs(maxItemOutputs) + .maxFluidInputs(maxFluidInputs) + .maxFluidOutputs(maxFluidOutputs) + .slotOverlays(slotOverlays) + .slotOverlaysSteam(slotOverlaysSteam) + .progressBarTexture(progressBarTexture) + .progressBarTextureSteam(progressBarTextureSteam) + .progressBarDirection(progressBarDirection) + .progressBarSize(progressBarSize) + .progressBarPos(progressBarPos) + .useProgressBar(useProgressBar) + .useSpecialSlot(useSpecialSlot) + .neiTransferRect(neiTransferRect) + .neiTransferRectId(neiTransferRectId) + .specialTextures(specialTextures) + .specialTexturesSteam(specialTexturesSteam) + .logo(logo) + .logoSize(logoSize) + .logoPos(logoPos) + .itemInputPositionsGetter(itemInputPositionsGetter) + .itemOutputPositionsGetter(itemOutputPositionsGetter) + .specialItemPositionGetter(specialItemPositionGetter) + .fluidInputPositionsGetter(fluidInputPositionsGetter) + .fluidOutputPositionsGetter(fluidOutputPositionsGetter) + .amperage(amperage); + } + + /** + * How many item inputs does this recipemap usually has at most. + * It does not actually restrict number of items used in the recipe. + */ + public final int maxItemInputs; + /** + * How many item outputs does this recipemap usually has at most. + * It does not actually restrict number of items used in the recipe. + */ + public final int maxItemOutputs; + /** + * How many fluid inputs does this recipemap usually has at most. + * It does not actually restrict number of items used in the recipe. + */ + public final int maxFluidInputs; + /** + * How many fluid outputs does this recipemap usually has at most. + * It does not actually restrict number of items used in the recipe. + */ + public final int maxFluidOutputs; + + private final SlotOverlayGetter<IDrawable> slotOverlays; + private final SlotOverlayGetter<SteamTexture> slotOverlaysSteam; + + /** + * Progressbar used for BasicMachine GUI and NEI. + */ + @Nullable + public final FallbackableUITexture progressBarTexture; + /** + * Progressbar used for steam machine GUI. + */ + @Nullable + public final FallbackableSteamTexture progressBarTextureSteam; + /** + * Direction of progressbar animation. + */ + public final ProgressBar.Direction progressBarDirection; + /** + * Size of the progressbar. (20, 36) by default. + */ + public final Size progressBarSize; + /** + * Position of the progressbar. (78, 24) by default. + */ + public final Pos2d progressBarPos; + /** + * Image size in the direction of progressbar. Used for non-smooth rendering. + */ + public final int progressBarImageSize; + + /** + * If progressbar should be added. + */ + public final boolean useProgressBar; + + /** + * If special slot has its usage for this GUI. + */ + public final boolean useSpecialSlot; + + /** + * GUI area where clicking shows up all the recipes available. + */ + public final List<Rectangle> neiTransferRect; + /** + * ID used to open NEI recipe GUI when progressbar is clicked. + */ + @Nullable + public final String neiTransferRectId; + + /** + * Additional textures shown on GUI. + */ + public final List<Pair<IDrawable, Pair<Size, Pos2d>>> specialTextures; + /** + * Additional textures shown on steam machine GUI. + */ + public final List<Pair<SteamTexture, Pair<Size, Pos2d>>> specialTexturesSteam; + + /** + * Logo shown on GUI. GregTech logo by default. + */ + public final IDrawable logo; + /** + * Size of logo. (17, 17) by default. + */ + public final Size logoSize; + /** + * Position of logo. (152, 63) by default. + */ + public final Pos2d logoPos; + + public final IntFunction<List<Pos2d>> itemInputPositionsGetter; + public final IntFunction<List<Pos2d>> itemOutputPositionsGetter; + public final Supplier<Pos2d> specialItemPositionGetter; + public final IntFunction<List<Pos2d>> fluidInputPositionsGetter; + public final IntFunction<List<Pos2d>> fluidOutputPositionsGetter; + + /** + * Amperage for the recipemap. Even though this is placed at frontend because backend logic doesn't need it, + * some machine logic also use this variable. + */ + public final int amperage; + + BasicUIProperties(int maxItemInputs, int maxItemOutputs, int maxFluidInputs, int maxFluidOutputs, + SlotOverlayGetter<IDrawable> slotOverlays, SlotOverlayGetter<SteamTexture> slotOverlaysSteam, + @Nullable FallbackableUITexture progressBarTexture, @Nullable FallbackableSteamTexture progressBarTextureSteam, + ProgressBar.Direction progressBarDirection, Size progressBarSize, Pos2d progressBarPos, boolean useProgressBar, + boolean useSpecialSlot, List<Rectangle> neiTransferRect, @Nullable String neiTransferRectId, + List<Pair<IDrawable, Pair<Size, Pos2d>>> specialTextures, + List<Pair<SteamTexture, Pair<Size, Pos2d>>> specialTexturesSteam, IDrawable logo, Size logoSize, Pos2d logoPos, + IntFunction<List<Pos2d>> itemInputPositionsGetter, IntFunction<List<Pos2d>> itemOutputPositionsGetter, + Supplier<Pos2d> specialItemPositionGetter, IntFunction<List<Pos2d>> fluidInputPositionsGetter, + IntFunction<List<Pos2d>> fluidOutputPositionsGetter, int amperage) { + if (maxItemInputs < 0 || maxItemOutputs < 0 || maxFluidInputs < 0 || maxFluidOutputs < 0) { + throw new IllegalArgumentException( + "maxItemInputs, maxItemOutputs, maxFluidInputs and maxFluidOutputs cannot be negative"); + } + if (amperage < 1) { + throw new IllegalArgumentException("Amperage cannot be lower than 1"); + } + this.maxItemInputs = maxItemInputs; + this.maxItemOutputs = maxItemOutputs; + this.maxFluidInputs = maxFluidInputs; + this.maxFluidOutputs = maxFluidOutputs; + this.slotOverlays = slotOverlays; + this.slotOverlaysSteam = slotOverlaysSteam; + this.progressBarTexture = progressBarTexture; + this.progressBarTextureSteam = progressBarTextureSteam; + this.progressBarDirection = progressBarDirection; + this.progressBarSize = progressBarSize; + this.progressBarPos = progressBarPos; + this.useProgressBar = useProgressBar; + this.useSpecialSlot = useSpecialSlot; + this.neiTransferRect = neiTransferRect; + this.neiTransferRectId = neiTransferRectId; + this.specialTextures = specialTextures; + this.specialTexturesSteam = specialTexturesSteam; + this.logo = logo; + this.logoSize = logoSize; + this.logoPos = logoPos; + this.itemInputPositionsGetter = itemInputPositionsGetter; + this.itemOutputPositionsGetter = itemOutputPositionsGetter; + this.specialItemPositionGetter = specialItemPositionGetter; + this.fluidInputPositionsGetter = fluidInputPositionsGetter; + this.fluidOutputPositionsGetter = fluidOutputPositionsGetter; + this.amperage = amperage; + + this.progressBarImageSize = switch (progressBarDirection) { + case UP, DOWN -> progressBarSize.height; + case CIRCULAR_CW -> Math.max(progressBarSize.width, progressBarSize.height); + default -> progressBarSize.width; + }; + } + + /** + * Retrieves overlay for slot, with given matching conditions. + */ + @Nullable + public IDrawable getOverlayForSlot(int index, boolean isFluid, boolean isOutput, boolean isSpecial) { + return slotOverlays.apply(index, isFluid, isOutput, isSpecial); + } + + /** + * Retrieves overlay for slot of steam machines, with given matching conditions. + */ + @Nullable + public SteamTexture getOverlayForSlotSteam(int index, boolean isFluid, boolean isOutput, boolean isSpecial) { + return slotOverlaysSteam.apply(index, isFluid, isOutput, isSpecial); + } + + public interface SlotOverlayGetter<T> { + + @Nullable + T apply(int index, boolean isFluid, boolean isOutput, boolean isSpecial); + } +} diff --git a/src/main/java/gregtech/api/recipe/BasicUIPropertiesBuilder.java b/src/main/java/gregtech/api/recipe/BasicUIPropertiesBuilder.java new file mode 100644 index 0000000000..7be2c94b23 --- /dev/null +++ b/src/main/java/gregtech/api/recipe/BasicUIPropertiesBuilder.java @@ -0,0 +1,264 @@ +package gregtech.api.recipe; + +import java.awt.Rectangle; +import java.util.Collections; +import java.util.List; +import java.util.function.IntFunction; +import java.util.function.Supplier; + +import javax.annotation.Nullable; +import javax.annotation.ParametersAreNonnullByDefault; + +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.apache.commons.lang3.tuple.Pair; + +import com.google.common.collect.ImmutableList; +import com.gtnewhorizons.modularui.api.drawable.FallbackableUITexture; +import com.gtnewhorizons.modularui.api.drawable.IDrawable; +import com.gtnewhorizons.modularui.api.math.Pos2d; +import com.gtnewhorizons.modularui.api.math.Size; +import com.gtnewhorizons.modularui.common.widget.ProgressBar; + +import gregtech.api.gui.modularui.FallbackableSteamTexture; +import gregtech.api.gui.modularui.GT_UITextures; +import gregtech.api.gui.modularui.SteamTexture; +import gregtech.api.util.MethodsReturnNonnullByDefault; +import gregtech.common.gui.modularui.UIHelper; + +/** + * Builder class for {@link BasicUIProperties}. + */ +@SuppressWarnings("UnusedReturnValue") +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +public final class BasicUIPropertiesBuilder { + + private int maxItemInputs, maxItemOutputs, maxFluidInputs, maxFluidOutputs; + + private BasicUIProperties.SlotOverlayGetter<IDrawable> slotOverlays = (index, isFluid, isOutput, isSpecial) -> null; + private BasicUIProperties.SlotOverlayGetter<SteamTexture> slotOverlaysSteam = (index, isFluid, isOutput, + isSpecial) -> null; + + @Nullable + private FallbackableUITexture progressBarTexture; + @Nullable + private FallbackableSteamTexture progressBarTextureSteam; + private ProgressBar.Direction progressBarDirection = ProgressBar.Direction.RIGHT; + private Size progressBarSize = new Size(20, 18); + private Pos2d progressBarPos = new Pos2d(78, 24); + + private boolean useProgressBar = true; + + private boolean useSpecialSlot; + + private final ImmutableList.Builder<Rectangle> neiTransferRect = ImmutableList.builder(); + @Nullable + private String neiTransferRectId; + + private final ImmutableList.Builder<Pair<IDrawable, Pair<Size, Pos2d>>> specialTextures = ImmutableList.builder(); + private final ImmutableList.Builder<Pair<SteamTexture, Pair<Size, Pos2d>>> specialTexturesSteam = ImmutableList + .builder(); + + private IDrawable logo = GT_UITextures.PICTURE_GT_LOGO_17x17_TRANSPARENT; + private Size logoSize = new Size(17, 17); + private Pos2d logoPos = new Pos2d(152, 63); + + private IntFunction<List<Pos2d>> itemInputPositionsGetter = UIHelper::getItemInputPositions; + private IntFunction<List<Pos2d>> itemOutputPositionsGetter = UIHelper::getItemOutputPositions; + private Supplier<Pos2d> specialItemPositionGetter = UIHelper::getSpecialItemPosition; + private IntFunction<List<Pos2d>> fluidInputPositionsGetter = UIHelper::getFluidInputPositions; + private IntFunction<List<Pos2d>> fluidOutputPositionsGetter = UIHelper::getFluidOutputPositions; + + private int amperage = 1; + + BasicUIPropertiesBuilder() {} + + public BasicUIProperties build() { + if (maxItemInputs == 0 && maxItemOutputs == 0 && maxFluidInputs == 0 && maxFluidOutputs == 0) { + throw new IllegalArgumentException("Set either of max I/O count"); + } + List<Rectangle> builtNEITransferRect = neiTransferRect.build(); + if (builtNEITransferRect.isEmpty()) { + builtNEITransferRect = Collections.singletonList( + new Rectangle( + progressBarPos.x - (16 / 2), + progressBarPos.y, + progressBarSize.width + 16, + progressBarSize.height)); + } + return new BasicUIProperties( + maxItemInputs, + maxItemOutputs, + maxFluidInputs, + maxFluidOutputs, + slotOverlays, + slotOverlaysSteam, + progressBarTexture, + progressBarTextureSteam, + progressBarDirection, + progressBarSize, + progressBarPos, + useProgressBar, + useSpecialSlot, + builtNEITransferRect, + neiTransferRectId, + specialTextures.build(), + specialTexturesSteam.build(), + logo, + logoSize, + logoPos, + itemInputPositionsGetter, + itemOutputPositionsGetter, + specialItemPositionGetter, + fluidInputPositionsGetter, + fluidOutputPositionsGetter, + amperage); + } + + public BasicUIPropertiesBuilder maxItemInputs(int maxItemInputs) { + this.maxItemInputs = maxItemInputs; + return this; + } + + public BasicUIPropertiesBuilder maxItemOutputs(int maxItemOutputs) { + this.maxItemOutputs = maxItemOutputs; + return this; + } + + public BasicUIPropertiesBuilder maxFluidInputs(int maxFluidInputs) { + this.maxFluidInputs = maxFluidInputs; + return this; + } + + public BasicUIPropertiesBuilder maxFluidOutputs(int maxFluidOutputs) { + this.maxFluidOutputs = maxFluidOutputs; + return this; + } + + public BasicUIPropertiesBuilder slotOverlays(BasicUIProperties.SlotOverlayGetter<IDrawable> slotOverlays) { + this.slotOverlays = slotOverlays; + return this; + } + + public BasicUIPropertiesBuilder slotOverlaysSteam( + BasicUIProperties.SlotOverlayGetter<SteamTexture> slotOverlaysSteam) { + this.slotOverlaysSteam = slotOverlaysSteam; + return this; + } + + public BasicUIPropertiesBuilder progressBarTexture(@Nullable FallbackableUITexture progressBarTexture) { + this.progressBarTexture = progressBarTexture; + return this; + } + + public BasicUIPropertiesBuilder progressBarTextureSteam( + @Nullable FallbackableSteamTexture progressBarTextureSteam) { + this.progressBarTextureSteam = progressBarTextureSteam; + return this; + } + + public BasicUIPropertiesBuilder progressBarDirection(ProgressBar.Direction progressBarDirection) { + this.progressBarDirection = progressBarDirection; + return this; + } + + public BasicUIPropertiesBuilder progressBarSize(Size progressBarSize) { + this.progressBarSize = progressBarSize; + return this; + } + + public BasicUIPropertiesBuilder progressBarPos(Pos2d progressBarPos) { + this.progressBarPos = progressBarPos; + return this; + } + + public BasicUIPropertiesBuilder useProgressBar(boolean useProgressBar) { + this.useProgressBar = useProgressBar; + return this; + } + + public BasicUIPropertiesBuilder useSpecialSlot(boolean useSpecialSlot) { + this.useSpecialSlot = useSpecialSlot; + return this; + } + + public BasicUIPropertiesBuilder addNEITransferRect(Rectangle neiTransferRect) { + this.neiTransferRect.add(neiTransferRect); + return this; + } + + BasicUIPropertiesBuilder neiTransferRect(List<Rectangle> neiTransferRect) { + this.neiTransferRect.addAll(neiTransferRect); + return this; + } + + public BasicUIPropertiesBuilder neiTransferRectId(@Nullable String neiTransferRectId) { + this.neiTransferRectId = neiTransferRectId; + return this; + } + + public BasicUIPropertiesBuilder addSpecialTexture(Size size, Pos2d pos, IDrawable texture) { + this.specialTextures.add(new ImmutablePair<>(texture, new ImmutablePair<>(size, pos))); + return this; + } + + BasicUIPropertiesBuilder specialTextures(List<Pair<IDrawable, Pair<Size, Pos2d>>> specialTextures) { + this.specialTextures.addAll(specialTextures); + return this; + } + + public BasicUIPropertiesBuilder addSpecialTextureSteam(Size size, Pos2d pos, SteamTexture texture) { + this.specialTexturesSteam.add(new ImmutablePair<>(texture, new ImmutablePair<>(size, pos))); + return this; + } + + BasicUIPropertiesBuilder specialTexturesSteam(List<Pair<SteamTexture, Pair<Size, Pos2d>>> specialTextures) { + this.specialTexturesSteam.addAll(specialTextures); + return this; + } + + public BasicUIPropertiesBuilder logo(IDrawable logo) { + this.logo = logo; + return this; + } + + public BasicUIPropertiesBuilder logoSize(Size logoSize) { + this.logoSize = logoSize; + return this; + } + + public BasicUIPropertiesBuilder logoPos(Pos2d logoPos) { + this.logoPos = logoPos; + return this; + } + + public BasicUIPropertiesBuilder itemInputPositionsGetter(IntFunction<List<Pos2d>> itemInputPositionsGetter) { + this.itemInputPositionsGetter = itemInputPositionsGetter; + return this; + } + + public BasicUIPropertiesBuilder itemOutputPositionsGetter(IntFunction<List<Pos2d>> itemOutputPositionsGetter) { + this.itemOutputPositionsGetter = itemOutputPositionsGetter; + return this; + } + + public BasicUIPropertiesBuilder specialItemPositionGetter(Supplier<Pos2d> specialItemPositionGetter) { + this.specialItemPositionGetter = specialItemPositionGetter; + return this; + } + + public BasicUIPropertiesBuilder fluidInputPositionsGetter(IntFunction<List<Pos2d>> fluidInputPositionsGetter) { + this.fluidInputPositionsGetter = fluidInputPositionsGetter; + return this; + } + + public BasicUIPropertiesBuilder fluidOutputPositionsGetter(IntFunction<List<Pos2d>> fluidOutputPositionsGetter) { + this.fluidOutputPositionsGetter = fluidOutputPositionsGetter; + return this; + } + + public BasicUIPropertiesBuilder amperage(int amperage) { + this.amperage = amperage; + return this; + } +} diff --git a/src/main/java/gregtech/api/recipe/FindRecipeQuery.java b/src/main/java/gregtech/api/recipe/FindRecipeQuery.java new file mode 100644 index 0000000000..77c0648688 --- /dev/null +++ b/src/main/java/gregtech/api/recipe/FindRecipeQuery.java @@ -0,0 +1,178 @@ +package gregtech.api.recipe; + +import java.util.function.Predicate; +import java.util.stream.Stream; + +import javax.annotation.Nullable; +import javax.annotation.ParametersAreNonnullByDefault; + +import net.minecraft.item.ItemStack; +import net.minecraftforge.fluids.FluidStack; + +import gregtech.api.util.GT_Recipe; +import gregtech.api.util.MethodsReturnNonnullByDefault; + +// spotless:off spotless likes formatting @code to @code +/** + * Helper class for searching recipe. Retrieve instance with {@link RecipeMap#findRecipeQuery}. + * <p> + * It features fluent API, so you can find recipes like this: + * + * <pre> + * {@code + * GT_Recipe recipe = recipeMap.findRecipeQuery() + * .items(inputItems) + * .fluids(inputFluids) + * .find(); + * } + * </pre> + */ +// spotless:on +@SuppressWarnings("unused") +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +public final class FindRecipeQuery { + + private static final Predicate<GT_Recipe> ALWAYS = r -> true; + + private final RecipeMap<?> recipeMap; + + @Nullable + private ItemStack[] items; + @Nullable + private FluidStack[] fluids; + @Nullable + private ItemStack specialSlot; + private Predicate<GT_Recipe> filter = ALWAYS; + private long voltage = Integer.MAX_VALUE; + @Nullable + private GT_Recipe cachedRecipe; + private boolean notUnificated; + private boolean dontCheckStackSizes; + private boolean forCollisionCheck; + + FindRecipeQuery(RecipeMap<?> recipeMap) { + this.recipeMap = recipeMap; + } + + // region executors + + /** + * @return The first matched recipe, or null if not found. + */ + @Nullable + public GT_Recipe find() { + return findAll().findFirst() + .orElse(null); + } + + /** + * @return All the matched recipes in the form of Stream. + */ + public Stream<GT_Recipe> findAll() { + if (items == null) { + items = new ItemStack[0]; + } + if (fluids == null) { + fluids = new FluidStack[0]; + } + + return recipeMap.getBackend() + .matchRecipeStream( + items, + fluids, + specialSlot, + cachedRecipe, + notUnificated, + dontCheckStackSizes, + forCollisionCheck) + .filter(recipe -> voltage * recipeMap.getAmperage() >= recipe.mEUt && filter.test(recipe)); + } + + /** + * Checks if given inputs conflict with already registered recipes. + * + * @return True if collision is found. + */ + public boolean checkCollision() { + dontCheckStackSizes = true; + forCollisionCheck = true; + return findAll().findAny() + .isPresent(); + } + + // endregion + + // region setters + + /** + * @param items Item inputs. + */ + public FindRecipeQuery items(@Nullable ItemStack... items) { + this.items = items; + return this; + } + + /** + * @param fluids Fluid inputs. + */ + public FindRecipeQuery fluids(@Nullable FluidStack... fluids) { + this.fluids = fluids; + return this; + } + + /** + * @param specialSlot Content of the special slot. Normal recipemaps don't need this, but some do. + * Set {@link RecipeMapBuilder#specialSlotSensitive} to make it actually functional. + * Alternatively overriding {@link RecipeMapBackend#filterFindRecipe} will also work. + */ + public FindRecipeQuery specialSlot(@Nullable ItemStack specialSlot) { + this.specialSlot = specialSlot; + return this; + } + + /** + * @param filter Matched recipe will be tested by this function. If it returns false, the query will attempt to + * find next recipe. + */ + public FindRecipeQuery filter(Predicate<GT_Recipe> filter) { + this.filter = filter; + return this; + } + + /** + * @param voltage Recipes that exceed this voltage won't match. It will be automatically multiplied by amperage + * of the recipemap. + */ + public FindRecipeQuery voltage(long voltage) { + this.voltage = voltage; + return this; + } + + /** + * @param cachedRecipe If this is not null, the query tests it before all other recipes. + */ + public FindRecipeQuery cachedRecipe(@Nullable GT_Recipe cachedRecipe) { + this.cachedRecipe = cachedRecipe; + return this; + } + + /** + * @param notUnificated If this is set to true, item inputs will be unificated. + */ + public FindRecipeQuery notUnificated(boolean notUnificated) { + this.notUnificated = notUnificated; + return this; + } + + /** + * @param dontCheckStackSizes If this is set to true, the query won't check item count and fluid amount + * for the matched recipe. + */ + public FindRecipeQuery dontCheckStackSizes(boolean dontCheckStackSizes) { + this.dontCheckStackSizes = dontCheckStackSizes; + return this; + } + + // endregion +} diff --git a/src/main/java/gregtech/api/recipe/NEIRecipeProperties.java b/src/main/java/gregtech/api/recipe/NEIRecipeProperties.java new file mode 100644 index 0000000000..2ba49f5da1 --- /dev/null +++ b/src/main/java/gregtech/api/recipe/NEIRecipeProperties.java @@ -0,0 +1,89 @@ +package gregtech.api.recipe; + +import java.util.Comparator; +import java.util.function.UnaryOperator; + +import javax.annotation.Nullable; +import javax.annotation.ParametersAreNonnullByDefault; + +import com.gtnewhorizons.modularui.api.math.Pos2d; +import com.gtnewhorizons.modularui.api.math.Size; + +import codechicken.nei.recipe.HandlerInfo; +import gregtech.api.objects.overclockdescriber.OverclockDescriber; +import gregtech.api.util.FieldsAreNonnullByDefault; +import gregtech.api.util.GT_Recipe; +import gregtech.api.util.MethodsReturnNonnullByDefault; +import gregtech.nei.formatter.INEISpecialInfoFormatter; + +/** + * Data object storing info exclusively used to draw NEI recipe GUI. Not all the properties used to draw NEI + * are present here. See {@link BasicUIProperties} for the rest. + * <p> + * Use {@link #builder} for creation. + */ +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +@FieldsAreNonnullByDefault +public final class NEIRecipeProperties { + + static NEIRecipePropertiesBuilder builder() { + return new NEIRecipePropertiesBuilder(); + } + + /** + * Whether to register dedicated NEI recipe page for the recipemap. + */ + public final boolean registerNEI; + @Nullable + public final UnaryOperator<HandlerInfo.Builder> handlerInfoCreator; + + /** + * Size of background shown. + */ + // todo make it final + public Size recipeBackgroundSize; + /** + * Offset of background shown. + */ + public final Pos2d recipeBackgroundOffset; + + /** + * Formats special description for the recipe, mainly {@link gregtech.api.util.GT_Recipe#mSpecialValue}. + */ + public final INEISpecialInfoFormatter neiSpecialInfoFormatter; + + /** + * Whether to show oredict equivalent item outputs. + */ + public final boolean unificateOutput; + /** + * If a custom filter method {@link OverclockDescriber#canHandle} should be used to limit the shown recipes when + * searching recipes with recipe catalyst. Else, the voltage of the recipe is the only factor to filter recipes. + */ + public final boolean useCustomFilter; + /** + * Whether to render the actual stack size of items or not. + */ + public final boolean renderRealStackSizes; + + /** + * Comparator for NEI recipe sort. {@link GT_Recipe#compareTo(GT_Recipe)} by default. + */ + public final Comparator<GT_Recipe> comparator; + + NEIRecipeProperties(boolean registerNEI, @Nullable UnaryOperator<HandlerInfo.Builder> handlerInfoCreator, + Size recipeBackgroundSize, Pos2d recipeBackgroundOffset, INEISpecialInfoFormatter neiSpecialInfoFormatter, + boolean unificateOutput, boolean useCustomFilter, boolean renderRealStackSizes, + Comparator<GT_Recipe> comparator) { + this.registerNEI = registerNEI; + this.handlerInfoCreator = handlerInfoCreator; + this.recipeBackgroundOffset = recipeBackgroundOffset; + this.recipeBackgroundSize = recipeBackgroundSize; + this.neiSpecialInfoFormatter = neiSpecialInfoFormatter; + this.unificateOutput = unificateOutput; + this.useCustomFilter = useCustomFilter; + this.renderRealStackSizes = renderRealStackSizes; + this.comparator = comparator; + } +} diff --git a/src/main/java/gregtech/api/recipe/NEIRecipePropertiesBuilder.java b/src/main/java/gregtech/api/recipe/NEIRecipePropertiesBuilder.java new file mode 100644 index 0000000000..050a1c4920 --- /dev/null +++ b/src/main/java/gregtech/api/recipe/NEIRecipePropertiesBuilder.java @@ -0,0 +1,100 @@ +package gregtech.api.recipe; + +import java.util.Comparator; +import java.util.function.UnaryOperator; + +import javax.annotation.Nullable; +import javax.annotation.ParametersAreNonnullByDefault; + +import com.gtnewhorizons.modularui.api.math.Pos2d; +import com.gtnewhorizons.modularui.api.math.Size; + +import codechicken.nei.recipe.HandlerInfo; +import gregtech.api.util.GT_Recipe; +import gregtech.api.util.MethodsReturnNonnullByDefault; +import gregtech.nei.formatter.DefaultSpecialValueFormatter; +import gregtech.nei.formatter.INEISpecialInfoFormatter; + +/** + * Builder class for {@link NEIRecipeProperties}. + */ +@SuppressWarnings("UnusedReturnValue") +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +public final class NEIRecipePropertiesBuilder { + + private boolean registerNEI = true; + @Nullable + private UnaryOperator<HandlerInfo.Builder> handlerInfoCreator; + + private Size recipeBackgroundSize = new Size(170, 82); + private Pos2d recipeBackgroundOffset = new Pos2d(3, 3); + + private INEISpecialInfoFormatter neiSpecialInfoFormatter = DefaultSpecialValueFormatter.INSTANCE; + + private boolean unificateOutput = true; + private boolean useCustomFilter; + private boolean renderRealStackSizes = true; + + private Comparator<GT_Recipe> comparator = GT_Recipe::compareTo; + + NEIRecipePropertiesBuilder() {} + + public NEIRecipeProperties build() { + return new NEIRecipeProperties( + registerNEI, + handlerInfoCreator, + recipeBackgroundSize, + recipeBackgroundOffset, + neiSpecialInfoFormatter, + unificateOutput, + useCustomFilter, + renderRealStackSizes, + comparator); + } + + public NEIRecipePropertiesBuilder disableRegisterNEI() { + this.registerNEI = false; + return this; + } + + public NEIRecipePropertiesBuilder handlerInfoCreator(UnaryOperator<HandlerInfo.Builder> builderCreator) { + this.handlerInfoCreator = builderCreator; + return this; + } + + public NEIRecipePropertiesBuilder recipeBackgroundSize(Size recipeBackgroundSize) { + this.recipeBackgroundSize = recipeBackgroundSize; + return this; + } + + public NEIRecipePropertiesBuilder recipeBackgroundOffset(Pos2d recipeBackgroundOffset) { + this.recipeBackgroundOffset = recipeBackgroundOffset; + return this; + } + + public NEIRecipePropertiesBuilder neiSpecialInfoFormatter(INEISpecialInfoFormatter neiSpecialInfoFormatter) { + this.neiSpecialInfoFormatter = neiSpecialInfoFormatter; + return this; + } + + public NEIRecipePropertiesBuilder unificateOutput(boolean unificateOutput) { + this.unificateOutput = unificateOutput; + return this; + } + + public NEIRecipePropertiesBuilder useCustomFilter() { + this.useCustomFilter = true; + return this; + } + + public NEIRecipePropertiesBuilder disableRenderRealStackSizes() { + this.renderRealStackSizes = false; + return this; + } + + public NEIRecipePropertiesBuilder recipeComparator(Comparator<GT_Recipe> comparator) { + this.comparator = comparator; + return this; + } +} diff --git a/src/main/java/gregtech/api/recipe/RecipeCategories.java b/src/main/java/gregtech/api/recipe/RecipeCategories.java new file mode 100644 index 0000000000..61eed8a8d7 --- /dev/null +++ b/src/main/java/gregtech/api/recipe/RecipeCategories.java @@ -0,0 +1,71 @@ +package gregtech.api.recipe; + +import static gregtech.api.enums.Mods.GregTech; +import static gregtech.api.recipe.RecipeCategory.createIcon; + +@SuppressWarnings("unused") +public final class RecipeCategories { + + @RecipeCategoryHolder + public static final RecipeCategory arcFurnaceRecycling = new RecipeCategory( + "gt.recipe.category.arc_furnace_recycling", + RecipeMaps.arcFurnaceRecipes, + builder -> builder.setDisplayImage( + createIcon(GregTech.getResourcePath("textures", "gui", "picture", "arc_furnace_recycling.png")))); + + @RecipeCategoryHolder + public static final RecipeCategory plasmaArcFurnaceRecycling = new RecipeCategory( + "gt.recipe.category.plasma_arc_furnace_recycling", + RecipeMaps.plasmaArcFurnaceRecipes, + builder -> builder.setDisplayImage( + createIcon(GregTech.getResourcePath("textures", "gui", "picture", "plasma_arc_furnace_recycling.png")))); + + @RecipeCategoryHolder + public static final RecipeCategory maceratorRecycling = new RecipeCategory( + "gt.recipe.category.macerator_recycling", + RecipeMaps.maceratorRecipes, + builder -> builder.setDisplayImage( + createIcon(GregTech.getResourcePath("textures", "gui", "picture", "macerator_recycling.png")))); + + @RecipeCategoryHolder + public static final RecipeCategory fluidExtractorRecycling = new RecipeCategory( + "gt.recipe.category.fluid_extractor_recycling", + RecipeMaps.fluidExtractionRecipes, + builder -> builder.setDisplayImage( + createIcon(GregTech.getResourcePath("textures", "gui", "picture", "fluid_extractor_recycling.png")))); + + @RecipeCategoryHolder + public static final RecipeCategory alloySmelterRecycling = new RecipeCategory( + "gt.recipe.category.alloy_smelter_recycling", + RecipeMaps.alloySmelterRecipes, + builder -> builder.setDisplayImage( + createIcon(GregTech.getResourcePath("textures", "gui", "picture", "alloy_smelter_recycling.png")))); + + @RecipeCategoryHolder + public static final RecipeCategory alloySmelterMolding = new RecipeCategory( + "gt.recipe.category.alloy_smelter_molding", + RecipeMaps.alloySmelterRecipes, + builder -> builder.setDisplayImage( + createIcon(GregTech.getResourcePath("textures", "gui", "picture", "alloy_smelter_molding.png")))); + + @RecipeCategoryHolder + public static final RecipeCategory forgeHammerRecycling = new RecipeCategory( + "gt.recipe.category.forge_hammer_recycling", + RecipeMaps.hammerRecipes, + builder -> builder.setDisplayImage( + createIcon(GregTech.getResourcePath("textures", "gui", "picture", "forge_hammer_recycling.png")))); + + @RecipeCategoryHolder + public static final RecipeCategory ticPartExtruding = new RecipeCategory( + "gt.recipe.category.tic_part_extruding", + RecipeMaps.extruderRecipes, + builder -> builder.setDisplayImage( + createIcon(GregTech.getResourcePath("textures", "gui", "picture", "tic_part_extruding.png")))); + + @RecipeCategoryHolder + public static final RecipeCategory ticBoltMolding = new RecipeCategory( + "gt.recipe.category.tic_bolt_molding", + RecipeMaps.fluidSolidifierRecipes, + builder -> builder.setDisplayImage( + createIcon(GregTech.getResourcePath("textures", "gui", "picture", "tic_bolt_molding.png")))); +} diff --git a/src/main/java/gregtech/api/recipe/RecipeCategory.java b/src/main/java/gregtech/api/recipe/RecipeCategory.java new file mode 100644 index 0000000000..9f8674e939 --- /dev/null +++ b/src/main/java/gregtech/api/recipe/RecipeCategory.java @@ -0,0 +1,81 @@ +package gregtech.api.recipe; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.UnaryOperator; + +import javax.annotation.Nullable; +import javax.annotation.ParametersAreNonnullByDefault; + +import codechicken.nei.drawable.DrawableBuilder; +import codechicken.nei.drawable.DrawableResource; +import codechicken.nei.recipe.HandlerInfo; +import cpw.mods.fml.common.Loader; +import cpw.mods.fml.common.ModContainer; +import gregtech.api.util.FieldsAreNonnullByDefault; +import gregtech.api.util.MethodsReturnNonnullByDefault; + +/** + * Allows certain recipes to be displayed on different tabs on NEI. + * <p> + * Also apply {@link RecipeCategoryHolder} annotation to each instance to be picked up by GT config. + */ +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +@FieldsAreNonnullByDefault +public final class RecipeCategory { + + public static final Map<String, RecipeCategory> ALL_RECIPE_CATEGORIES = new HashMap<>(); + + public final String unlocalizedName; + public final RecipeMap<?> recipeMap; + public final ModContainer ownerMod; + @Nullable + public final UnaryOperator<HandlerInfo.Builder> handlerInfoCreator; + + /** + * @param unlocalizedName Unlocalized name of this category. Must be unique. + * @param recipeMap RecipeMap this category belongs to. + * @param handlerInfoCreator Supplier of handler info for the NEI handler this category belongs to. + */ + public RecipeCategory(String unlocalizedName, RecipeMap<?> recipeMap, + @Nullable UnaryOperator<HandlerInfo.Builder> handlerInfoCreator) { + this.unlocalizedName = unlocalizedName; + this.recipeMap = recipeMap; + this.ownerMod = Loader.instance() + .activeModContainer(); + this.handlerInfoCreator = handlerInfoCreator; + if (ALL_RECIPE_CATEGORIES.containsKey(unlocalizedName)) { + throw new IllegalArgumentException( + "Cannot register recipe category with duplicated unlocalized name: " + unlocalizedName); + } + ALL_RECIPE_CATEGORIES.put(unlocalizedName, this); + } + + RecipeCategory(RecipeMap<?> recipeMap) { + this(recipeMap.unlocalizedName, recipeMap, recipeMap.getFrontend().neiProperties.handlerInfoCreator); + } + + @Override + public String toString() { + return "RecipeCategory{" + "unlocalizedName='" + + unlocalizedName + + '\'' + + ", recipeMap=" + + recipeMap.unlocalizedName + + ", ownerMod=" + + ownerMod.getModId() + + '}'; + } + + /** + * Util method for creating icon for recipe category. Size is 16px. + */ + public static DrawableResource createIcon(String resourceLocation) { + return new DrawableBuilder(resourceLocation, 0, 0, 16, 16) + // GuiRecipeTab#drawForeground draws icon with 1px offset to make fuel icon (14px) prettier + .addPadding(-1, 0, -1, 0) + .setTextureSize(16, 16) + .build(); + } +} diff --git a/src/main/java/gregtech/api/recipe/RecipeCategoryHolder.java b/src/main/java/gregtech/api/recipe/RecipeCategoryHolder.java new file mode 100644 index 0000000000..0ad87e3f6f --- /dev/null +++ b/src/main/java/gregtech/api/recipe/RecipeCategoryHolder.java @@ -0,0 +1,13 @@ +package gregtech.api.recipe; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Apply this annotation to each recipe category so that GT config will pick it up. + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface RecipeCategoryHolder {} diff --git a/src/main/java/gregtech/api/recipe/RecipeCategorySetting.java b/src/main/java/gregtech/api/recipe/RecipeCategorySetting.java new file mode 100644 index 0000000000..4920d64212 --- /dev/null +++ b/src/main/java/gregtech/api/recipe/RecipeCategorySetting.java @@ -0,0 +1,52 @@ +package gregtech.api.recipe; + +import java.util.Locale; +import java.util.stream.Stream; + +import javax.annotation.ParametersAreNonnullByDefault; + +import gregtech.api.util.MethodsReturnNonnullByDefault; + +/** + * Specifies behaviors for {@link RecipeCategory}. + */ +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +public enum RecipeCategorySetting { + + /** + * Show the category in separated NEI tab. + */ + ENABLE, + /** + * The category is merged to default one for the recipemap. + */ + MERGE, + /** + * Recipes belonging to the category are hidden from NEI. + */ + HIDE; + + public static final RecipeCategorySetting[] VALUES = values(); + public static final String[] NAMES = Stream.of(VALUES) + .map(RecipeCategorySetting::toName) + .toArray(String[]::new); + + public static RecipeCategorySetting getDefault() { + return ENABLE; + } + + public String toName() { + return toString().toLowerCase(Locale.ENGLISH); + } + + public static RecipeCategorySetting find(String name) { + for (RecipeCategorySetting setting : VALUES) { + if (setting.toName() + .equals(name)) { + return setting; + } + } + return getDefault(); + } +} diff --git a/src/main/java/gregtech/api/recipe/RecipeMap.java b/src/main/java/gregtech/api/recipe/RecipeMap.java new file mode 100644 index 0000000000..2ee2d3cb94 --- /dev/null +++ b/src/main/java/gregtech/api/recipe/RecipeMap.java @@ -0,0 +1,395 @@ +package gregtech.api.recipe; + +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.annotation.ParametersAreNonnullByDefault; + +import net.minecraft.item.ItemStack; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidStack; + +import org.jetbrains.annotations.Unmodifiable; + +import gregtech.api.interfaces.IRecipeMap; +import gregtech.api.interfaces.tileentity.IHasWorldObjectAndCoords; +import gregtech.api.util.FieldsAreNonnullByDefault; +import gregtech.api.util.GT_Recipe; +import gregtech.api.util.GT_RecipeBuilder; +import gregtech.api.util.MethodsReturnNonnullByDefault; + +/** + * Manages list of recipes. Its functionalities are split + * between {@link RecipeMapBackend} and {@link RecipeMapFrontend}. + * + * @param <B> Type of {@link RecipeMapBackend} + */ +@SuppressWarnings({ "unused", "UnusedReturnValue" }) +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +@FieldsAreNonnullByDefault +public final class RecipeMap<B extends RecipeMapBackend> implements IRecipeMap { + + /** + * All the recipemap instances. key=unlocalized name, value=instance. + */ + public static final Map<String, RecipeMap<?>> ALL_RECIPE_MAPS = new HashMap<>(); + + private final B backend; + private final RecipeMapFrontend frontend; + + /** + * Unique unlocalized name of this recipemap. Used for identifier, localization key for NEI tab name, etc. + */ + public final String unlocalizedName; + + private final RecipeCategory defaultRecipeCategory; + + /** + * Use {@link RecipeMapBuilder} to instantiate. + */ + RecipeMap(String unlocalizedName, B backend, RecipeMapFrontend frontend) { + this.unlocalizedName = unlocalizedName; + this.backend = backend; + this.frontend = frontend; + this.defaultRecipeCategory = new RecipeCategory(this); + backend.setRecipeMap(this); + if (ALL_RECIPE_MAPS.containsKey(unlocalizedName)) { + throw new IllegalArgumentException( + "Cannot register recipemap with duplicated unlocalized name: " + unlocalizedName); + } + ALL_RECIPE_MAPS.put(unlocalizedName, this); + } + + public B getBackend() { + return backend; + } + + public RecipeMapFrontend getFrontend() { + return frontend; + } + + /** + * @return All the recipes belonging to this recipemap. + */ + @Unmodifiable + public Collection<GT_Recipe> getAllRecipes() { + return backend.getAllRecipes(); + } + + /** + * @return List of registered recipe categories associated with this recipemap. + */ + public List<RecipeCategory> getAssociatedCategories() { + return RecipeCategory.ALL_RECIPE_CATEGORIES.values() + .stream() + .filter(category -> category.recipeMap == this) + .collect(Collectors.toList()); + } + + public RecipeCategory getDefaultRecipeCategory() { + return defaultRecipeCategory; + } + + /** + * @return Amperage of this recipemap. Note that recipes store EU/t with amperage included, + * e.g. Arc Furnace recipe with 90 EU/t means 30 EU/t (LV) with 3 amperage. + */ + public int getAmperage() { + return frontend.getUIProperties().amperage; + } + + @Override + public void addDownstream(IRecipeMap downstream) { + backend.addDownstream(downstream); + } + + // region add recipe + + @Nullable + public GT_Recipe addRecipe(boolean aOptimize, @Nullable ItemStack[] aInputs, @Nullable ItemStack[] aOutputs, + @Nullable Object aSpecial, @Nullable int[] aOutputChances, @Nullable FluidStack[] aFluidInputs, + @Nullable FluidStack[] aFluidOutputs, int aDuration, int aEUt, int aSpecialValue) { + return addRecipe( + new GT_Recipe( + aOptimize, + aInputs, + aOutputs, + aSpecial, + aOutputChances, + aFluidInputs, + aFluidOutputs, + aDuration, + aEUt, + aSpecialValue)); + } + + @Nullable + public GT_Recipe addRecipe(@Nullable int[] aOutputChances, @Nullable FluidStack[] aFluidInputs, + @Nullable FluidStack[] aFluidOutputs, int aDuration, int aEUt, int aSpecialValue) { + return addRecipe( + new GT_Recipe( + false, + null, + null, + null, + aOutputChances, + aFluidInputs, + aFluidOutputs, + aDuration, + aEUt, + aSpecialValue), + false, + false, + false); + } + + @Nullable + public GT_Recipe addRecipe(boolean aOptimize, @Nullable ItemStack[] aInputs, @Nullable ItemStack[] aOutputs, + @Nullable Object aSpecial, @Nullable FluidStack[] aFluidInputs, @Nullable FluidStack[] aFluidOutputs, + int aDuration, int aEUt, int aSpecialValue) { + return addRecipe( + new GT_Recipe( + aOptimize, + aInputs, + aOutputs, + aSpecial, + null, + aFluidInputs, + aFluidOutputs, + aDuration, + aEUt, + aSpecialValue)); + } + + @Nullable + public GT_Recipe addRecipe(GT_Recipe aRecipe) { + return addRecipe(aRecipe, true, false, false); + } + + @Nullable + public GT_Recipe addRecipe(GT_Recipe aRecipe, boolean aCheckForCollisions, boolean aFakeRecipe, boolean aHidden) { + aRecipe.mHidden = aHidden; + aRecipe.mFakeRecipe = aFakeRecipe; + if (aRecipe.mFluidInputs.length < backend.properties.minFluidInputs + && aRecipe.mInputs.length < backend.properties.minItemInputs) return null; + if (aCheckForCollisions && backend.checkCollision(aRecipe)) return null; + return backend.compileRecipe(aRecipe); + } + + /** + * Only used for fake Recipe Handlers to show something in NEI, do not use this for adding actual Recipes! + * findRecipe won't find fake Recipes, containsInput WILL find fake Recipes + */ + @Nullable + public GT_Recipe addFakeRecipe(boolean aCheckForCollisions, @Nullable ItemStack[] aInputs, + @Nullable ItemStack[] aOutputs, @Nullable Object aSpecial, @Nullable int[] aOutputChances, + @Nullable FluidStack[] aFluidInputs, @Nullable FluidStack[] aFluidOutputs, int aDuration, int aEUt, + int aSpecialValue) { + return addFakeRecipe( + aCheckForCollisions, + new GT_Recipe( + false, + aInputs, + aOutputs, + aSpecial, + aOutputChances, + aFluidInputs, + aFluidOutputs, + aDuration, + aEUt, + aSpecialValue)); + } + + /** + * Only used for fake Recipe Handlers to show something in NEI, do not use this for adding actual Recipes! + * findRecipe won't find fake Recipes, containsInput WILL find fake Recipes + */ + @Nullable + public GT_Recipe addFakeRecipe(boolean aCheckForCollisions, @Nullable ItemStack[] aInputs, + @Nullable ItemStack[] aOutputs, @Nullable Object aSpecial, @Nullable FluidStack[] aFluidInputs, + @Nullable FluidStack[] aFluidOutputs, int aDuration, int aEUt, int aSpecialValue) { + return addFakeRecipe( + aCheckForCollisions, + new GT_Recipe( + false, + aInputs, + aOutputs, + aSpecial, + null, + aFluidInputs, + aFluidOutputs, + aDuration, + aEUt, + aSpecialValue)); + } + + @Nullable + public GT_Recipe addFakeRecipe(boolean aCheckForCollisions, @Nullable ItemStack[] aInputs, + @Nullable ItemStack[] aOutputs, @Nullable Object aSpecial, @Nullable FluidStack[] aFluidInputs, + @Nullable FluidStack[] aFluidOutputs, int aDuration, int aEUt, int aSpecialValue, boolean hidden) { + return addFakeRecipe( + aCheckForCollisions, + new GT_Recipe( + false, + aInputs, + aOutputs, + aSpecial, + null, + aFluidInputs, + aFluidOutputs, + aDuration, + aEUt, + aSpecialValue), + hidden); + } + + @Nullable + public GT_Recipe addFakeRecipe(boolean aCheckForCollisions, @Nullable ItemStack[] aInputs, + @Nullable ItemStack[] aOutputs, @Nullable Object aSpecial, @Nullable FluidStack[] aFluidInputs, + @Nullable FluidStack[] aFluidOutputs, int aDuration, int aEUt, int aSpecialValue, ItemStack[][] aAlt, + boolean hidden) { + return addFakeRecipe( + aCheckForCollisions, + new GT_Recipe.GT_Recipe_WithAlt( + false, + aInputs, + aOutputs, + aSpecial, + null, + aFluidInputs, + aFluidOutputs, + aDuration, + aEUt, + aSpecialValue, + aAlt), + hidden); + } + + /** + * Only used for fake Recipe Handlers to show something in NEI, do not use this for adding actual Recipes! + * findRecipe won't find fake Recipes, containsInput WILL find fake Recipes + */ + @Nullable + public GT_Recipe addFakeRecipe(boolean aCheckForCollisions, GT_Recipe aRecipe) { + return addRecipe(aRecipe, aCheckForCollisions, true, false); + } + + @Nullable + public GT_Recipe addFakeRecipe(boolean aCheckForCollisions, GT_Recipe aRecipe, boolean hidden) { + return addRecipe(aRecipe, aCheckForCollisions, true, hidden); + } + + @Nonnull + @Override + public Collection<GT_Recipe> doAdd(GT_RecipeBuilder builder) { + return backend.doAdd(builder); + } + + public GT_Recipe add(GT_Recipe aRecipe) { + return backend.compileRecipe(aRecipe); + } + + // endregion + + /** + * @return if this Item is a valid Input for any for the Recipes + */ + public boolean containsInput(@Nullable ItemStack aStack) { + return aStack != null && backend.containsInput(aStack); + } + + /** + * @return if this Fluid is a valid Input for any for the Recipes + */ + public boolean containsInput(@Nullable FluidStack aFluid) { + return aFluid != null && containsInput(aFluid.getFluid()); + } + + /** + * @return if this Fluid is a valid Input for any for the Recipes + */ + public boolean containsInput(@Nullable Fluid aFluid) { + return aFluid != null && backend.containsInput(aFluid); + } + + // region find recipe + + /** + * @return Entrypoint for fluent API for finding recipe. + */ + public FindRecipeQuery findRecipeQuery() { + return new FindRecipeQuery(this); + } + + @Nullable + public GT_Recipe findRecipe(@Nullable IHasWorldObjectAndCoords aTileEntity, boolean aNotUnificated, long aVoltage, + @Nullable FluidStack[] aFluids, @Nullable ItemStack... aInputs) { + return findRecipe(aTileEntity, null, aNotUnificated, aVoltage, aFluids, null, aInputs); + } + + @Nullable + public GT_Recipe findRecipe(@Nullable IHasWorldObjectAndCoords aTileEntity, boolean aNotUnificated, + boolean aDontCheckStackSizes, long aVoltage, @Nullable FluidStack[] aFluids, @Nullable ItemStack... aInputs) { + return findRecipe(aTileEntity, null, aNotUnificated, aDontCheckStackSizes, aVoltage, aFluids, null, aInputs); + } + + @Nullable + public GT_Recipe findRecipe(@Nullable IHasWorldObjectAndCoords aTileEntity, @Nullable GT_Recipe aRecipe, + boolean aNotUnificated, long aVoltage, @Nullable FluidStack[] aFluids, @Nullable ItemStack aSpecialSlot, + @Nullable ItemStack... aInputs) { + return findRecipe(aTileEntity, aRecipe, aNotUnificated, false, aVoltage, aFluids, aSpecialSlot, aInputs); + } + + @Nullable + public GT_Recipe findRecipe(@Nullable IHasWorldObjectAndCoords aTileEntity, @Nullable GT_Recipe aRecipe, + boolean aNotUnificated, boolean aDontCheckStackSizes, long aVoltage, @Nullable FluidStack[] aFluids, + @Nullable ItemStack aSpecialSlot, @Nullable ItemStack... aInputs) { + return findRecipeQuery().items(aInputs != null ? aInputs : new ItemStack[0]) + .fluids(aFluids != null ? aFluids : new FluidStack[0]) + .specialSlot(aSpecialSlot) + .voltage(aVoltage) + .cachedRecipe(aRecipe) + .notUnificated(aNotUnificated) + .dontCheckStackSizes(aDontCheckStackSizes) + .find(); + } + + // endregion + + @Override + public String toString() { + return "RecipeMap{" + "unlocalizedName='" + + unlocalizedName + + '\'' + + ", ownerMod=" + + defaultRecipeCategory.ownerMod.getModId() + + '}'; + } + + private static final Pattern LEGACY_IDENTIFIER_PATTERN = Pattern.compile("(.+)_[0-9]+_[0-9]+_[0-9]+_[0-9]+_[0-9]+"); + + /** + * Gets recipemap instance from old mUniqueIdentifier format. This is only for backward compat, where tiles + * saved recipemap with mUniqueIdentifier. + * + * @param legacyIdentifier mUniqueIdentifier, in %s_%d_%d_%d_%d_%d format + * @return Found recipemap, can be null + */ + @Nullable + public static RecipeMap<?> getFromOldIdentifier(String legacyIdentifier) { + Matcher matcher = LEGACY_IDENTIFIER_PATTERN.matcher(legacyIdentifier); + if (!matcher.find()) { + // It can be new format + return ALL_RECIPE_MAPS.get(legacyIdentifier); + } + return ALL_RECIPE_MAPS.get(matcher.group(1)); + } +} diff --git a/src/main/java/gregtech/api/recipe/RecipeMapBackend.java b/src/main/java/gregtech/api/recipe/RecipeMapBackend.java new file mode 100644 index 0000000000..a539067e93 --- /dev/null +++ b/src/main/java/gregtech/api/recipe/RecipeMapBackend.java @@ -0,0 +1,469 @@ +package gregtech.api.recipe; + +import static gregtech.api.util.GT_RecipeBuilder.handleInvalidRecipe; +import static gregtech.api.util.GT_RecipeBuilder.handleRecipeCollision; +import static gregtech.api.util.GT_Utility.areStacksEqualOrNull; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import javax.annotation.Nullable; +import javax.annotation.ParametersAreNonnullByDefault; + +import net.minecraft.item.ItemStack; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidStack; + +import org.jetbrains.annotations.Unmodifiable; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.SetMultimap; + +import gregtech.api.GregTech_API; +import gregtech.api.interfaces.IRecipeMap; +import gregtech.api.objects.GT_ItemStack; +import gregtech.api.util.GT_OreDictUnificator; +import gregtech.api.util.GT_Recipe; +import gregtech.api.util.GT_RecipeBuilder; +import gregtech.api.util.GT_StreamUtil; +import gregtech.api.util.MethodsReturnNonnullByDefault; + +/** + * Responsible for recipe addition / search for recipemap. + * <p> + * In order to bind custom backend to recipemap, use {@link RecipeMapBuilder#of(String, BackendCreator)}. + */ +@SuppressWarnings("unused") +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +public class RecipeMapBackend { + + private RecipeMap<?> recipeMap; + + /** + * Recipe index based on items. + */ + private final SetMultimap<GT_ItemStack, GT_Recipe> itemIndex = HashMultimap.create(); + /** + * Recipe index based on fluids. + */ + private final SetMultimap<String, GT_Recipe> fluidIndex = HashMultimap.create(); + + /** + * All the recipes belonging to this backend, indexed by recipe category. + */ + private final Map<RecipeCategory, Collection<GT_Recipe>> recipesByCategory = new HashMap<>(); + + /** + * List of recipemaps that also receive recipe addition from this backend. + */ + private final List<IRecipeMap> downstreams = new ArrayList<>(0); + + /** + * All the properties specific to this backend. + */ + protected final RecipeMapBackendProperties properties; + + public RecipeMapBackend(RecipeMapBackendPropertiesBuilder propertiesBuilder) { + this.properties = propertiesBuilder.build(); + GregTech_API.itemStackMultiMaps.add(itemIndex); + } + + void setRecipeMap(RecipeMap<?> recipeMap) { + this.recipeMap = recipeMap; + } + + /** + * @return Properties specific to this backend. + */ + public RecipeMapBackendProperties getProperties() { + return properties; + } + + /** + * @return All the recipes belonging to this backend. Returned collection is immutable, + * use {@link #compileRecipe} to add / {@link #removeRecipes} to remove. + */ + @Unmodifiable + public Collection<GT_Recipe> getAllRecipes() { + return Collections.unmodifiableCollection(allRecipes()); + } + + /** + * @return Raw recipe list + */ + private Collection<GT_Recipe> allRecipes() { + return recipesByCategory.values() + .stream() + .flatMap(Collection::stream) + .collect(Collectors.toCollection(ArrayList::new)); + } + + /** + * @return All the recipes belonging to this backend, indexed by recipe category. + */ + @Unmodifiable + public Collection<GT_Recipe> getRecipesByCategory(RecipeCategory recipeCategory) { + return Collections + .unmodifiableCollection(recipesByCategory.getOrDefault(recipeCategory, Collections.emptyList())); + } + + @Unmodifiable + public Map<RecipeCategory, Collection<GT_Recipe>> getRecipeCategoryMap() { + return Collections.unmodifiableMap(recipesByCategory); + } + + // region add recipe + + /** + * Adds the supplied recipe to the recipe list and index, without any check. + * + * @return Supplied recipe. + */ + public GT_Recipe compileRecipe(GT_Recipe recipe) { + if (recipe.getRecipeCategory() == null) { + recipe.setRecipeCategory(recipeMap.getDefaultRecipeCategory()); + } + recipesByCategory.computeIfAbsent(recipe.getRecipeCategory(), v -> new ArrayList<>()) + .add(recipe); + for (FluidStack fluid : recipe.mFluidInputs) { + if (fluid == null) continue; + fluidIndex.put( + fluid.getFluid() + .getName(), + recipe); + } + return addToItemMap(recipe); + } + + /** + * Adds the supplied recipe to the item cache. + */ + protected GT_Recipe addToItemMap(GT_Recipe recipe) { + for (ItemStack item : recipe.mInputs) { + if (item == null) continue; + itemIndex.put(new GT_ItemStack(item), recipe); + } + if (recipe instanceof GT_Recipe.GT_Recipe_WithAlt recipeWithAlt) { + for (ItemStack[] itemStacks : recipeWithAlt.mOreDictAlt) { + if (itemStacks == null) continue; + for (ItemStack item : itemStacks) { + if (item == null) continue; + itemIndex.put(new GT_ItemStack(item), recipe); + } + } + } + return recipe; + } + + /** + * Builds recipe from supplied recipe builder and adds it. + */ + protected Collection<GT_Recipe> doAdd(GT_RecipeBuilder builder) { + Iterable<? extends GT_Recipe> recipes = properties.recipeEmitter.apply(builder); + Collection<GT_Recipe> ret = new ArrayList<>(); + for (GT_Recipe recipe : recipes) { + if (properties.recipeConfigCategory != null) { + assert properties.recipeConfigKeyConvertor != null; + String configKey = properties.recipeConfigKeyConvertor.apply(recipe); + if (configKey != null && recipe.mDuration <= 0) { + continue; + } + } + if (recipe.mFluidInputs.length < properties.minFluidInputs + || recipe.mInputs.length < properties.minItemInputs) { + return Collections.emptyList(); + } + if (properties.recipeTransformer != null) { + recipe = properties.recipeTransformer.apply(recipe); + } + if (recipe == null) continue; + if (builder.isCheckForCollision() && checkCollision(recipe)) { + handleCollision(recipe); + continue; + } + if (recipe.getRecipeCategory() != null && recipe.getRecipeCategory().recipeMap != this.recipeMap) { + handleInvalidRecipe(); + continue; + } + ret.add(compileRecipe(recipe)); + } + if (!ret.isEmpty()) { + builder.clearInvalid(); + for (IRecipeMap downstream : downstreams) { + downstream.doAdd(builder); + } + } + return ret; + } + + private void handleCollision(GT_Recipe recipe) { + StringBuilder errorInfo = new StringBuilder(); + boolean hasAnEntry = false; + for (FluidStack fluid : recipe.mFluidInputs) { + if (fluid == null) { + continue; + } + String s = fluid.getLocalizedName(); + if (s == null) { + continue; + } + if (hasAnEntry) { + errorInfo.append("+") + .append(s); + } else { + errorInfo.append(s); + } + hasAnEntry = true; + } + for (ItemStack item : recipe.mInputs) { + if (item == null) { + continue; + } + String itemName = item.getDisplayName(); + if (hasAnEntry) { + errorInfo.append("+") + .append(itemName); + } else { + errorInfo.append(itemName); + } + hasAnEntry = true; + } + handleRecipeCollision(errorInfo.toString()); + } + + void addDownstream(IRecipeMap downstream) { + downstreams.add(downstream); + } + + /** + * Removes supplied recipes from recipe list. Do not use unless absolute necessity! + */ + public void removeRecipes(Collection<? extends GT_Recipe> recipesToRemove) { + for (Collection<GT_Recipe> recipes : recipesByCategory.values()) { + recipes.removeAll(recipesToRemove); + } + for (GT_ItemStack key : new HashMap<>(itemIndex.asMap()).keySet()) { + itemIndex.get(key) + .removeAll(recipesToRemove); + } + for (String key : new HashMap<>(fluidIndex.asMap()).keySet()) { + fluidIndex.get(key) + .removeAll(recipesToRemove); + } + } + + /** + * Removes supplied recipe from recipe list. Do not use unless absolute necessity! + */ + public void removeRecipe(GT_Recipe recipe) { + removeRecipes(Collections.singleton(recipe)); + } + + /** + * If you want to shoot your foot... + */ + public void clearRecipes() { + recipesByCategory.clear(); + } + + // endregion + + /** + * Re-unificates all the items present in recipes. Also reflects recipe removals. + */ + public void reInit() { + itemIndex.clear(); + for (GT_Recipe recipe : allRecipes()) { + GT_OreDictUnificator.setStackArray(true, recipe.mInputs); + GT_OreDictUnificator.setStackArray(true, recipe.mOutputs); + addToItemMap(recipe); + } + } + + /** + * @return If supplied item is a valid input for any of the recipes + */ + public boolean containsInput(ItemStack item) { + return itemIndex.containsKey(new GT_ItemStack(item)) || itemIndex.containsKey(new GT_ItemStack(item, true)); + } + + /** + * @return If supplied fluid is a valid input for any of the recipes + */ + public boolean containsInput(Fluid fluid) { + return fluidIndex.containsKey(fluid.getName()); + } + + // region find recipe + + /** + * Checks if given recipe conflicts with already registered recipes. + * + * @return True if collision is found. + */ + boolean checkCollision(GT_Recipe recipe) { + return matchRecipeStream(recipe.mInputs, recipe.mFluidInputs, null, null, false, true, true).findAny() + .isPresent(); + } + + /** + * Overwrites {@link #matchRecipeStream} method. Also override {@link #doesOverwriteFindRecipe} to make it work. + */ + @Nullable + protected GT_Recipe overwriteFindRecipe(ItemStack[] items, FluidStack[] fluids, @Nullable ItemStack specialSlot, + @Nullable GT_Recipe cachedRecipe) { + return null; + } + + /** + * @return Whether to use {@link #overwriteFindRecipe} for finding recipe. + */ + protected boolean doesOverwriteFindRecipe() { + return false; + } + + /** + * Modifies successfully found recipe. Make sure not to mutate the found recipe but use copy! + */ + @Nullable + protected GT_Recipe modifyFoundRecipe(GT_Recipe recipe, ItemStack[] items, FluidStack[] fluids, + @Nullable ItemStack specialSlot) { + return recipe; + } + + /** + * Called when {@link #matchRecipeStream} cannot find recipe. + */ + @Nullable + protected GT_Recipe findFallback(ItemStack[] items, FluidStack[] fluids, @Nullable ItemStack specialSlot) { + return null; + } + + /** + * Returns all the matched recipes in the form of Stream, without any additional check for matches. + * + * @param rawItems Item inputs. + * @param fluids Fluid inputs. + * @param specialSlot Content of the special slot. Normal recipemaps don't need this, but some do. + * Set {@link RecipeMapBuilder#specialSlotSensitive} to make it actually functional. + * Alternatively overriding {@link #filterFindRecipe} will also work. + * @param cachedRecipe If this is not null, this method tests it before all other recipes. + * @param notUnificated If this is set to true, item inputs will be unificated. + * @param dontCheckStackSizes If this is set to true, this method won't check item count and fluid amount + * for the matched recipe. + * @param forCollisionCheck If this method is called to check collision with already registered recipes. + * @return Stream of matches recipes. + */ + Stream<GT_Recipe> matchRecipeStream(ItemStack[] rawItems, FluidStack[] fluids, @Nullable ItemStack specialSlot, + @Nullable GT_Recipe cachedRecipe, boolean notUnificated, boolean dontCheckStackSizes, + boolean forCollisionCheck) { + if (doesOverwriteFindRecipe()) { + return GT_StreamUtil.ofNullable(overwriteFindRecipe(rawItems, fluids, specialSlot, cachedRecipe)); + } + + if (recipesByCategory.isEmpty()) { + return Stream.empty(); + } + + // Some recipe classes require a certain amount of inputs of certain kinds. Like "at least 1 fluid + 1 item" + // or "at least 2 items" before they start searching for recipes. + // This improves performance massively, especially when people leave things like programmed circuits, + // molds or shapes in their machines. + // For checking collision, we assume min inputs check already has been passed as of building the recipe. + if (!forCollisionCheck) { + if (properties.minFluidInputs > 0) { + int count = 0; + for (FluidStack fluid : fluids) if (fluid != null) count++; + if (count < properties.minFluidInputs) { + return Stream.empty(); + } + } + if (properties.minItemInputs > 0) { + int count = 0; + for (ItemStack item : rawItems) if (item != null) count++; + if (count < properties.minItemInputs) { + return Stream.empty(); + } + } + } + + ItemStack[] items; + // Unification happens here in case the item input isn't already unificated. + if (notUnificated) { + items = GT_OreDictUnificator.getStackArray(true, (Object[]) rawItems); + } else { + items = rawItems; + } + + return Stream.<Stream<GT_Recipe>>of( + // Check the recipe which has been used last time in order to not have to search for it again, if possible. + GT_StreamUtil.ofNullable(cachedRecipe) + .filter(recipe -> recipe.mCanBeBuffered) + .filter(recipe -> filterFindRecipe(recipe, items, fluids, specialSlot, dontCheckStackSizes)) + .map(recipe -> modifyFoundRecipe(recipe, items, fluids, specialSlot)) + .filter(Objects::nonNull), + // Now look for the recipes inside the item index, but only when the recipes actually can have items inputs. + GT_StreamUtil.ofConditional(!itemIndex.isEmpty(), items) + .filter(Objects::nonNull) + .flatMap(item -> Stream.of(new GT_ItemStack(item), new GT_ItemStack(item, true))) + .map(itemIndex::get) + .flatMap(Collection::stream) + .filter(recipe -> filterFindRecipe(recipe, items, fluids, specialSlot, dontCheckStackSizes)) + .map(recipe -> modifyFoundRecipe(recipe, items, fluids, specialSlot)) + .filter(Objects::nonNull), + // If the minimum amount of items required for the recipes is 0, then it could match to fluid-only recipes, + // so check fluid index too. + GT_StreamUtil.ofConditional(properties.minItemInputs == 0, fluids) + .filter(Objects::nonNull) + .map( + fluidStack -> fluidIndex.get( + fluidStack.getFluid() + .getName())) + .flatMap(Collection::stream) + .filter(recipe -> filterFindRecipe(recipe, items, fluids, specialSlot, dontCheckStackSizes)) + .map(recipe -> modifyFoundRecipe(recipe, items, fluids, specialSlot)) + .filter(Objects::nonNull), + // Lastly, find fallback. + forCollisionCheck ? Stream.empty() + : GT_StreamUtil.ofSupplier(() -> findFallback(items, fluids, specialSlot)) + .filter(Objects::nonNull)) + .flatMap(Function.identity()); + } + + /** + * The minimum filter required for recipe match logic. You can override this to have custom validation. + * <p> + * Other checks like machine voltage will be done in another places. + * <p> + * Note that this won't be called if {@link #doesOverwriteFindRecipe} is true. + */ + protected boolean filterFindRecipe(GT_Recipe recipe, ItemStack[] items, FluidStack[] fluids, + @Nullable ItemStack specialSlot, boolean dontCheckStackSizes) { + if (recipe.mEnabled && !recipe.mFakeRecipe + && recipe.isRecipeInputEqual(false, dontCheckStackSizes, fluids, items)) { + return !properties.specialSlotSensitive + || areStacksEqualOrNull((ItemStack) recipe.mSpecialItems, specialSlot); + } + return false; + } + + // endregion + + @FunctionalInterface + public interface BackendCreator<B extends RecipeMapBackend> { + + /** + * @see RecipeMapBackend#RecipeMapBackend + */ + B create(RecipeMapBackendPropertiesBuilder propertiesBuilder); + } +} diff --git a/src/main/java/gregtech/api/recipe/RecipeMapBackendProperties.java b/src/main/java/gregtech/api/recipe/RecipeMapBackendProperties.java new file mode 100644 index 0000000000..7262b794ab --- /dev/null +++ b/src/main/java/gregtech/api/recipe/RecipeMapBackendProperties.java @@ -0,0 +1,77 @@ +package gregtech.api.recipe; + +import java.util.function.Function; + +import javax.annotation.Nullable; +import javax.annotation.ParametersAreNonnullByDefault; + +import gregtech.api.util.FieldsAreNonnullByDefault; +import gregtech.api.util.GT_Recipe; +import gregtech.api.util.GT_RecipeBuilder; +import gregtech.api.util.MethodsReturnNonnullByDefault; + +/** + * Data object to store properties used for {@link RecipeMapBackend}. Use {@link #builder()} for creation. + */ +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +@FieldsAreNonnullByDefault +public final class RecipeMapBackendProperties { + + static RecipeMapBackendPropertiesBuilder builder() { + return new RecipeMapBackendPropertiesBuilder(); + } + + /** + * Minimum amount of item inputs required for the recipes. + */ + public final int minItemInputs; + /** + * Minimum amount of fluid inputs required for the recipes. + */ + public final int minFluidInputs; + + /** + * Whether this backend should check for equality of special slot when searching recipe. + */ + public final boolean specialSlotSensitive; + + /** + * If recipe builder should stop optimizing inputs. + */ + public final boolean disableOptimize; + + /** + * Changes how recipes are emitted by a particular recipe builder. + */ + public final Function<? super GT_RecipeBuilder, ? extends Iterable<? extends GT_Recipe>> recipeEmitter; + + /** + * Runs a custom hook on all recipes added <b>via builder</b>. + */ + @Nullable + public final Function<? super GT_Recipe, ? extends GT_Recipe> recipeTransformer; + + @Nullable + public final String recipeConfigCategory; + @Nullable + public final Function<? super GT_Recipe, String> recipeConfigKeyConvertor; + + RecipeMapBackendProperties(int minItemInputs, int minFluidInputs, boolean specialSlotSensitive, + boolean disableOptimize, + Function<? super GT_RecipeBuilder, ? extends Iterable<? extends GT_Recipe>> recipeEmitter, + @Nullable Function<? super GT_Recipe, ? extends GT_Recipe> recipeTransformer, + @Nullable String recipeConfigCategory, @Nullable Function<? super GT_Recipe, String> recipeConfigKeyConvertor) { + if (minItemInputs < 0 || minFluidInputs < 0) { + throw new IllegalArgumentException("minItemInputs and minFluidInputs cannot be negative"); + } + this.minItemInputs = minItemInputs; + this.minFluidInputs = minFluidInputs; + this.specialSlotSensitive = specialSlotSensitive; + this.disableOptimize = disableOptimize; + this.recipeEmitter = recipeEmitter; + this.recipeTransformer = recipeTransformer; + this.recipeConfigCategory = recipeConfigCategory; + this.recipeConfigKeyConvertor = recipeConfigKeyConvertor; + } +} diff --git a/src/main/java/gregtech/api/recipe/RecipeMapBackendPropertiesBuilder.java b/src/main/java/gregtech/api/recipe/RecipeMapBackendPropertiesBuilder.java new file mode 100644 index 0000000000..933ea1b06b --- /dev/null +++ b/src/main/java/gregtech/api/recipe/RecipeMapBackendPropertiesBuilder.java @@ -0,0 +1,119 @@ +package gregtech.api.recipe; + +import static gregtech.api.util.GT_RecipeMapUtil.buildOrEmpty; + +import java.util.function.Function; + +import javax.annotation.Nullable; +import javax.annotation.ParametersAreNonnullByDefault; + +import com.google.common.collect.Iterables; + +import gregtech.api.util.GT_Recipe; +import gregtech.api.util.GT_RecipeBuilder; +import gregtech.api.util.MethodsReturnNonnullByDefault; + +/** + * Builder class for {@link RecipeMapBackendProperties}. + */ +@SuppressWarnings({ "unused", "UnusedReturnValue" }) +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +public final class RecipeMapBackendPropertiesBuilder { + + private int minItemInputs; + private int minFluidInputs; + + private boolean specialSlotSensitive; + + private boolean disableOptimize; + + private Function<? super GT_RecipeBuilder, ? extends Iterable<? extends GT_Recipe>> recipeEmitter = this::defaultBuildRecipe; + + @Nullable + private Function<? super GT_Recipe, ? extends GT_Recipe> recipeTransformer; + + @Nullable + private String recipeConfigCategory; + @Nullable + private Function<? super GT_Recipe, String> recipeConfigKeyConvertor; + + RecipeMapBackendPropertiesBuilder() {} + + RecipeMapBackendProperties build() { + return new RecipeMapBackendProperties( + minItemInputs, + minFluidInputs, + specialSlotSensitive, + disableOptimize, + recipeEmitter, + recipeTransformer, + recipeConfigCategory, + recipeConfigKeyConvertor); + } + + public RecipeMapBackendPropertiesBuilder minItemInputs(int minItemInputs) { + this.minItemInputs = minItemInputs; + return this; + } + + public RecipeMapBackendPropertiesBuilder minFluidInputs(int minFluidInputs) { + this.minFluidInputs = minFluidInputs; + return this; + } + + public RecipeMapBackendPropertiesBuilder specialSlotSensitive() { + this.specialSlotSensitive = true; + return this; + } + + public RecipeMapBackendPropertiesBuilder disableOptimize() { + this.disableOptimize = true; + return this; + } + + public RecipeMapBackendPropertiesBuilder recipeEmitter( + Function<? super GT_RecipeBuilder, ? extends Iterable<? extends GT_Recipe>> recipeEmitter) { + this.recipeEmitter = recipeEmitter; + return this; + } + + public RecipeMapBackendPropertiesBuilder combineRecipeEmitter( + Function<? super GT_RecipeBuilder, ? extends Iterable<? extends GT_Recipe>> func) { + // move recipeEmitter to local variable, so lambda capture the function itself instead of this + Function<? super GT_RecipeBuilder, ? extends Iterable<? extends GT_Recipe>> cur = this.recipeEmitter; + return recipeEmitter(b -> Iterables.concat(cur.apply(b), func.apply(b))); + } + + public RecipeMapBackendPropertiesBuilder recipeTransformer( + Function<? super GT_Recipe, ? extends GT_Recipe> recipeTransformer) { + this.recipeTransformer = recipeTransformer; + return this; + } + + public RecipeMapBackendPropertiesBuilder chainRecipeTransformer( + Function<? super GT_Recipe, ? extends GT_Recipe> func) { + this.recipeTransformer = this.recipeTransformer == null ? func : this.recipeTransformer.andThen(func); + return this; + } + + public RecipeMapBackendPropertiesBuilder recipeConfigFile(String category, + Function<? super GT_Recipe, String> keyConvertor) { + this.recipeConfigCategory = category; + this.recipeConfigKeyConvertor = keyConvertor; + return this; + } + + private Iterable<? extends GT_Recipe> defaultBuildRecipe(GT_RecipeBuilder builder) { + // TODO sensible validation + GT_RecipeBuilder b = builder; + if (disableOptimize && builder.isOptimize()) { + b = copy(builder, b).noOptimize(); + } + return buildOrEmpty(b); + } + + private static GT_RecipeBuilder copy(GT_RecipeBuilder original, GT_RecipeBuilder b) { + return b == original ? b.copy() : b; + } +} diff --git a/src/main/java/gregtech/api/recipe/RecipeMapBuilder.java b/src/main/java/gregtech/api/recipe/RecipeMapBuilder.java new file mode 100644 index 0000000000..8659018934 --- /dev/null +++ b/src/main/java/gregtech/api/recipe/RecipeMapBuilder.java @@ -0,0 +1,522 @@ +package gregtech.api.recipe; + +import static gregtech.api.enums.Mods.GregTech; + +import java.awt.Rectangle; +import java.util.Collections; +import java.util.Comparator; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.UnaryOperator; + +import javax.annotation.ParametersAreNonnullByDefault; + +import org.apache.commons.lang3.StringUtils; + +import com.gtnewhorizons.modularui.api.drawable.FallbackableUITexture; +import com.gtnewhorizons.modularui.api.drawable.IDrawable; +import com.gtnewhorizons.modularui.api.drawable.UITexture; +import com.gtnewhorizons.modularui.api.math.Pos2d; +import com.gtnewhorizons.modularui.api.math.Size; +import com.gtnewhorizons.modularui.common.widget.ProgressBar; + +import codechicken.nei.recipe.HandlerInfo; +import gregtech.api.gui.modularui.FallbackableSteamTexture; +import gregtech.api.gui.modularui.GT_UITextures; +import gregtech.api.gui.modularui.SteamTexture; +import gregtech.api.objects.overclockdescriber.OverclockDescriber; +import gregtech.api.util.GT_Recipe; +import gregtech.api.util.GT_RecipeBuilder; +import gregtech.api.util.MethodsReturnNonnullByDefault; +import gregtech.nei.formatter.INEISpecialInfoFormatter; + +// spotless:off spotless likes formatting @code to @code +/** + * Builder class for constructing {@link RecipeMap}. Instantiate this class and call {@link #build} + * to retrieve RecipeMap. Smallest example: + * + * <pre> + * {@code + * RecipeMap<RecipeMapBackend> exampleRecipes = RecipeMapBuilder.of("example") + * .maxIO(9, 4, 1, 1) + * .build(); + * } + * </pre> + * + * Note that {@link #maxIO} is required to build. + */ +// spotless:on +@SuppressWarnings("unused") +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +public final class RecipeMapBuilder<B extends RecipeMapBackend> { + + private final String unlocalizedName; + private final RecipeMapBackendPropertiesBuilder backendPropertiesBuilder = RecipeMapBackendProperties.builder(); + private final RecipeMapBackend.BackendCreator<B> backendCreator; + private final BasicUIPropertiesBuilder uiPropertiesBuilder; + private final NEIRecipePropertiesBuilder neiPropertiesBuilder = NEIRecipeProperties.builder(); + private RecipeMapFrontend.FrontendCreator frontendCreator = RecipeMapFrontend::new; + + /** + * Constructs builder object for {@link RecipeMap} with given backend logic. For custom frontend, + * call {@link #frontend} for the created builder object. + * + * @param unlocalizedName Unique identifier for the recipemap. This is also used as translation key + * for NEI recipe GUI header, so add localization for it if needed. + * @return New builder object. + */ + public static <B extends RecipeMapBackend> RecipeMapBuilder<B> of(String unlocalizedName, + RecipeMapBackend.BackendCreator<B> backendCreator) { + return new RecipeMapBuilder<>(unlocalizedName, backendCreator); + } + + /** + * Constructs builder object for {@link RecipeMap}. + * + * @param unlocalizedName Unique identifier for the recipemap. This is also used as translation key + * for NEI recipe GUI header, so add localization for it if needed. + * @return New builder object. + */ + public static RecipeMapBuilder<RecipeMapBackend> of(String unlocalizedName) { + return new RecipeMapBuilder<>(unlocalizedName, RecipeMapBackend::new); + } + + private RecipeMapBuilder(String unlocalizedName, RecipeMapBackend.BackendCreator<B> backendCreator) { + this.unlocalizedName = unlocalizedName; + this.backendCreator = backendCreator; + this.uiPropertiesBuilder = BasicUIProperties.builder() + .progressBarTexture(GT_UITextures.fallbackableProgressbar(unlocalizedName, GT_UITextures.PROGRESSBAR_ARROW)) + .neiTransferRectId(unlocalizedName); + } + + // region backend + + /** + * Sets minimum amount of inputs required for the recipes. + */ + public RecipeMapBuilder<B> minInputs(int minItemInputs, int minFluidInputs) { + backendPropertiesBuilder.minItemInputs(minItemInputs) + .minFluidInputs(minFluidInputs); + return this; + } + + /** + * Whether this recipemap should check for equality of special slot when searching recipe. + */ + public RecipeMapBuilder<B> specialSlotSensitive() { + backendPropertiesBuilder.specialSlotSensitive(); + return this; + } + + /** + * If recipe builder should stop optimizing inputs. + */ + public RecipeMapBuilder<B> disableOptimize() { + backendPropertiesBuilder.disableOptimize(); + return this; + } + + /** + * Changes how recipes are emitted by a particular recipe builder. Can emit multiple recipe per builder. + */ + public RecipeMapBuilder<B> recipeEmitter( + Function<? super GT_RecipeBuilder, ? extends Iterable<? extends GT_Recipe>> recipeEmitter) { + backendPropertiesBuilder.recipeEmitter(recipeEmitter); + return this; + } + + /** + * Changes how recipes are emitted by a particular recipe builder. Should not return null. + * <p> + * Recipes added via one of the overloads of addRecipe will NOT be affected by this function. + */ + public RecipeMapBuilder<B> recipeEmitterSingle( + Function<? super GT_RecipeBuilder, ? extends GT_Recipe> recipeEmitter) { + return recipeEmitter(recipeEmitter.andThen(Collections::singletonList)); + } + + /** + * Changes how recipes are emitted by a particular recipe builder. Can emit multiple recipe per builder. + * <p> + * Recipes added via one of the overloads of addRecipe will NOT be affected by this function. + * <p> + * Unlike {@link #recipeEmitter(Function)}, this one does not clear the existing recipe being emitted, if any + */ + public RecipeMapBuilder<B> combineRecipeEmitter( + Function<? super GT_RecipeBuilder, ? extends Iterable<? extends GT_Recipe>> recipeEmitter) { + backendPropertiesBuilder.combineRecipeEmitter(recipeEmitter); + return this; + } + + /** + * Changes how recipes are emitted by a particular recipe builder. Effectively add a new recipe per recipe added. + * func must not return null. + * <p> + * Recipes added via one of the overloads of addRecipe will NOT be affected by this function. + * <p> + * Unlike {@link #recipeEmitter(Function)}, this one does not clear the existing recipe being emitted, if any + */ + public RecipeMapBuilder<B> combineRecipeEmitterSingle( + Function<? super GT_RecipeBuilder, ? extends GT_Recipe> recipeEmitter) { + return combineRecipeEmitter(recipeEmitter.andThen(Collections::singletonList)); + } + + /** + * Runs a custom hook on all recipes added <b>via builder</b>. For more complicated behavior, + * use {@link #recipeEmitter}. + * <p> + * Recipes added via one of the overloads of addRecipe will NOT be affected by this function. + */ + public RecipeMapBuilder<B> recipeTransformer(Function<? super GT_Recipe, ? extends GT_Recipe> recipeTransformer) { + backendPropertiesBuilder.recipeTransformer(recipeTransformer); + return this; + } + + /** + * Runs a custom hook on all recipes added <b>via builder</b>. For more complicated behavior, + * use {@link #recipeEmitter}. + * <p> + * Recipes added via one of the overloads of addRecipe will NOT be affected by this function. + */ + public RecipeMapBuilder<B> recipeTransformer(Consumer<GT_Recipe> recipeTransformer) { + return recipeTransformer(withIdentityReturn(recipeTransformer)); + } + + /** + * Runs a custom hook on all recipes added <b>via builder</b>. For more complicated behavior, + * use {@link #recipeEmitter}. + * <p> + * Recipes added via one of the overloads of addRecipe will NOT be affected by this function. + * <p> + * Unlike {@link #recipeTransformer(Function)}, this one will not replace the existing special handler. + * The supplied function will be given the output of existing handler when a recipe is added. + */ + public RecipeMapBuilder<B> chainRecipeTransformer( + Function<? super GT_Recipe, ? extends GT_Recipe> recipeTransformer) { + backendPropertiesBuilder.chainRecipeTransformer(recipeTransformer); + return this; + } + + /** + * Runs a custom hook on all recipes added <b>via builder</b>. For more complicated behavior, + * use {@link #recipeEmitter}. + * <p> + * Recipes added via one of the overloads of addRecipe will NOT be affected by this function. + * <p> + * Unlike {@link #recipeTransformer(Function)}, this one will not replace the existing special handler. + * The supplied function will be given the output of existing handler when a recipe is added. + */ + public RecipeMapBuilder<B> chainRecipeTransformer(Consumer<GT_Recipe> recipeTransformer) { + return chainRecipeTransformer(withIdentityReturn(recipeTransformer)); + } + + public RecipeMapBuilder<B> recipeConfigFile(String category, Function<? super GT_Recipe, String> keyConvertor) { + if (StringUtils.isBlank(category)) throw new IllegalArgumentException(); + backendPropertiesBuilder.recipeConfigFile(category, keyConvertor); + return this; + } + + // endregion + + // region frontend UI properties + + /** + * Sets how many item/fluid inputs/outputs does this recipemap usually has at most. + * It does not actually restrict the number of items that can be used in recipes. + */ + public RecipeMapBuilder<B> maxIO(int maxItemInputs, int maxItemOutputs, int maxFluidInputs, int maxFluidOutputs) { + uiPropertiesBuilder.maxItemInputs(maxItemInputs) + .maxItemOutputs(maxItemOutputs) + .maxFluidInputs(maxFluidInputs) + .maxFluidOutputs(maxFluidOutputs); + return this; + } + + /** + * Sets function to get overlay for slots. + */ + public RecipeMapBuilder<B> slotOverlays(BasicUIProperties.SlotOverlayGetter<IDrawable> slotOverlays) { + uiPropertiesBuilder.slotOverlays(slotOverlays); + return this; + } + + /** + * Sets function to get overlay for slots of steam machines. + */ + public RecipeMapBuilder<B> slotOverlaysSteam(BasicUIProperties.SlotOverlayGetter<SteamTexture> slotOverlaysSteam) { + uiPropertiesBuilder.slotOverlaysSteam(slotOverlaysSteam); + return this; + } + + /** + * Sets texture and animation direction of the progressbar. + * <p> + * Unless specified, size should be (20, 36), consisting of two parts; + * First is (20, 18) size of "empty" image at the top, Second is (20, 18) size of "filled" image at the bottom. + * <p> + * By default, it's set to {@code GT_UITextures.PROGRESSBAR_ARROW, ProgressBar.Direction.RIGHT}. + */ + public RecipeMapBuilder<B> progressBar(UITexture texture, ProgressBar.Direction direction) { + return progressBarWithFallback(GT_UITextures.fallbackableProgressbar(unlocalizedName, texture), direction); + } + + /** + * Sets progressbar texture with right direction. + * <p> + * Unless specified, size should be (20, 36), consisting of two parts; + * First is (20, 18) size of "empty" image at the top, Second is (20, 18) size of "filled" image at the bottom. + */ + public RecipeMapBuilder<B> progressBar(UITexture texture) { + return progressBar(texture, ProgressBar.Direction.RIGHT); + } + + /** + * Some resource packs want to use custom progress bar textures even for plain arrow. This method allows them to + * add unique textures, yet other packs don't need to make textures for every recipemap. + */ + private RecipeMapBuilder<B> progressBarWithFallback(FallbackableUITexture texture, + ProgressBar.Direction direction) { + uiPropertiesBuilder.progressBarTexture(texture) + .progressBarDirection(direction); + return this; + } + + /** + * Sets progressbar texture for steam machines. + * <p> + * Unless specified, size should be (20, 36), consisting of two parts; + * First is (20, 18) size of "empty" image at the top, Second is (20, 18) size of "filled" image at the bottom. + */ + public RecipeMapBuilder<B> progressBarSteam(SteamTexture texture) { + return progressBarSteamWithFallback( + new FallbackableSteamTexture( + SteamTexture.fullImage(GregTech.ID, "gui/progressbar/" + unlocalizedName + "_%s"), + texture)); + } + + private RecipeMapBuilder<B> progressBarSteamWithFallback(FallbackableSteamTexture texture) { + uiPropertiesBuilder.progressBarTextureSteam(texture); + return this; + } + + /** + * Sets size of the progressbar. (20, 36) by default. + */ + public RecipeMapBuilder<B> progressBarSize(int x, int y) { + uiPropertiesBuilder.progressBarSize(new Size(x, y)); + return this; + } + + /** + * Sets position of the progressbar. (78, 24) by default. + */ + public RecipeMapBuilder<B> progressBarPos(int x, int y) { + uiPropertiesBuilder.progressBarPos(new Pos2d(x, y)); + return this; + } + + /** + * Stops adding progressbar to the UI. + */ + public RecipeMapBuilder<B> dontUseProgressBar() { + uiPropertiesBuilder.useProgressBar(false); + return this; + } + + /** + * Configures this recipemap to use special slot. This means special slot shows up on NEI and tooltip for + * special slot on basic machine GUI indicates it has actual usage. + */ + public RecipeMapBuilder<B> useSpecialSlot() { + uiPropertiesBuilder.useSpecialSlot(true); + return this; + } + + /** + * Adds GUI area where clicking shows up all the recipes available. + * + * @see codechicken.nei.recipe.TemplateRecipeHandler.RecipeTransferRect + */ + public RecipeMapBuilder<B> neiTransferRect(int x, int y, int width, int height) { + uiPropertiesBuilder.addNEITransferRect(new Rectangle(x, y, width, height)); + return this; + } + + /** + * Sets ID used to open NEI recipe GUI when progressbar is clicked. + */ + public RecipeMapBuilder<B> neiTransferRectId(String neiTransferRectId) { + uiPropertiesBuilder.neiTransferRectId(neiTransferRectId); + return this; + } + + /** + * Adds additional textures shown on GUI. + */ + public RecipeMapBuilder<B> addSpecialTexture(int x, int y, int width, int height, IDrawable texture) { + uiPropertiesBuilder.addSpecialTexture(new Size(width, height), new Pos2d(x, y), texture); + return this; + } + + /** + * Adds additional textures shown on steam machine GUI. + */ + public RecipeMapBuilder<B> addSpecialTextureSteam(int x, int y, int width, int height, SteamTexture texture) { + uiPropertiesBuilder.addSpecialTextureSteam(new Size(width, height), new Pos2d(x, y), texture); + return this; + } + + /** + * Sets logo shown on GUI. GregTech logo by default. + */ + public RecipeMapBuilder<B> logo(IDrawable logo) { + uiPropertiesBuilder.logo(logo); + return this; + } + + /** + * Sets size of logo. (17, 17) by default. + */ + public RecipeMapBuilder<B> logoSize(int width, int height) { + uiPropertiesBuilder.logoSize(new Size(width, height)); + return this; + } + + /** + * Sets position of logo. (152, 63) by default. + */ + public RecipeMapBuilder<B> logoPos(int x, int y) { + uiPropertiesBuilder.logoPos(new Pos2d(x, y)); + return this; + } + + /** + * Sets amperage for the recipemap. + */ + public RecipeMapBuilder<B> amperage(int amperage) { + uiPropertiesBuilder.amperage(amperage); + return this; + } + + // endregion + + // region frontend NEI properties + + /** + * Stops adding dedicated NEI recipe page for this recipemap. This does not prevent adding transferrect + * for the machine GUI. + */ + public RecipeMapBuilder<B> disableRegisterNEI() { + neiPropertiesBuilder.disableRegisterNEI(); + return this; + } + + /** + * Sets properties of NEI handler info this recipemap belongs to. You can specify icon shown on recipe tab, + * handler height, number of recipes per page, etc. Either use supplied template or return newly constructed one. + * <p> + * Invocation of the builder creator is delayed until the actual registration (FMLLoadCompleteEvent), + * so you can safely use itemstack that doesn't exist as of recipemap initialization. + * <p> + * If this method is not used, handler icon will be inferred from recipe catalysts associated with this recipemap. + * <p> + * Precisely, what's registered to NEI is {@link RecipeCategory}, not RecipeMap. However, handler info supplied + * by this method will be used for default category where most of the recipes belong to. + */ + public RecipeMapBuilder<B> neiHandlerInfo(UnaryOperator<HandlerInfo.Builder> handlerInfoCreator) { + neiPropertiesBuilder.handlerInfoCreator(handlerInfoCreator); + return this; + } + + /** + * Sets offset of background shown on NEI. + */ + public RecipeMapBuilder<B> neiRecipeBackgroundSize(int width, int height) { + neiPropertiesBuilder.recipeBackgroundSize(new Size(width, height)); + return this; + } + + /** + * Sets size of background shown on NEI. + */ + public RecipeMapBuilder<B> neiRecipeBackgroundOffset(int x, int y) { + neiPropertiesBuilder.recipeBackgroundOffset(new Pos2d(x, y)); + return this; + } + + /** + * Sets formatter for special description for the recipe, mainly {@link gregtech.api.util.GT_Recipe#mSpecialValue}. + */ + public RecipeMapBuilder<B> neiSpecialInfoFormatter(INEISpecialInfoFormatter neiSpecialInfoFormatter) { + neiPropertiesBuilder.neiSpecialInfoFormatter(neiSpecialInfoFormatter); + return this; + } + + /** + * Sets whether to show oredict equivalent item outputs on NEI. + */ + public RecipeMapBuilder<B> unificateOutputNEI(boolean unificateOutputNEI) { + neiPropertiesBuilder.unificateOutput(unificateOutputNEI); + return this; + } + + /** + * Sets NEI recipe handler to use a custom filter method {@link OverclockDescriber#canHandle} to limit the shown + * recipes when searching recipes with recipe catalyst. Without calling this method, the voltage of the recipe is + * the only factor to filter recipes by default. + * <p> + * This method on its own doesn't do anything. You need to bind custom {@link OverclockDescriber} object to machines + * that will be shown as recipe catalysts for this recipemap by implementing + * {@link gregtech.api.interfaces.tileentity.IOverclockDescriptionProvider}. + */ + public RecipeMapBuilder<B> useCustomFilterForNEI() { + neiPropertiesBuilder.useCustomFilter(); + return this; + } + + /** + * Stops rendering the actual stack size of items on NEI. + */ + public RecipeMapBuilder<B> disableRenderRealStackSizes() { + neiPropertiesBuilder.disableRenderRealStackSizes(); + return this; + } + + /** + * Sets custom comparator for NEI recipe sort. + */ + public RecipeMapBuilder<B> neiRecipeComparator(Comparator<GT_Recipe> comparator) { + neiPropertiesBuilder.recipeComparator(comparator); + return this; + } + + // endregion + + /** + * Sets custom frontend logic. For custom backend, pass it to {@link #of(String, RecipeMapBackend.BackendCreator)}. + */ + public RecipeMapBuilder<B> frontend(RecipeMapFrontend.FrontendCreator frontendCreator) { + this.frontendCreator = frontendCreator; + return this; + } + + /** + * Builds new recipemap. + * + * @return Recipemap object with backend type parameter, which is {@code RecipeMapFrontend} unless specified. + */ + public RecipeMap<B> build() { + return new RecipeMap<>( + unlocalizedName, + backendCreator.create(backendPropertiesBuilder), + frontendCreator.create(uiPropertiesBuilder, neiPropertiesBuilder)); + } + + private static <T> Function<? super T, ? extends T> withIdentityReturn(Consumer<T> func) { + return r -> { + func.accept(r); + return r; + }; + } +} diff --git a/src/main/java/gregtech/api/recipe/RecipeMapFrontend.java b/src/main/java/gregtech/api/recipe/RecipeMapFrontend.java new file mode 100644 index 0000000000..63daa00dc7 --- /dev/null +++ b/src/main/java/gregtech/api/recipe/RecipeMapFrontend.java @@ -0,0 +1,395 @@ +package gregtech.api.recipe; + +import static gregtech.api.util.GT_Utility.trans; +import static net.minecraft.util.EnumChatFormatting.GRAY; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; +import java.util.stream.IntStream; + +import javax.annotation.ParametersAreNonnullByDefault; + +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.item.ItemStack; +import net.minecraft.util.EnumChatFormatting; + +import org.apache.commons.lang3.tuple.Pair; + +import com.gtnewhorizons.modularui.api.GlStateManager; +import com.gtnewhorizons.modularui.api.ModularUITextures; +import com.gtnewhorizons.modularui.api.drawable.IDrawable; +import com.gtnewhorizons.modularui.api.forge.IItemHandlerModifiable; +import com.gtnewhorizons.modularui.api.math.Alignment; +import com.gtnewhorizons.modularui.api.math.Pos2d; +import com.gtnewhorizons.modularui.api.math.Size; +import com.gtnewhorizons.modularui.api.screen.ModularWindow; +import com.gtnewhorizons.modularui.common.widget.DrawableWidget; +import com.gtnewhorizons.modularui.common.widget.ProgressBar; +import com.gtnewhorizons.modularui.common.widget.SlotWidget; + +import codechicken.nei.PositionedStack; +import gregtech.GT_Mod; +import gregtech.api.enums.SteamVariant; +import gregtech.api.gui.GT_GUIColorOverride; +import gregtech.api.gui.modularui.GT_UITextures; +import gregtech.api.recipe.metadata.IRecipeMetadataStorage; +import gregtech.api.util.GT_Recipe; +import gregtech.api.util.MethodsReturnNonnullByDefault; +import gregtech.common.gui.modularui.UIHelper; +import gregtech.nei.GT_NEI_DefaultHandler; +import gregtech.nei.RecipeDisplayInfo; + +/** + * Responsible for managing GUI tied to recipemap. It has two property objects, {@link NEIRecipeProperties} and + * {@link BasicUIProperties}. The former is only for NEI display, while the latter is for both NEI and basic machine. + * <p> + * In order to bind custom frontend to recipemap, use {@link RecipeMapBuilder#frontend}. + */ +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +public class RecipeMapFrontend { + + /** + * Properties specific to this frontend, mainly for GUI widgets. + */ + protected final BasicUIProperties uiProperties; + /** + * Properties specific to this frontend, only for NEI specific settings. + */ + protected final NEIRecipeProperties neiProperties; + + protected final GT_GUIColorOverride colorOverride = GT_GUIColorOverride + .get(GT_UITextures.BACKGROUND_NEI_SINGLE_RECIPE.location); + + public RecipeMapFrontend(BasicUIPropertiesBuilder uiPropertiesBuilder, + NEIRecipePropertiesBuilder neiPropertiesBuilder) { + this.uiProperties = uiPropertiesBuilder.itemInputPositionsGetter(this::getItemInputPositions) + .itemOutputPositionsGetter(this::getItemOutputPositions) + .specialItemPositionGetter(this::getSpecialItemPosition) + .fluidInputPositionsGetter(this::getFluidInputPositions) + .fluidOutputPositionsGetter(this::getFluidOutputPositions) + .build(); + this.neiProperties = neiPropertiesBuilder.build(); + } + + /** + * @return Properties specific to this frontend, mainly for GUI widgets. + */ + public BasicUIProperties getUIProperties() { + return uiProperties; + } + + /** + * @return Properties specific to this frontend, only for NEI specific settings. + */ + public NEIRecipeProperties getNEIProperties() { + return neiProperties; + } + + /** + * Creates NEI recipe layout, except for actual items / fluids. + */ + public ModularWindow.Builder createNEITemplate(IItemHandlerModifiable itemInputsInventory, + IItemHandlerModifiable itemOutputsInventory, IItemHandlerModifiable specialSlotInventory, + IItemHandlerModifiable fluidInputsInventory, IItemHandlerModifiable fluidOutputsInventory, + Supplier<Float> progressSupplier, Pos2d windowOffset) { + ModularWindow.Builder builder = ModularWindow.builder(neiProperties.recipeBackgroundSize) + .setBackground(GT_UITextures.BACKGROUND_NEI_SINGLE_RECIPE); + + UIHelper.forEachSlots( + (i, backgrounds, pos) -> builder.widget( + SlotWidget.phantom(itemInputsInventory, i) + .setBackground(backgrounds) + .setPos(pos) + .setSize(18, 18)), + (i, backgrounds, pos) -> builder.widget( + SlotWidget.phantom(itemOutputsInventory, i) + .setBackground(backgrounds) + .setPos(pos) + .setSize(18, 18)), + (i, backgrounds, pos) -> { + if (uiProperties.useSpecialSlot) builder.widget( + SlotWidget.phantom(specialSlotInventory, 0) + .setBackground(backgrounds) + .setPos(pos) + .setSize(18, 18)); + }, + (i, backgrounds, pos) -> builder.widget( + SlotWidget.phantom(fluidInputsInventory, i) + .setBackground(backgrounds) + .setPos(pos) + .setSize(18, 18)), + (i, backgrounds, pos) -> builder.widget( + SlotWidget.phantom(fluidOutputsInventory, i) + .setBackground(backgrounds) + .setPos(pos) + .setSize(18, 18)), + ModularUITextures.ITEM_SLOT, + ModularUITextures.FLUID_SLOT, + uiProperties, + uiProperties.maxItemInputs, + uiProperties.maxItemOutputs, + uiProperties.maxFluidInputs, + uiProperties.maxFluidOutputs, + SteamVariant.NONE, + windowOffset); + + if (uiProperties.useProgressBar) { + addProgressBar(builder, progressSupplier, windowOffset); + } + addGregTechLogo(builder, windowOffset); + + for (Pair<IDrawable, Pair<Size, Pos2d>> specialTexture : uiProperties.specialTextures) { + builder.widget( + new DrawableWidget().setDrawable(specialTexture.getLeft()) + .setSize( + specialTexture.getRight() + .getLeft()) + .setPos( + specialTexture.getRight() + .getRight() + .add(windowOffset))); + } + + return builder; + } + + public void addProgressBar(ModularWindow.Builder builder, Supplier<Float> progressSupplier, Pos2d windowOffset) { + assert uiProperties.progressBarTexture != null; + builder.widget( + new ProgressBar().setTexture(uiProperties.progressBarTexture.get(), 20) + .setDirection(uiProperties.progressBarDirection) + .setProgress(progressSupplier) + .setSynced(false, false) + .setPos(uiProperties.progressBarPos.add(windowOffset)) + .setSize(uiProperties.progressBarSize)); + } + + public void addGregTechLogo(ModularWindow.Builder builder, Pos2d windowOffset) { + builder.widget( + new DrawableWidget().setDrawable(uiProperties.logo) + .setSize(uiProperties.logoSize) + .setPos(uiProperties.logoPos.add(windowOffset))); + } + + /** + * Overriding this method allows custom NEI stack placement + */ + public List<Pos2d> getItemInputPositions(int itemInputCount) { + return UIHelper.getItemInputPositions(itemInputCount); + } + + /** + * Overriding this method allows custom NEI stack placement + */ + public List<Pos2d> getItemOutputPositions(int itemOutputCount) { + return UIHelper.getItemOutputPositions(itemOutputCount); + } + + /** + * Overriding this method allows custom NEI stack placement + */ + public Pos2d getSpecialItemPosition() { + return UIHelper.getSpecialItemPosition(); + } + + /** + * Overriding this method allows custom NEI stack placement + */ + public List<Pos2d> getFluidInputPositions(int fluidInputCount) { + return UIHelper.getFluidInputPositions(fluidInputCount); + } + + /** + * Overriding this method allows custom NEI stack placement + */ + public List<Pos2d> getFluidOutputPositions(int fluidOutputCount) { + return UIHelper.getFluidOutputPositions(fluidOutputCount); + } + + public void drawDescription(RecipeDisplayInfo recipeInfo) { + drawEnergyInfo(recipeInfo); + drawDurationInfo(recipeInfo); + drawSpecialInfo(recipeInfo); + drawMetadataInfo(recipeInfo); + drawRecipeOwnerInfo(recipeInfo); + } + + protected void drawEnergyInfo(RecipeDisplayInfo recipeInfo) { + recipeInfo.overclockDescriber.drawEnergyInfo(recipeInfo); + } + + protected void drawDurationInfo(RecipeDisplayInfo recipeInfo) { + recipeInfo.overclockDescriber.drawDurationInfo(recipeInfo); + } + + protected void drawSpecialInfo(RecipeDisplayInfo recipeInfo) { + String[] recipeDesc = recipeInfo.recipe.getNeiDesc(); + if (recipeDesc != null) { + for (String s : recipeDesc) { + recipeInfo.drawText(s); + } + } else { + recipeInfo.drawTextMultipleLines(neiProperties.neiSpecialInfoFormatter.format(recipeInfo)); + } + } + + protected void drawMetadataInfo(RecipeDisplayInfo recipeInfo) { + IRecipeMetadataStorage metadataStorage = recipeInfo.recipe.getMetadataStorage(); + for (Map.Entry<RecipeMetadataKey<?>, Object> entry : metadataStorage.getEntries()) { + entry.getKey() + .drawInfo(recipeInfo, entry.getValue()); + } + } + + protected void drawRecipeOwnerInfo(RecipeDisplayInfo recipeInfo) { + GT_Recipe recipe = recipeInfo.recipe; + if (GT_Mod.gregtechproxy.mNEIRecipeOwner) { + if (recipe.owners.size() > 1) { + recipeInfo.drawText( + EnumChatFormatting.ITALIC + trans("273", "Original Recipe by: ") + + recipe.owners.get(0) + .getName()); + for (int i = 1; i < recipe.owners.size(); i++) { + recipeInfo.drawText( + EnumChatFormatting.ITALIC + trans("274", "Modified by: ") + + recipe.owners.get(i) + .getName()); + } + } else if (!recipe.owners.isEmpty()) { + recipeInfo.drawText( + EnumChatFormatting.ITALIC + trans("272", "Recipe by: ") + + recipe.owners.get(0) + .getName()); + } + } + if (GT_Mod.gregtechproxy.mNEIRecipeOwnerStackTrace && recipe.stackTraces != null + && !recipe.stackTraces.isEmpty()) { + recipeInfo.drawText("stackTrace:"); + // todo: good way to show all stacktraces + for (String stackTrace : recipe.stackTraces.get(0)) { + recipeInfo.drawText(stackTrace); + } + } + } + + public List<String> handleNEIItemTooltip(ItemStack stack, List<String> currentTip, + GT_NEI_DefaultHandler.CachedDefaultRecipe neiCachedRecipe) { + for (PositionedStack pStack : neiCachedRecipe.mInputs) { + if (stack == pStack.item) { + if (pStack instanceof GT_NEI_DefaultHandler.FixedPositionedStack) { + currentTip = handleNEIItemInputTooltip( + currentTip, + (GT_NEI_DefaultHandler.FixedPositionedStack) pStack); + } + break; + } + } + for (PositionedStack pStack : neiCachedRecipe.mOutputs) { + if (stack == pStack.item) { + if (pStack instanceof GT_NEI_DefaultHandler.FixedPositionedStack) { + currentTip = handleNEIItemOutputTooltip( + currentTip, + (GT_NEI_DefaultHandler.FixedPositionedStack) pStack); + } + break; + } + } + return currentTip; + } + + protected List<String> handleNEIItemInputTooltip(List<String> currentTip, + GT_NEI_DefaultHandler.FixedPositionedStack pStack) { + if (pStack.isNotConsumed()) { + currentTip.add(GRAY + trans("151", "Does not get consumed in the process")); + } + return currentTip; + } + + protected List<String> handleNEIItemOutputTooltip(List<String> currentTip, + GT_NEI_DefaultHandler.FixedPositionedStack pStack) { + if (pStack.isChanceBased()) { + currentTip.add(GRAY + trans("150", "Chance: ") + pStack.getChanceText()); + } + return currentTip; + } + + public void drawNEIOverlays(GT_NEI_DefaultHandler.CachedDefaultRecipe neiCachedRecipe) { + for (PositionedStack stack : neiCachedRecipe.mInputs) { + if (stack instanceof GT_NEI_DefaultHandler.FixedPositionedStack) { + drawNEIOverlayForInput((GT_NEI_DefaultHandler.FixedPositionedStack) stack); + } + } + for (PositionedStack stack : neiCachedRecipe.mOutputs) { + if (stack instanceof GT_NEI_DefaultHandler.FixedPositionedStack) { + drawNEIOverlayForOutput((GT_NEI_DefaultHandler.FixedPositionedStack) stack); + } + } + } + + protected void drawNEIOverlayForInput(GT_NEI_DefaultHandler.FixedPositionedStack stack) { + if (stack.isNotConsumed()) { + drawNEIOverlayText("NC", stack); + } + } + + protected void drawNEIOverlayForOutput(GT_NEI_DefaultHandler.FixedPositionedStack stack) { + if (stack.isChanceBased()) { + drawNEIOverlayText(stack.getChanceText(), stack); + } + } + + @SuppressWarnings("SameParameterValue") + protected void drawNEIOverlayText(String text, PositionedStack stack, int color, float scale, boolean shadow, + Alignment alignment) { + FontRenderer fontRenderer = net.minecraft.client.Minecraft.getMinecraft().fontRenderer; + int width = fontRenderer.getStringWidth(text); + int x = (int) ((stack.relx + 8 + 8 * alignment.x) / scale) - (width / 2 * (alignment.x + 1)); + int y = (int) ((stack.rely + 8 + 8 * alignment.y) / scale) - (fontRenderer.FONT_HEIGHT / 2 * (alignment.y + 1)) + - (alignment.y - 1) / 2; + + GlStateManager.pushMatrix(); + GlStateManager.scale(scale, scale, 1); + fontRenderer.drawString(text, x, y, color, shadow); + GlStateManager.popMatrix(); + } + + protected void drawNEIOverlayText(String text, PositionedStack stack) { + drawNEIOverlayText( + text, + stack, + colorOverride.getTextColorOrDefault("nei_overlay_yellow", 0xFDD835), + 0.5f, + false, + Alignment.TopLeft); + } + + public static List<Supplier<Float>> splitProgress(Supplier<Float> progress, int... progressbarLengthArray) { + float lengthSum = IntStream.of(progressbarLengthArray) + .sum(); + List<Supplier<Float>> ret = new ArrayList<>(); + float currentLengthSum = 0; + for (int progressbarLength : progressbarLengthArray) { + float speed = lengthSum / progressbarLength; + float offset = currentLengthSum / lengthSum; + ret.add(() -> { + float current = progress.get(); + return (current - offset) * speed; + }); + currentLengthSum += progressbarLength; + } + return ret; + } + + @FunctionalInterface + public interface FrontendCreator { + + /** + * @see RecipeMapFrontend#RecipeMapFrontend + */ + RecipeMapFrontend create(BasicUIPropertiesBuilder uiPropertiesBuilder, + NEIRecipePropertiesBuilder neiPropertiesBuilder); + } +} diff --git a/src/main/java/gregtech/api/recipe/RecipeMaps.java b/src/main/java/gregtech/api/recipe/RecipeMaps.java new file mode 100644 index 0000000000..4494d1efba --- /dev/null +++ b/src/main/java/gregtech/api/recipe/RecipeMaps.java @@ -0,0 +1,1194 @@ +package gregtech.api.recipe; + +import static gregtech.api.enums.Mods.GTNHIntergalactic; +import static gregtech.api.enums.Mods.GTPlusPlus; +import static gregtech.api.enums.Mods.NEICustomDiagrams; +import static gregtech.api.enums.Mods.Railcraft; +import static gregtech.api.util.GT_RecipeConstants.ADDITIVE_AMOUNT; +import static gregtech.api.util.GT_RecipeConstants.FUEL_VALUE; +import static gregtech.api.util.GT_RecipeMapUtil.FIRST_FLUIDSTACK_INPUT; +import static gregtech.api.util.GT_RecipeMapUtil.FIRST_FLUIDSTACK_OUTPUT; +import static gregtech.api.util.GT_RecipeMapUtil.FIRST_FLUID_OUTPUT; +import static gregtech.api.util.GT_RecipeMapUtil.FIRST_ITEM_INPUT; +import static gregtech.api.util.GT_RecipeMapUtil.FIRST_ITEM_OR_FLUID_INPUT; +import static gregtech.api.util.GT_RecipeMapUtil.FIRST_ITEM_OR_FLUID_OUTPUT; +import static gregtech.api.util.GT_RecipeMapUtil.FIRST_ITEM_OUTPUT; +import static gregtech.api.util.GT_RecipeMapUtil.GT_RecipeTemplate; +import static gregtech.api.util.GT_RecipeMapUtil.asTemplate; +import static gregtech.api.util.GT_RecipeMapUtil.buildOrEmpty; +import static gregtech.api.util.GT_Utility.clamp; +import static gregtech.api.util.GT_Utility.copyAmount; +import static gregtech.api.util.GT_Utility.getFluidForFilledItem; +import static gregtech.api.util.GT_Utility.isArrayEmptyOrNull; +import static gregtech.api.util.GT_Utility.isArrayOfLength; +import static gregtech.api.util.GT_Utility.multiplyStack; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Optional; + +import net.minecraft.init.Blocks; +import net.minecraft.item.ItemStack; +import net.minecraftforge.fluids.FluidStack; + +import org.apache.commons.lang3.ArrayUtils; + +import com.gtnewhorizons.modularui.api.drawable.UITexture; +import com.gtnewhorizons.modularui.common.widget.ProgressBar; + +import gregtech.api.enums.GT_Values; +import gregtech.api.enums.ItemList; +import gregtech.api.enums.Materials; +import gregtech.api.enums.OrePrefixes; +import gregtech.api.gui.modularui.GT_UITextures; +import gregtech.api.interfaces.IRecipeMap; +import gregtech.api.objects.ItemData; +import gregtech.api.recipe.maps.AssemblerBackend; +import gregtech.api.recipe.maps.AssemblyLineFrontend; +import gregtech.api.recipe.maps.DistillationTowerFrontend; +import gregtech.api.recipe.maps.FluidCannerBackend; +import gregtech.api.recipe.maps.FluidOnlyFrontend; +import gregtech.api.recipe.maps.FormingPressBackend; +import gregtech.api.recipe.maps.FuelBackend; +import gregtech.api.recipe.maps.FurnaceBackend; +import gregtech.api.recipe.maps.LargeBoilerFuelBackend; +import gregtech.api.recipe.maps.LargeBoilerFuelFrontend; +import gregtech.api.recipe.maps.LargeNEIFrontend; +import gregtech.api.recipe.maps.MicrowaveBackend; +import gregtech.api.recipe.maps.OilCrackerBackend; +import gregtech.api.recipe.maps.PrinterBackend; +import gregtech.api.recipe.maps.RecyclerBackend; +import gregtech.api.recipe.maps.ReplicatorBackend; +import gregtech.api.recipe.maps.SpaceProjectFrontend; +import gregtech.api.recipe.maps.TranscendentPlasmaMixerFrontend; +import gregtech.api.recipe.maps.UnpackagerBackend; +import gregtech.api.recipe.metadata.PCBFactoryTierKey; +import gregtech.api.util.GT_Config; +import gregtech.api.util.GT_ModHandler; +import gregtech.api.util.GT_OreDictUnificator; +import gregtech.api.util.GT_Recipe; +import gregtech.api.util.GT_RecipeConstants; +import gregtech.api.util.GT_RecipeMapUtil; +import gregtech.api.util.GT_Utility; +import gregtech.nei.formatter.FuelSpecialValueFormatter; +import gregtech.nei.formatter.FusionSpecialValueFormatter; +import gregtech.nei.formatter.HeatingCoilSpecialValueFormatter; +import gregtech.nei.formatter.SimpleSpecialValueFormatter; +import mods.railcraft.common.blocks.aesthetics.cube.EnumCube; +import mods.railcraft.common.items.RailcraftToolItems; + +@SuppressWarnings("SimplifyOptionalCallChains") +public final class RecipeMaps { + + public static final RecipeMap<RecipeMapBackend> oreWasherRecipes = RecipeMapBuilder.of("gt.recipe.orewasher") + .maxIO(1, 3, 1, 0) + .minInputs(1, 1) + .slotOverlays((index, isFluid, isOutput, isSpecial) -> { + if (isFluid) { + return null; + } + if (isOutput) { + return GT_UITextures.OVERLAY_SLOT_DUST; + } else { + return GT_UITextures.OVERLAY_SLOT_CRUSHED_ORE; + } + }) + .progressBar(GT_UITextures.PROGRESSBAR_BATH, ProgressBar.Direction.CIRCULAR_CW) + .recipeConfigFile("orewasher", FIRST_ITEM_INPUT) + .build(); + public static final RecipeMap<RecipeMapBackend> thermalCentrifugeRecipes = RecipeMapBuilder + .of("gt.recipe.thermalcentrifuge") + .maxIO(1, 3, 0, 0) + .minInputs(1, 0) + .amperage(2) + .slotOverlays((index, isFluid, isOutput, isSpecial) -> { + if (isFluid) { + return null; + } + if (isOutput) { + return GT_UITextures.OVERLAY_SLOT_DUST; + } else { + return GT_UITextures.OVERLAY_SLOT_CRUSHED_ORE; + } + }) + .recipeConfigFile("thermalcentrifuge", FIRST_ITEM_INPUT) + .build(); + public static final RecipeMap<RecipeMapBackend> compressorRecipes = RecipeMapBuilder.of("gt.recipe.compressor") + .maxIO(1, 1, 0, 0) + .minInputs(1, 0) + .slotOverlays( + (index, isFluid, isOutput, isSpecial) -> !isFluid && !isOutput ? GT_UITextures.OVERLAY_SLOT_COMPRESSOR + : null) + .progressBar(GT_UITextures.PROGRESSBAR_COMPRESS) + .slotOverlaysSteam( + (index, isFluid, isOutput, isSpecial) -> !isFluid && !isOutput ? GT_UITextures.OVERLAY_SLOT_COMPRESSOR_STEAM + : null) + .progressBarSteam(GT_UITextures.PROGRESSBAR_COMPRESS_STEAM) + // Avoid steam machine being used as handler icon + .neiHandlerInfo(builder -> builder.setDisplayStack(ItemList.Machine_LV_Compressor.get(1))) + .recipeConfigFile("compressor", FIRST_ITEM_INPUT) + .build(); + public static final RecipeMap<RecipeMapBackend> extractorRecipes = RecipeMapBuilder.of("gt.recipe.extractor") + .maxIO(1, 1, 0, 0) + .minInputs(1, 0) + .slotOverlays( + (index, isFluid, isOutput, isSpecial) -> !isFluid && !isOutput ? GT_UITextures.OVERLAY_SLOT_CENTRIFUGE + : null) + .progressBar(GT_UITextures.PROGRESSBAR_EXTRACT) + .slotOverlaysSteam( + (index, isFluid, isOutput, isSpecial) -> !isFluid && !isOutput ? GT_UITextures.OVERLAY_SLOT_CENTRIFUGE_STEAM + : null) + .progressBarSteam(GT_UITextures.PROGRESSBAR_EXTRACT_STEAM) + // Avoid steam machine being used as handler icon + .neiHandlerInfo(builder -> builder.setDisplayStack(ItemList.Machine_LV_Extractor.get(1))) + .recipeConfigFile("extractor", FIRST_ITEM_INPUT) + .build(); + public static final RecipeMap<RecyclerBackend> recyclerRecipes = RecipeMapBuilder + .of("ic.recipe.recycler", RecyclerBackend::new) + .maxIO(1, 1, 0, 0) + .minInputs(1, 0) + .slotOverlays( + (index, isFluid, isOutput, isSpecial) -> !isFluid && !isOutput ? GT_UITextures.OVERLAY_SLOT_RECYCLE : null) + .progressBar(GT_UITextures.PROGRESSBAR_RECYCLE, ProgressBar.Direction.CIRCULAR_CW) + .neiTransferRectId("ic2.recycler") + .disableRegisterNEI() + .build(); + public static final RecipeMap<FurnaceBackend> furnaceRecipes = RecipeMapBuilder + .of("mc.recipe.furnace", FurnaceBackend::new) + .maxIO(1, 1, 0, 0) + .minInputs(1, 9) + .slotOverlays( + (index, isFluid, isOutput, isSpecial) -> !isFluid && !isOutput ? GT_UITextures.OVERLAY_SLOT_FURNACE : null) + .slotOverlaysSteam( + (index, isFluid, isOutput, isSpecial) -> !isFluid && !isOutput ? GT_UITextures.OVERLAY_SLOT_FURNACE_STEAM + : null) + .progressBarSteam(GT_UITextures.PROGRESSBAR_ARROW_STEAM) + .neiTransferRectId("smelting") + .disableRegisterNEI() + .build(); + public static final RecipeMap<MicrowaveBackend> microwaveRecipes = RecipeMapBuilder + .of("gt.recipe.microwave", MicrowaveBackend::new) + .maxIO(1, 1, 0, 0) + .minInputs(1, 0) + .slotOverlays( + (index, isFluid, isOutput, isSpecial) -> !isFluid && !isOutput ? GT_UITextures.OVERLAY_SLOT_FURNACE : null) + .neiTransferRectId("smelting") + .disableRegisterNEI() + .build(); + public static final RecipeMap<RecipeMapBackend> scannerFakeRecipes = RecipeMapBuilder.of("gt.recipe.scanner") + .maxIO(1, 1, 1, 0) + .minInputs(1, 0) + .useSpecialSlot() + .slotOverlays((index, isFluid, isOutput, isSpecial) -> { + if (isSpecial) { + return GT_UITextures.OVERLAY_SLOT_DATA_ORB; + } + if (!isFluid && !isOutput) { + return GT_UITextures.OVERLAY_SLOT_MICROSCOPE; + } + return null; + }) + .build(); + public static final RecipeMap<RecipeMapBackend> rockBreakerFakeRecipes = RecipeMapBuilder + .of("gt.recipe.rockbreaker") + .maxIO(2, 1, 0, 0) + .slotOverlays((index, isFluid, isOutput, isSpecial) -> { + if (isOutput) { + return GT_UITextures.OVERLAY_SLOT_CRUSHED_ORE; + } else { + return GT_UITextures.OVERLAY_SLOT_DUST; + } + }) + .progressBar(GT_UITextures.PROGRESSBAR_MACERATE) + .build(); + public static final RecipeMap<ReplicatorBackend> replicatorRecipes = RecipeMapBuilder + .of("gt.recipe.replicator", ReplicatorBackend::new) + .maxIO(0, 1, 1, 1) + .minInputs(0, 1) + .useSpecialSlot() + .slotOverlays((index, isFluid, isOutput, isSpecial) -> { + if (isSpecial) { + return GT_UITextures.OVERLAY_SLOT_DATA_ORB; + } + if (isFluid && !isOutput) { + return GT_UITextures.OVERLAY_SLOT_UUM; + } + return null; + }) + .build(); + /** + * Use {@link GT_RecipeConstants#AssemblyLine} for recipe addition. + */ + public static final RecipeMap<RecipeMapBackend> assemblylineVisualRecipes = RecipeMapBuilder + .of("gt.recipe.fakeAssemblylineProcess") + .maxIO(16, 1, 4, 0) + .minInputs(1, 0) + .useSpecialSlot() + .slotOverlays((index, isFluid, isOutput, isSpecial) -> isSpecial ? GT_UITextures.OVERLAY_SLOT_DATA_ORB : null) + .disableOptimize() + .neiTransferRect(88, 8, 18, 72) + .neiTransferRect(124, 8, 18, 72) + .neiTransferRect(142, 26, 18, 18) + .frontend(AssemblyLineFrontend::new) + .build(); + /** + * Usually, but not always, you should use {@link GT_RecipeConstants#UniversalArcFurnace} instead. + */ + public static final RecipeMap<RecipeMapBackend> plasmaArcFurnaceRecipes = RecipeMapBuilder + .of("gt.recipe.plasmaarcfurnace") + .maxIO(1, 9, 1, 1) + .minInputs(1, 1) + .recipeConfigFile("arcfurnace", FIRST_ITEM_INPUT) + .build(); + /** + * Usually, but not always, you should use {@link GT_RecipeConstants#UniversalArcFurnace} instead. + */ + public static final RecipeMap<RecipeMapBackend> arcFurnaceRecipes = RecipeMapBuilder.of("gt.recipe.arcfurnace") + .maxIO(1, 9, 1, 0) + .minInputs(1, 1) + .amperage(3) + .recipeConfigFile("arcfurnace", FIRST_ITEM_INPUT) + .build(); + public static final RecipeMap<PrinterBackend> printerRecipes = RecipeMapBuilder + .of("gt.recipe.printer", PrinterBackend::new) + .maxIO(1, 1, 1, 0) + .minInputs(1, 1) + .useSpecialSlot() + .slotOverlays((index, isFluid, isOutput, isSpecial) -> { + if (isSpecial) { + return GT_UITextures.OVERLAY_SLOT_DATA_STICK; + } + if (isFluid) { + return null; + } + if (isOutput) { + return GT_UITextures.OVERLAY_SLOT_PAGE_PRINTED; + } + return GT_UITextures.OVERLAY_SLOT_PAGE_BLANK; + }) + .recipeConfigFile("printer", FIRST_ITEM_INPUT) + .build(); + public static final RecipeMap<RecipeMapBackend> sifterRecipes = RecipeMapBuilder.of("gt.recipe.sifter") + .maxIO(1, 9, 1, 1) + .progressBar(GT_UITextures.PROGRESSBAR_SIFT, ProgressBar.Direction.DOWN) + .recipeConfigFile("sifter", FIRST_ITEM_INPUT) + .build(); + public static final RecipeMap<FormingPressBackend> formingPressRecipes = RecipeMapBuilder + .of("gt.recipe.press", FormingPressBackend::new) + .maxIO(6, 1, 0, 0) + .minInputs(2, 0) + .slotOverlays((index, isFluid, isOutput, isSpecial) -> { + if (isOutput) { + return GT_UITextures.OVERLAY_SLOT_PRESS_3; + } + if (index == 0) { + return GT_UITextures.OVERLAY_SLOT_PRESS_1; + } + return GT_UITextures.OVERLAY_SLOT_PRESS_2; + }) + .progressBar(GT_UITextures.PROGRESSBAR_COMPRESS) + .recipeConfigFile("press", FIRST_ITEM_OUTPUT) + .build(); + public static final RecipeMap<RecipeMapBackend> laserEngraverRecipes = RecipeMapBuilder + .of("gt.recipe.laserengraver") + .maxIO(4, 4, 2, 2) + .slotOverlays( + (index, isFluid, isOutput, + isSpecial) -> !isFluid && !isOutput && index != 0 ? GT_UITextures.OVERLAY_SLOT_LENS : null) + .recipeConfigFile("laserengraving", FIRST_ITEM_OUTPUT) + .build(); + public static final RecipeMap<RecipeMapBackend> mixerRecipes = RecipeMapBuilder.of("gt.recipe.mixer") + .maxIO(9, 4, 1, 1) + .minInputs(1, 0) + .slotOverlays((index, isFluid, isOutput, isSpecial) -> !isFluid ? GT_UITextures.OVERLAY_SLOT_DUST : null) + .progressBar(GT_UITextures.PROGRESSBAR_MIXER, ProgressBar.Direction.CIRCULAR_CW) + .recipeConfigFile("mixer", FIRST_ITEM_OR_FLUID_OUTPUT) + .build(); + public static final RecipeMap<RecipeMapBackend> autoclaveRecipes = RecipeMapBuilder.of("gt.recipe.autoclave") + .maxIO(2, 4, 1, 1) + .minInputs(1, 1) + .slotOverlays((index, isFluid, isOutput, isSpecial) -> { + if (isFluid) { + return null; + } + if (isOutput) { + if (index == 0) { + return GT_UITextures.OVERLAY_SLOT_GEM; + } + return GT_UITextures.OVERLAY_SLOT_DUST; + } + return GT_UITextures.OVERLAY_SLOT_DUST; + }) + + .recipeConfigFile("autoclave", FIRST_ITEM_INPUT) + .build(); + public static final RecipeMap<RecipeMapBackend> electroMagneticSeparatorRecipes = RecipeMapBuilder + .of("gt.recipe.electromagneticseparator") + .maxIO(1, 3, 0, 0) + .minInputs(1, 0) + .slotOverlays( + (index, isFluid, isOutput, isSpecial) -> isOutput ? GT_UITextures.OVERLAY_SLOT_DUST + : GT_UITextures.OVERLAY_SLOT_CRUSHED_ORE) + .progressBar(GT_UITextures.PROGRESSBAR_MAGNET) + .recipeConfigFile("electromagneticseparator", FIRST_ITEM_INPUT) + .build(); + public static final RecipeMap<RecipeMapBackend> polarizerRecipes = RecipeMapBuilder.of("gt.recipe.polarizer") + .maxIO(1, 1, 0, 0) + .minInputs(1, 0) + .progressBar(GT_UITextures.PROGRESSBAR_MAGNET) + .recipeConfigFile("polarizer", FIRST_ITEM_INPUT) + .build(); + public static final RecipeMap<RecipeMapBackend> maceratorRecipes = RecipeMapBuilder.of("gt.recipe.macerator") + .maxIO(1, 4, 0, 0) + .minInputs(1, 0) + .slotOverlays( + (index, isFluid, isOutput, isSpecial) -> isOutput ? GT_UITextures.OVERLAY_SLOT_DUST + : GT_UITextures.OVERLAY_SLOT_CRUSHED_ORE) + .slotOverlaysSteam( + (index, isFluid, isOutput, isSpecial) -> isOutput ? GT_UITextures.OVERLAY_SLOT_DUST_STEAM + : GT_UITextures.OVERLAY_SLOT_CRUSHED_ORE_STEAM) + .progressBar(GT_UITextures.PROGRESSBAR_MACERATE) + .progressBarSteam(GT_UITextures.PROGRESSBAR_MACERATE_STEAM) + // Avoid steam machine being used as handler icon + .neiHandlerInfo(builder -> builder.setDisplayStack(ItemList.Machine_LV_Macerator.get(1))) + .recipeConfigFile("pulveriser", FIRST_ITEM_INPUT) + .build(); + public static final RecipeMap<RecipeMapBackend> chemicalBathRecipes = RecipeMapBuilder.of("gt.recipe.chemicalbath") + .maxIO(1, 3, 1, 1) + .minInputs(1, 1) + .progressBar(GT_UITextures.PROGRESSBAR_BATH, ProgressBar.Direction.CIRCULAR_CW) + .recipeConfigFile("chemicalbath", FIRST_ITEM_INPUT) + .build(); + public static final RecipeMap<FluidCannerBackend> fluidCannerRecipes = RecipeMapBuilder + .of("gt.recipe.fluidcanner", FluidCannerBackend::new) + .maxIO(1, 1, 1, 1) + .minInputs(1, 0) + .slotOverlays((index, isFluid, isOutput, isSpecial) -> !isFluid ? GT_UITextures.OVERLAY_SLOT_CANISTER : null) + .progressBar(GT_UITextures.PROGRESSBAR_CANNER) + .recipeConfigFile("canning", FIRST_ITEM_INPUT) + .build(); + public static final RecipeMap<RecipeMapBackend> brewingRecipes = RecipeMapBuilder.of("gt.recipe.brewer") + .maxIO(1, 0, 1, 1) + .minInputs(1, 1) + .slotOverlays( + (index, isFluid, isOutput, isSpecial) -> !isFluid && !isOutput ? GT_UITextures.OVERLAY_SLOT_CAULDRON : null) + .progressBar(GT_UITextures.PROGRESSBAR_ARROW_MULTIPLE) + .recipeConfigFile("brewing", FIRST_FLUIDSTACK_OUTPUT) + .build(); + public static final RecipeMap<RecipeMapBackend> fluidHeaterRecipes = RecipeMapBuilder.of("gt.recipe.fluidheater") + .maxIO(1, 0, 1, 1) + .slotOverlays((index, isFluid, isOutput, isSpecial) -> { + if (!isFluid) { + return null; + } + if (isOutput) { + return GT_UITextures.OVERLAY_SLOT_HEATER_2; + } + return GT_UITextures.OVERLAY_SLOT_HEATER_1; + }) + .progressBar(GT_UITextures.PROGRESSBAR_ARROW_MULTIPLE) + .recipeConfigFile("fluidheater", FIRST_FLUIDSTACK_OUTPUT) + .build(); + public static final RecipeMap<RecipeMapBackend> distilleryRecipes = RecipeMapBuilder.of("gt.recipe.distillery") + .maxIO(1, 1, 1, 1) + .minInputs(1, 1) + .slotOverlays((index, isFluid, isOutput, isSpecial) -> { + if (!isFluid) { + return null; + } + if (isOutput) { + return GT_UITextures.OVERLAY_SLOT_BEAKER_2; + } + return GT_UITextures.OVERLAY_SLOT_BEAKER_1; + }) + .progressBar(GT_UITextures.PROGRESSBAR_ARROW_MULTIPLE) + .recipeTransformer(r -> { + int aInput = r.mFluidInputs[0].amount, aOutput = r.mFluidOutputs[0].amount, aDuration = r.mDuration; + + // reduce the batch size if fluid amount is exceeding + int tScale = (Math.max(aInput, aOutput) + 999) / 1000; + if (tScale <= 0) tScale = 1; + if (tScale > 1) { + // trying to find whether there is a better factor + for (int i = tScale; i <= 5; i++) { + if (aInput % i == 0 && aDuration % i == 0) { + tScale = i; + break; + } + } + for (int i = tScale; i <= 5; i++) { + if (aInput % i == 0 && aDuration % i == 0 && aOutput % i == 0) { + tScale = i; + break; + } + } + aInput = (aInput + tScale - 1) / tScale; + aOutput = aOutput / tScale; + if (!isArrayEmptyOrNull(r.mOutputs)) { + ItemData tData = GT_OreDictUnificator.getItemData(r.mOutputs[0]); + if (tData != null && (tData.mPrefix == OrePrefixes.dust + || OrePrefixes.dust.mFamiliarPrefixes.contains(tData.mPrefix))) { + r.mOutputs[0] = GT_OreDictUnificator.getDust( + tData.mMaterial.mMaterial, + tData.mMaterial.mAmount * r.mOutputs[0].stackSize / tScale); + } else { + if (r.mOutputs[0].stackSize / tScale == 0) r.mOutputs[0] = GT_Values.NI; + else r.mOutputs[0] = copyAmount(r.mOutputs[0].stackSize / tScale, r.mOutputs[0]); + } + } + aDuration = (aDuration + tScale - 1) / tScale; + r.mFluidInputs[0] = copyAmount(aInput, r.mFluidInputs[0]); + r.mFluidOutputs[0] = copyAmount(aOutput, r.mFluidOutputs[0]); + r.mDuration = aDuration; + } + }) + .recipeConfigFile("distillery", FIRST_FLUIDSTACK_OUTPUT) + .build(); + public static final RecipeMap<RecipeMapBackend> fermentingRecipes = RecipeMapBuilder.of("gt.recipe.fermenter") + .maxIO(0, 0, 1, 1) + .minInputs(0, 1) + .progressBar(GT_UITextures.PROGRESSBAR_ARROW_MULTIPLE) + .recipeConfigFile("fermenting", FIRST_FLUIDSTACK_OUTPUT) + .build(); + public static final RecipeMap<RecipeMapBackend> fluidSolidifierRecipes = RecipeMapBuilder + .of("gt.recipe.fluidsolidifier") + .maxIO(1, 1, 1, 0) + .minInputs(1, 1) + .slotOverlays( + (index, isFluid, isOutput, isSpecial) -> !isFluid && !isOutput ? GT_UITextures.OVERLAY_SLOT_MOLD : null) + .recipeTransformer(r -> { + if (ArrayUtils.isNotEmpty(r.mFluidInputs)) { + if (Materials.PhasedGold.getMolten(1) + .isFluidEqual(r.mFluidInputs[0])) + r.mFluidInputs = new FluidStack[] { Materials.VibrantAlloy.getMolten(r.mFluidInputs[0].amount) }; + else if (Materials.PhasedIron.getMolten(1) + .isFluidEqual(r.mFluidInputs[0])) + r.mFluidInputs = new FluidStack[] { Materials.PulsatingIron.getMolten(r.mFluidInputs[0].amount) }; + } + }) + .recipeConfigFile("fluidsolidifier", FIRST_ITEM_OUTPUT) + .build(); + public static final RecipeMap<RecipeMapBackend> fluidExtractionRecipes = RecipeMapBuilder + .of("gt.recipe.fluidextractor") + .maxIO(1, 1, 0, 1) + .minInputs(1, 0) + .slotOverlays( + (index, isFluid, isOutput, isSpecial) -> !isFluid && !isOutput ? GT_UITextures.OVERLAY_SLOT_CENTRIFUGE + : null) + .progressBar(GT_UITextures.PROGRESSBAR_EXTRACT) + .recipeTransformer(r -> { + if (ArrayUtils.isNotEmpty(r.mFluidOutputs)) { + if (Materials.PhasedGold.getMolten(1) + .isFluidEqual(r.mFluidOutputs[0])) + r.mFluidOutputs = new FluidStack[] { Materials.VibrantAlloy.getMolten(r.mFluidOutputs[0].amount) }; + else if (Materials.PhasedIron.getMolten(1) + .isFluidEqual(r.mFluidOutputs[0])) + r.mFluidOutputs = new FluidStack[] { Materials.PulsatingIron.getMolten(r.mFluidOutputs[0].amount) }; + } + }) + .recipeConfigFile("fluidextractor", FIRST_ITEM_INPUT) + .build(); + public static final RecipeMap<RecipeMapBackend> packagerRecipes = RecipeMapBuilder.of("gt.recipe.packager") + .maxIO(2, 1, 0, 0) + .minInputs(2, 0) + .slotOverlays((index, isFluid, isOutput, isSpecial) -> { + if (isOutput) { + return GT_UITextures.OVERLAY_SLOT_BOXED; + } + if (index != 0) { + return GT_UITextures.OVERLAY_SLOT_BOX; + } + return null; + }) + .recipeConfigFile("boxing", FIRST_ITEM_OUTPUT) + .build(); + public static final RecipeMap<UnpackagerBackend> unpackagerRecipes = RecipeMapBuilder + .of("gt.recipe.unpackager", UnpackagerBackend::new) + .maxIO(1, 2, 0, 0) + .minInputs(1, 0) + .slotOverlays((index, isFluid, isOutput, isSpecial) -> !isOutput ? GT_UITextures.OVERLAY_SLOT_BOXED : null) + .recipeConfigFile("unboxing", FIRST_ITEM_OUTPUT) + .build(); + public static final RecipeMap<RecipeMapBackend> fusionRecipes = RecipeMapBuilder.of("gt.recipe.fusionreactor") + .maxIO(0, 0, 2, 1) + .minInputs(0, 2) + .disableOptimize() + .useCustomFilterForNEI() + .neiSpecialInfoFormatter(FusionSpecialValueFormatter.INSTANCE) + .neiRecipeComparator( + Comparator + .<GT_Recipe, Integer>comparing( + recipe -> FusionSpecialValueFormatter.getFusionTier(recipe.mSpecialValue, recipe.mEUt)) + .thenComparing(GT_Recipe::compareTo)) + .frontend(FluidOnlyFrontend::new) + .recipeConfigFile("fusion", FIRST_FLUID_OUTPUT) + .build(); + public static final RecipeMap<RecipeMapBackend> centrifugeRecipes = RecipeMapBuilder.of("gt.recipe.centrifuge") + .maxIO(2, 6, 1, 1) + .slotOverlays((index, isFluid, isOutput, isSpecial) -> { + if (isOutput) { + return null; + } + if (isFluid) { + return GT_UITextures.OVERLAY_SLOT_CENTRIFUGE_FLUID; + } else { + if (index == 0) { + return GT_UITextures.OVERLAY_SLOT_CENTRIFUGE; + } + return GT_UITextures.OVERLAY_SLOT_CANISTER; + } + }) + .progressBar(GT_UITextures.PROGRESSBAR_EXTRACT) + .recipeConfigFile("centrifuge", FIRST_ITEM_OR_FLUID_INPUT) + .build(); + public static final RecipeMap<RecipeMapBackend> electrolyzerRecipes = RecipeMapBuilder.of("gt.recipe.electrolyzer") + .maxIO(2, 6, 1, 1) + .slotOverlays((index, isFluid, isOutput, isSpecial) -> { + if (isOutput) { + return null; + } + if (isFluid) { + return GT_UITextures.OVERLAY_SLOT_CHARGER_FLUID; + } else { + if (index == 0) { + return GT_UITextures.OVERLAY_SLOT_CHARGER; + } + return GT_UITextures.OVERLAY_SLOT_CANISTER; + } + }) + .progressBar(GT_UITextures.PROGRESSBAR_EXTRACT) + .recipeConfigFile("electrolyzer", FIRST_ITEM_OR_FLUID_INPUT) + .build(); + /** + * Use {@link GT_RecipeConstants#COIL_HEAT} as heat level. + */ + public static final RecipeMap<RecipeMapBackend> blastFurnaceRecipes = RecipeMapBuilder.of("gt.recipe.blastfurnace") + .maxIO(6, 6, 1, 1) + .minInputs(1, 0) + .neiSpecialInfoFormatter(HeatingCoilSpecialValueFormatter.INSTANCE) + .recipeConfigFile("blastfurnace", FIRST_ITEM_INPUT) + .build(); + /** + * Use {@link GT_RecipeConstants#COIL_HEAT} as heat level. + */ + public static final RecipeMap<RecipeMapBackend> plasmaForgeRecipes = RecipeMapBuilder.of("gt.recipe.plasmaforge") + .maxIO(9, 9, 9, 9) + .disableOptimize() + .neiSpecialInfoFormatter(HeatingCoilSpecialValueFormatter.INSTANCE) + .neiHandlerInfo( + builder -> builder.setDisplayStack(ItemList.Machine_Multi_PlasmaForge.get(1)) + .setMaxRecipesPerPage(1)) + .frontend(LargeNEIFrontend::new) + .build(); + public static final RecipeMap<RecipeMapBackend> transcendentPlasmaMixerRecipes = RecipeMapBuilder + .of("gt.recipe.transcendentplasmamixerrecipes") + .maxIO(1, 0, 20, 1) + .progressBarPos(86, 44) + .logoPos(87, 99) + .neiRecipeBackgroundSize(170, 118) + .neiHandlerInfo( + builder -> builder.setDisplayStack(ItemList.Machine_Multi_TranscendentPlasmaMixer.get(1)) + .setMaxRecipesPerPage(1)) + .frontend(TranscendentPlasmaMixerFrontend::new) + .disableOptimize() + .build(); + public static final RecipeMap<RecipeMapBackend> spaceProjectFakeRecipes = RecipeMapBuilder + .of("gt.recipe.fakespaceprojects") + .maxIO(12, 0, 4, 0) + .neiSpecialInfoFormatter(new SimpleSpecialValueFormatter("GT5U.nei.stages")) + .neiRecipeBackgroundOffset(3, 23) + .logo(UITexture.fullImage(GTNHIntergalactic.ID, "gui/picture/space_elevator_logo.png")) + .logoSize(18, 18) + .logoPos(152, 83) + .neiTransferRect(70, 28, 18, 72) + .neiTransferRect(106, 28, 18, 72) + .frontend(SpaceProjectFrontend::new) + .disableRenderRealStackSizes() + .disableOptimize() + .build(); + /** + * Uses {@link GT_RecipeConstants#ADDITIVE_AMOUNT} for coal/charcoal amount. + */ + public static final RecipeMap<RecipeMapBackend> primitiveBlastRecipes = RecipeMapBuilder + .of("gt.recipe.primitiveblastfurnace") + .maxIO(3, 3, 0, 0) + .minInputs(1, 0) + .recipeEmitter(builder -> { + Optional<GT_Recipe> rr = builder.eut(0) + .validateInputCount(1, 2) + .validateOutputCount(1, 2) + .validateNoInputFluid() + .validateNoOutputFluid() + .noOptimize() + .build(); + if (!rr.isPresent()) return Collections.emptyList(); + ItemStack aInput1 = builder.getItemInputBasic(0); + ItemStack aInput2 = builder.getItemInputBasic(1); + ItemStack aOutput1 = builder.getItemOutput(0); + ItemStack aOutput2 = builder.getItemOutput(1); + if ((aInput1 == null && aInput2 == null) || (aOutput1 == null && aOutput2 == null)) + return Collections.emptyList(); + int aCoalAmount = builder.getMetadataOrDefault(ADDITIVE_AMOUNT, 0); + if (aCoalAmount <= 0) return Collections.emptyList(); + GT_RecipeTemplate coll = asTemplate(rr.get()); + for (Materials coal : new Materials[] { Materials.Coal, Materials.Charcoal }) { + coll.derive() + .setInputs(aInput1, aInput2, coal.getGems(aCoalAmount)) + .setOutputs(aOutput1, aOutput2, Materials.DarkAsh.getDustTiny(aCoalAmount)); + coll.derive() + .setInputs(aInput1, aInput2, coal.getDust(aCoalAmount)) + .setOutputs(aOutput1, aOutput2, Materials.DarkAsh.getDustTiny(aCoalAmount)); + } + int aDuration = builder.getDuration(); + if (Railcraft.isModLoaded()) { + coll.derive() + .setInputs(aInput1, aInput2, RailcraftToolItems.getCoalCoke(aCoalAmount / 2)) + .setOutputs(aOutput1, aOutput2, Materials.Ash.getDustTiny(aCoalAmount / 2)) + .setDuration(aDuration * 2 / 3); + } + if (GTPlusPlus.isModLoaded()) { + ItemStack cactusCoke = GT_ModHandler.getModItem(GTPlusPlus.ID, "itemCactusCoke", aCoalAmount * 2L); + ItemStack sugarCoke = GT_ModHandler.getModItem(GTPlusPlus.ID, "itemSugarCoke", aCoalAmount * 2L); + coll.derive() + .setInputs(aInput1, aInput2, cactusCoke) + .setOutputs(aOutput1, aOutput2, Materials.Ash.getDustTiny(aCoalAmount * 2)) + .setDuration(aDuration * 2 / 3); + coll.derive() + .setInputs(aInput1, aInput2, sugarCoke) + .setOutputs(aOutput1, aOutput2, Materials.Ash.getDustTiny(aCoalAmount * 2)) + .setDuration(aDuration * 2 / 3); + } + if ((aInput1 == null || aInput1.stackSize <= 6) && (aInput2 == null || aInput2.stackSize <= 6) + && (aOutput1 == null || aOutput1.stackSize <= 6) + && (aOutput2 == null || aOutput2.stackSize <= 6)) { + // we don't use GT_Utility.mul() here. It does not have the truncating we need here. + aInput1 = multiplyStack(10, aInput1); + aInput2 = multiplyStack(10, aInput2); + aOutput1 = multiplyStack(10, aOutput1); + aOutput2 = multiplyStack(10, aOutput2); + for (Materials coal : new Materials[] { Materials.Coal, Materials.Charcoal }) { + coll.derive() + .setInputs(aInput1, aInput2, coal.getBlocks(aCoalAmount)) + .setOutputs(aOutput1, aOutput2, Materials.DarkAsh.getDust(aCoalAmount)) + .setDuration(aDuration * 10); + coll.derive() + .setInputs(aInput1, aInput2, coal.getBlocks(aCoalAmount)) + .setOutputs(aOutput1, aOutput2, Materials.DarkAsh.getDust(aCoalAmount)) + .setDuration(aDuration * 10); + } + if (Railcraft.isModLoaded()) { + coll.derive() + .setInputs(aInput1, aInput2, EnumCube.COKE_BLOCK.getItem(aCoalAmount / 2)) + .setOutputs(aOutput1, aOutput2, Materials.Ash.getDust(aCoalAmount / 2)) + .setDuration(aDuration * 20 / 3); + } + } + return coll.getAll(); + }) + .recipeConfigFile("primitiveblastfurnace", FIRST_ITEM_INPUT) + .build(); + /** + * Uses {@link GT_RecipeConstants#ADDITIVE_AMOUNT} for TNT/ITNT/... amount. Value is truncated to [0, 64] + */ + public static final RecipeMap<RecipeMapBackend> implosionRecipes = RecipeMapBuilder + .of("gt.recipe.implosioncompressor") + .maxIO(2, 2, 0, 0) + .minInputs(2, 0) + .slotOverlays((index, isFluid, isOutput, isSpecial) -> { + if (!isFluid && !isOutput) { + if (index == 0) { + return GT_UITextures.OVERLAY_SLOT_IMPLOSION; + } + return GT_UITextures.OVERLAY_SLOT_EXPLOSIVE; + } + return null; + }) + .progressBar(GT_UITextures.PROGRESSBAR_COMPRESS) + .disableOptimize() + .recipeEmitter(b -> { + switch (b.getItemInputsBasic().length) { + case 0: + return Collections.emptyList(); + case 1: + break; + default: + return b.build() + .map(Collections::singletonList) + .orElse(Collections.emptyList()); + } + Optional<GT_Recipe> t = b.noOptimize() + .duration(20) + .eut(30) + .validateInputCount(1, 1) + .validateOutputCount(1, 2) + .build(); + if (!t.isPresent()) return Collections.emptyList(); + ItemStack input = b.getItemInputBasic(0); + GT_RecipeTemplate coll = asTemplate(t.get()); + int tExplosives = Math.min(b.getMetadataOrDefault(ADDITIVE_AMOUNT, 0), 64); + int tGunpowder = tExplosives << 1; // Worst + int tDynamite = Math.max(1, tExplosives >> 1); // good + @SuppressWarnings("UnnecessaryLocalVariable") + int tTNT = tExplosives; // Slightly better + int tITNT = Math.max(1, tExplosives >> 2); // the best + if (tGunpowder < 65) coll.derive() + .setInputs(input, ItemList.Block_Powderbarrel.get(tGunpowder)); + if (tDynamite < 17) coll.derive() + .setInputs(input, GT_ModHandler.getIC2Item("dynamite", tDynamite, null)); + coll.derive() + .setInputs(input, new ItemStack(Blocks.tnt, tTNT)); + coll.derive() + .setInputs(input, GT_ModHandler.getIC2Item("industrialTnt", tITNT, null)); + return coll.getAll(); + }) + .recipeConfigFile("implosion", FIRST_ITEM_INPUT) + .build(); + public static final RecipeMap<RecipeMapBackend> vacuumFreezerRecipes = RecipeMapBuilder + .of("gt.recipe.vacuumfreezer") + .maxIO(1, 1, 2, 1) + .recipeEmitter(b -> { + b.noOptimize(); + FluidStack in, out; + if (isArrayOfLength(b.getItemInputsBasic(), 1) && isArrayOfLength(b.getItemOutputs(), 1) + && isArrayEmptyOrNull(b.getFluidInputs()) + && isArrayEmptyOrNull(b.getFluidOutputs()) + && (in = getFluidForFilledItem(b.getItemInputBasic(0), true)) != null + && (out = getFluidForFilledItem(b.getItemOutput(0), true)) != null) { + Collection<GT_Recipe> ret = new ArrayList<>(); + b.build() + .ifPresent(ret::add); + b.itemInputs() + .itemOutputs() + .fluidInputs(in) + .fluidOutputs(out) + .build() + .ifPresent(ret::add); + return ret; + } + return buildOrEmpty(b); + }) + .recipeConfigFile("vacuumfreezer", FIRST_ITEM_INPUT) + .build(); + /** + * Using {@code .addTo(chemicalReactorRecipes)} will cause the recipe to be added to single block recipe map ONLY! + * Use {@link GT_RecipeConstants#UniversalChemical} to add to both. + */ + public static final RecipeMap<RecipeMapBackend> chemicalReactorRecipes = RecipeMapBuilder + .of("gt.recipe.chemicalreactor") + .maxIO(2, 2, 1, 1) + .minInputs(1, 0) + .slotOverlays((index, isFluid, isOutput, isSpecial) -> { + if (isFluid) { + if (isOutput) { + return GT_UITextures.OVERLAY_SLOT_VIAL_2; + } + return GT_UITextures.OVERLAY_SLOT_MOLECULAR_3; + } else { + if (isOutput) { + return GT_UITextures.OVERLAY_SLOT_VIAL_1; + } + if (index == 0) { + return GT_UITextures.OVERLAY_SLOT_MOLECULAR_1; + } + return GT_UITextures.OVERLAY_SLOT_MOLECULAR_2; + } + }) + .progressBar(GT_UITextures.PROGRESSBAR_ARROW_MULTIPLE) + .disableOptimize() + .recipeConfigFile("chemicalreactor", FIRST_ITEM_OR_FLUID_OUTPUT) + .build(); + /** + * Using {@code .addTo(multiblockChemicalReactorRecipes)} will cause the recipe to be added to + * multiblock recipe map ONLY! Use {@link GT_RecipeConstants#UniversalChemical} to add to both. + */ + public static final RecipeMap<RecipeMapBackend> multiblockChemicalReactorRecipes = RecipeMapBuilder + .of("gt.recipe.largechemicalreactor") + .maxIO(6, 6, 6, 6) + .progressBar(GT_UITextures.PROGRESSBAR_ARROW_MULTIPLE) + .disableOptimize() + .frontend(LargeNEIFrontend::new) + .build(); + public static final RecipeMap<RecipeMapBackend> distillationTowerRecipes = RecipeMapBuilder + .of("gt.recipe.distillationtower") + .maxIO(2, 1, 1, 11) + .slotOverlays((index, isFluid, isOutput, isSpecial) -> { + if (!isOutput) { + return null; + } + if (isFluid) { + return GT_UITextures.OVERLAY_SLOTS_NUMBER[index + 1]; + } else { + return GT_UITextures.OVERLAY_SLOTS_NUMBER[0]; + } + }) + .progressBar(GT_UITextures.PROGRESSBAR_ARROW_MULTIPLE) + .logoPos(80, 62) + .frontend(DistillationTowerFrontend::new) + .disableOptimize() + .recipeConfigFile("distillation", FIRST_FLUIDSTACK_INPUT) + .build(); + public static final RecipeMap<OilCrackerBackend> crackingRecipes = RecipeMapBuilder + .of("gt.recipe.craker", OilCrackerBackend::new) + .maxIO(1, 1, 2, 1) + .minInputs(1, 2) + .progressBar(GT_UITextures.PROGRESSBAR_ARROW_MULTIPLE) + .recipeConfigFile("cracking", FIRST_FLUIDSTACK_INPUT) + .build(); + public static final RecipeMap<RecipeMapBackend> pyrolyseRecipes = RecipeMapBuilder.of("gt.recipe.pyro") + .maxIO(2, 1, 1, 1) + .minInputs(1, 0) + .disableOptimize() + .recipeConfigFile("pyrolyse", FIRST_ITEM_INPUT) + .build(); + public static final RecipeMap<RecipeMapBackend> wiremillRecipes = RecipeMapBuilder.of("gt.recipe.wiremill") + .maxIO(2, 1, 0, 0) + .minInputs(1, 0) + .slotOverlays( + (index, isFluid, isOutput, isSpecial) -> !isFluid && !isOutput ? GT_UITextures.OVERLAY_SLOT_WIREMILL : null) + .progressBar(GT_UITextures.PROGRESSBAR_WIREMILL) + .recipeConfigFile("wiremill", FIRST_ITEM_INPUT) + .build(); + public static final RecipeMap<RecipeMapBackend> benderRecipes = RecipeMapBuilder.of("gt.recipe.metalbender") + .maxIO(2, 1, 0, 0) + .minInputs(2, 0) + .slotOverlays( + (index, isFluid, isOutput, isSpecial) -> !isFluid && !isOutput ? GT_UITextures.OVERLAY_SLOT_BENDER : null) + .progressBar(GT_UITextures.PROGRESSBAR_BENDING) + .recipeConfigFile("bender", FIRST_ITEM_INPUT) + .build(); + public static final RecipeMap<RecipeMapBackend> alloySmelterRecipes = RecipeMapBuilder.of("gt.recipe.alloysmelter") + .maxIO(2, 1, 0, 0) + .minInputs(2, 0) + .slotOverlays( + (index, isFluid, isOutput, isSpecial) -> !isFluid && !isOutput ? GT_UITextures.OVERLAY_SLOT_FURNACE : null) + .slotOverlaysSteam((index, isFluid, isOutput, isSpecial) -> GT_UITextures.OVERLAY_SLOT_FURNACE_STEAM) + .progressBarSteam(GT_UITextures.PROGRESSBAR_ARROW_STEAM) + .recipeEmitter(b -> { + if (Materials.Graphite.contains(b.getItemInputBasic(0))) return Collections.emptyList(); + if (GT_Utility.isArrayOfLength(b.getItemInputsBasic(), 1)) { + ItemStack aInput1 = b.getItemInputBasic(0); + if (((OrePrefixes.ingot.contains(aInput1)) || (OrePrefixes.dust.contains(aInput1)) + || (OrePrefixes.gem.contains(aInput1)))) return Collections.emptyList(); + } + return buildOrEmpty( + b.validateNoInputFluid() + .validateNoOutputFluid() + .validateInputCount(1, 2) + .validateOutputCount(1, 1)); + }) + // Avoid steam machine being used as handler icon + .neiHandlerInfo(builder -> builder.setDisplayStack(ItemList.Machine_LV_AlloySmelter.get(1))) + .recipeConfigFile( + "alloysmelting", + r -> GT_Config.getStackConfigName(GT_Utility.isArrayOfLength(r.mInputs, 1) ? r.mInputs[0] : r.mOutputs[0])) + .build(); + public static final RecipeMap<AssemblerBackend> assemblerRecipes = RecipeMapBuilder + .of("gt.recipe.assembler", AssemblerBackend::new) + .maxIO(9, 1, 1, 0) + .minInputs(1, 0) + .slotOverlays( + (index, isFluid, isOutput, isSpecial) -> !isFluid && !isOutput ? GT_UITextures.OVERLAY_SLOT_CIRCUIT : null) + .progressBar(GT_UITextures.PROGRESSBAR_ASSEMBLE) + .disableOptimize() + .recipeConfigFile("assembling", FIRST_ITEM_OUTPUT) + .build(); + public static final RecipeMap<RecipeMapBackend> circuitAssemblerRecipes = RecipeMapBuilder + .of("gt.recipe.circuitassembler") + .maxIO(6, 1, 1, 0) + .minInputs(1, 0) + .slotOverlays( + (index, isFluid, isOutput, isSpecial) -> !isFluid && !isOutput ? GT_UITextures.OVERLAY_SLOT_CIRCUIT : null) + .progressBar(GT_UITextures.PROGRESSBAR_CIRCUIT_ASSEMBLER) + .unificateOutputNEI(!NEICustomDiagrams.isModLoaded()) + .recipeConfigFile("circuitassembler", FIRST_ITEM_OUTPUT) + .build(); + public static final RecipeMap<RecipeMapBackend> cannerRecipes = RecipeMapBuilder.of("gt.recipe.canner") + .maxIO(2, 2, 0, 0) + .minInputs(1, 0) + .slotOverlays((index, isFluid, isOutput, isSpecial) -> { + if (isOutput) { + return null; + } + if (index == 0) { + return GT_UITextures.OVERLAY_SLOT_CANNER; + } + return GT_UITextures.OVERLAY_SLOT_CANISTER; + }) + .progressBar(GT_UITextures.PROGRESSBAR_CANNER) + .recipeConfigFile("canning", FIRST_ITEM_INPUT) + .build(); + public static final RecipeMap<RecipeMapBackend> latheRecipes = RecipeMapBuilder.of("gt.recipe.lathe") + .maxIO(1, 2, 0, 0) + .minInputs(1, 0) + .slotOverlays((index, isFluid, isOutput, isSpecial) -> { + if (isOutput) { + if (index == 0) { + return GT_UITextures.OVERLAY_SLOT_ROD_2; + } + return GT_UITextures.OVERLAY_SLOT_DUST; + } + return GT_UITextures.OVERLAY_SLOT_ROD_1; + }) + .progressBar(GT_UITextures.PROGRESSBAR_LATHE) + .addSpecialTexture(98, 24, 5, 18, GT_UITextures.PROGRESSBAR_LATHE_BASE) + .recipeConfigFile("lathe", FIRST_ITEM_INPUT) + .build(); + public static final RecipeMap<RecipeMapBackend> cutterRecipes = RecipeMapBuilder.of("gt.recipe.cuttingsaw") + .maxIO(2, 4, 1, 0) + .minInputs(1, 1) + .slotOverlays((index, isFluid, isOutput, isSpecial) -> { + if (isFluid) { + return null; + } + if (isOutput) { + if (index == 0) { + return GT_UITextures.OVERLAY_SLOT_CUTTER_SLICED; + } + return GT_UITextures.OVERLAY_SLOT_DUST; + } + return GT_UITextures.OVERLAY_SLOT_BOX; + }) + .progressBar(GT_UITextures.PROGRESSBAR_CUT) + .recipeEmitter(b -> { + b.validateInputCount(1, 2) + .validateOutputCount(1, 4) + .validateNoOutputFluid(); + if ((b.getFluidInputs() != null && b.getFluidInputs().length > 0) || !b.isValid()) + return buildOrEmpty(b.validateInputFluidCount(1, 1)); + int aDuration = b.getDuration(), aEUt = b.getEUt(); + Collection<GT_Recipe> ret = new ArrayList<>(); + b.copy() + .fluidInputs(Materials.Water.getFluid(clamp(aDuration * aEUt / 320, 4, 1000))) + .duration(aDuration * 2) + .build() + .ifPresent(ret::add); + b.copy() + .fluidInputs(GT_ModHandler.getDistilledWater(clamp(aDuration * aEUt / 426, 3, 750))) + .duration(aDuration * 2) + .build() + .ifPresent(ret::add); + b.fluidInputs(Materials.Lubricant.getFluid(clamp(aDuration * aEUt / 1280, 1, 250))) + .duration(aDuration) + .build() + .ifPresent(ret::add); + return ret; + }) + .recipeConfigFile("cutting", FIRST_ITEM_INPUT) + .build(); + public static final RecipeMap<RecipeMapBackend> slicerRecipes = RecipeMapBuilder.of("gt.recipe.slicer") + .maxIO(2, 1, 0, 0) + .minInputs(2, 0) + .slotOverlays((index, isFluid, isOutput, isSpecial) -> { + if (isOutput) { + return GT_UITextures.OVERLAY_SLOT_SLICER_SLICED; + } + if (index == 0) { + return GT_UITextures.OVERLAY_SLOT_SQUARE; + } + return GT_UITextures.OVERLAY_SLOT_SLICE_SHAPE; + }) + .progressBar(GT_UITextures.PROGRESSBAR_SLICE) + .recipeConfigFile("slicer", FIRST_ITEM_OUTPUT) + .build(); + public static final RecipeMap<RecipeMapBackend> extruderRecipes = RecipeMapBuilder.of("gt.recipe.extruder") + .maxIO(2, 1, 0, 0) + .minInputs(2, 0) + .slotOverlays( + (index, isFluid, isOutput, + isSpecial) -> !isFluid && !isOutput && index != 0 ? GT_UITextures.OVERLAY_SLOT_EXTRUDER_SHAPE : null) + .progressBar(GT_UITextures.PROGRESSBAR_EXTRUDE) + .recipeConfigFile("extruder", FIRST_ITEM_OUTPUT) + .build(); + public static final RecipeMap<RecipeMapBackend> hammerRecipes = RecipeMapBuilder.of("gt.recipe.hammer") + .maxIO(2, 2, 2, 2) + .minInputs(1, 0) + .slotOverlays( + (index, isFluid, isOutput, isSpecial) -> !isFluid && !isOutput ? GT_UITextures.OVERLAY_SLOT_HAMMER : null) + .progressBar(GT_UITextures.PROGRESSBAR_HAMMER, ProgressBar.Direction.DOWN) + .addSpecialTexture(78, 42, 20, 6, GT_UITextures.PROGRESSBAR_HAMMER_BASE) + .slotOverlaysSteam( + (index, isFluid, isOutput, isSpecial) -> !isOutput ? GT_UITextures.OVERLAY_SLOT_HAMMER_STEAM : null) + .progressBarSteam(GT_UITextures.PROGRESSBAR_HAMMER_STEAM) + .addSpecialTextureSteam(78, 42, 20, 6, GT_UITextures.PROGRESSBAR_HAMMER_BASE_STEAM) + // Avoid steam machine being used as handler icon + .neiHandlerInfo(builder -> builder.setDisplayStack(ItemList.Machine_LV_Hammer.get(1))) + .recipeConfigFile("forgehammer", FIRST_ITEM_OUTPUT) + .build(); + public static final RecipeMap<RecipeMapBackend> amplifierRecipes = RecipeMapBuilder.of("gt.recipe.uuamplifier") + .maxIO(1, 0, 0, 1) + .minInputs(1, 0) + .slotOverlays((index, isFluid, isOutput, isSpecial) -> { + if (isFluid) { + return GT_UITextures.OVERLAY_SLOT_UUA; + } + if (!isOutput) { + return GT_UITextures.OVERLAY_SLOT_CENTRIFUGE; + } + return null; + }) + .progressBar(GT_UITextures.PROGRESSBAR_EXTRACT) + .recipeConfigFile("amplifier", FIRST_ITEM_INPUT) + .build(); + public static final RecipeMap<RecipeMapBackend> massFabFakeRecipes = RecipeMapBuilder.of("gt.recipe.massfab") + .maxIO(1, 0, 1, 1) + .minInputs(1, 0) + .amperage(8) + .slotOverlays((index, isFluid, isOutput, isSpecial) -> { + if (!isFluid) { + return null; + } + if (isOutput) { + return GT_UITextures.OVERLAY_SLOT_UUM; + } + return GT_UITextures.OVERLAY_SLOT_UUA; + }) + .build(); + public static final RecipeMap<FuelBackend> dieselFuels = RecipeMapBuilder + .of("gt.recipe.dieselgeneratorfuel", FuelBackend::new) + .maxIO(1, 1, 0, 0) + .neiSpecialInfoFormatter(FuelSpecialValueFormatter.INSTANCE) + .build(); + public static final RecipeMap<FuelBackend> extremeDieselFuels = RecipeMapBuilder + .of("gt.recipe.extremedieselgeneratorfuel", FuelBackend::new) + .maxIO(1, 1, 0, 0) + .neiSpecialInfoFormatter(FuelSpecialValueFormatter.INSTANCE) + .build(); + public static final RecipeMap<FuelBackend> gasTurbineFuels = RecipeMapBuilder + .of("gt.recipe.gasturbinefuel", FuelBackend::new) + .maxIO(1, 1, 0, 0) + .neiSpecialInfoFormatter(FuelSpecialValueFormatter.INSTANCE) + .build(); + public static final RecipeMap<FuelBackend> hotFuels = RecipeMapBuilder + .of("gt.recipe.thermalgeneratorfuel", FuelBackend::new) + .maxIO(1, 4, 0, 0) + .neiSpecialInfoFormatter(FuelSpecialValueFormatter.INSTANCE) + .build(); + public static final RecipeMap<FuelBackend> denseLiquidFuels = RecipeMapBuilder + .of("gt.recipe.semifluidboilerfuels", FuelBackend::new) + .maxIO(1, 1, 0, 0) + .disableRegisterNEI() + .build(); + public static final RecipeMap<FuelBackend> plasmaFuels = RecipeMapBuilder + .of("gt.recipe.plasmageneratorfuels", FuelBackend::new) + .maxIO(1, 1, 0, 0) + .neiSpecialInfoFormatter(FuelSpecialValueFormatter.INSTANCE) + .build(); + public static final RecipeMap<FuelBackend> magicFuels = RecipeMapBuilder + .of("gt.recipe.magicfuels", FuelBackend::new) + .maxIO(1, 1, 0, 0) + .neiSpecialInfoFormatter(FuelSpecialValueFormatter.INSTANCE) + .build(); + public static final RecipeMap<FuelBackend> smallNaquadahReactorFuels = RecipeMapBuilder + .of("gt.recipe.smallnaquadahreactor", FuelBackend::new) + .maxIO(1, 1, 0, 0) + .neiSpecialInfoFormatter(FuelSpecialValueFormatter.INSTANCE) + .build(); + public static final RecipeMap<FuelBackend> largeNaquadahReactorFuels = RecipeMapBuilder + .of("gt.recipe.largenaquadahreactor", FuelBackend::new) + .maxIO(1, 1, 0, 0) + .neiSpecialInfoFormatter(FuelSpecialValueFormatter.INSTANCE) + .build(); + public static final RecipeMap<FuelBackend> hugeNaquadahReactorFuels = RecipeMapBuilder + .of("gt.recipe.fluidnaquadahreactor", FuelBackend::new) + .maxIO(1, 1, 0, 0) + .neiSpecialInfoFormatter(FuelSpecialValueFormatter.INSTANCE) + .build(); + public static final RecipeMap<FuelBackend> extremeNaquadahReactorFuels = RecipeMapBuilder + .of("gt.recipe.hugenaquadahreactor", FuelBackend::new) + .maxIO(1, 1, 0, 0) + .neiSpecialInfoFormatter(FuelSpecialValueFormatter.INSTANCE) + .build(); + public static final RecipeMap<FuelBackend> ultraHugeNaquadahReactorFuels = RecipeMapBuilder + .of("gt.recipe.extrahugenaquadahreactor", FuelBackend::new) + .maxIO(1, 1, 0, 0) + .neiSpecialInfoFormatter(FuelSpecialValueFormatter.INSTANCE) + .build(); + public static final RecipeMap<FuelBackend> fluidNaquadahReactorFuels = RecipeMapBuilder + .of("gt.recipe.fluidfuelnaquadahreactor", FuelBackend::new) + .maxIO(1, 1, 0, 0) + .neiSpecialInfoFormatter(FuelSpecialValueFormatter.INSTANCE) + .build(); + public static final RecipeMap<RecipeMapBackend> electrolyzerNonCellRecipes = RecipeMapBuilder + .of("gt.recipe.largeelectrolyzer") + .maxIO(1, 6, 1, 6) + .disableRegisterNEI() + .recipeEmitter(GT_RecipeMapUtil::buildRecipeForMultiblock) + .build(); + public static final RecipeMap<RecipeMapBackend> centrifugeNonCellRecipes = RecipeMapBuilder + .of("gt.recipe.largecentrifuge") + .maxIO(2, 6, 1, 6) + .disableOptimize() + .disableRegisterNEI() + .recipeEmitter(GT_RecipeMapUtil::buildRecipeForMultiblock) + .build(); + public static final RecipeMap<RecipeMapBackend> mixerNonCellRecipes = RecipeMapBuilder.of("gt.recipe.largemixer") + .maxIO(9, 4, 6, 4) + .disableOptimize() + .disableRegisterNEI() + .recipeEmitter(GT_RecipeMapUtil::buildRecipeForMultiblockNoCircuit) + .build(); + public static final RecipeMap<LargeBoilerFuelBackend> largeBoilerFakeFuels = RecipeMapBuilder + .of("gt.recipe.largeboilerfakefuels", LargeBoilerFuelBackend::new) + .maxIO(1, 1, 0, 0) + .minInputs(1, 0) + .disableOptimize() + .frontend(LargeBoilerFuelFrontend::new) + .build(); + public static final RecipeMap<RecipeMapBackend> nanoForgeRecipes = RecipeMapBuilder.of("gt.recipe.nanoforge") + .maxIO(6, 2, 3, 0) + .minInputs(2, 1) + .slotOverlays( + (index, isFluid, isOutput, + isSpecial) -> !isFluid && !isOutput && index == 0 ? GT_UITextures.OVERLAY_SLOT_LENS : null) + .progressBar(GT_UITextures.PROGRESSBAR_ASSEMBLE) + .disableOptimize() + .neiSpecialInfoFormatter(new SimpleSpecialValueFormatter("GT5U.nei.tier")) + .build(); + public static final RecipeMap<RecipeMapBackend> pcbFactoryRecipes = RecipeMapBuilder.of("gt.recipe.pcbfactory") + .maxIO(6, 9, 3, 0) + .minInputs(3, 1) + .progressBar(GT_UITextures.PROGRESSBAR_ASSEMBLE) + .disableOptimize() + .neiRecipeComparator( + Comparator + .<GT_Recipe, Integer>comparing(recipe -> recipe.getMetadataOrDefault(PCBFactoryTierKey.INSTANCE, 1)) + .thenComparing(GT_Recipe::compareTo)) + .build(); + public static final RecipeMap<RecipeMapBackend> ic2NuclearFakeRecipes = RecipeMapBuilder.of("gt.recipe.ic2nuke") + .maxIO(1, 1, 0, 0) + .minInputs(1, 0) + .disableOptimize() + .logo(GT_UITextures.PICTURE_RADIATION_WARNING) + .logoPos(152, 41) + .neiRecipeBackgroundSize(170, 60) + .neiHandlerInfo(builder -> builder.setDisplayStack(GT_ModHandler.getIC2Item("nuclearReactor", 1, null))) + .build(); + + static { + RecipeMaps.centrifugeRecipes.addDownstream(RecipeMaps.centrifugeNonCellRecipes.deepCopyInput()); + RecipeMaps.mixerRecipes.addDownstream(RecipeMaps.mixerNonCellRecipes.deepCopyInput()); + RecipeMaps.electrolyzerRecipes.addDownstream(RecipeMaps.electrolyzerNonCellRecipes.deepCopyInput()); + RecipeMaps.dieselFuels.addDownstream( + IRecipeMap.newRecipeMap( + b -> b.build() + .map( + r -> RecipeMaps.largeBoilerFakeFuels.getBackend() + .addDieselRecipe(r)) + .map(Collections::singletonList) + .orElse(Collections.emptyList()))); + RecipeMaps.dieselFuels.addDownstream(IRecipeMap.newRecipeMap(b -> { + if (b.getMetadataOrDefault(FUEL_VALUE, 0) < 1500) return Collections.emptyList(); + return b.addTo(RecipeMaps.extremeDieselFuels); + })); + RecipeMaps.denseLiquidFuels.addDownstream( + IRecipeMap.newRecipeMap( + b -> b.build() + .map( + r -> RecipeMaps.largeBoilerFakeFuels.getBackend() + .addDenseLiquidRecipe(r)) + .map(Collections::singletonList) + .orElse(Collections.emptyList()))); + } +} diff --git a/src/main/java/gregtech/api/recipe/RecipeMetadataKey.java b/src/main/java/gregtech/api/recipe/RecipeMetadataKey.java new file mode 100644 index 0000000000..2156421835 --- /dev/null +++ b/src/main/java/gregtech/api/recipe/RecipeMetadataKey.java @@ -0,0 +1,84 @@ +package gregtech.api.recipe; + +import java.util.HashSet; +import java.util.Set; + +import javax.annotation.Nullable; +import javax.annotation.ParametersAreNonnullByDefault; + +import org.jetbrains.annotations.Contract; + +import gregtech.api.recipe.metadata.IRecipeMetadataStorage; +import gregtech.api.util.FieldsAreNonnullByDefault; +import gregtech.api.util.MethodsReturnNonnullByDefault; +import gregtech.nei.RecipeDisplayInfo; + +/** + * Unique key for the {@link IRecipeMetadataStorage}. It's also responsible for drawing metadata info on NEI. + * <p> + * You can use {@link gregtech.api.recipe.metadata.SimpleRecipeMetadataKey} if your metadata does not need NEI handling. + * + * @param <T> Type of the metadata to use. + */ +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +@FieldsAreNonnullByDefault +public abstract class RecipeMetadataKey<T> { + + private static final Set<RecipeMetadataKey<?>> allIdentifiers = new HashSet<>(); + private final Class<T> clazz; + private final String identifier; + + protected RecipeMetadataKey(Class<T> clazz, String identifier) { + this.clazz = clazz; + this.identifier = identifier; + if (allIdentifiers.contains(this)) { + throw new IllegalArgumentException( + "Cannot register metadata key with exact same properties: " + identifier + "@" + clazz); + } + allIdentifiers.add(this); + } + + /** + * Draws info about the metadata. + * + * @param recipeInfo Object to use for drawing text. + * @param value Metadata stored in the recipe. Can be safely {@link #cast}ed to the desired type. + */ + public abstract void drawInfo(RecipeDisplayInfo recipeInfo, @Nullable Object value); + + @Nullable + public T cast(@Nullable Object o) { + return clazz.cast(o); + } + + @Contract("_, !null -> !null") + @Nullable + public T cast(@Nullable Object o, @Nullable T defaultValue) { + T val = cast(o); + return val != null ? val : defaultValue; + } + + @Override + public String toString() { + return "RecipeMetadataKey{" + "clazz=" + clazz.getName() + ", identifier=" + identifier + '\'' + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + RecipeMetadataKey<?> that = (RecipeMetadataKey<?>) o; + + if (!clazz.equals(that.clazz)) return false; + return identifier.equals(that.identifier); + } + + @Override + public int hashCode() { + int result = clazz.hashCode(); + result = 31 * result + identifier.hashCode(); + return result; + } +} diff --git a/src/main/java/gregtech/api/recipe/check/CheckRecipeResult.java b/src/main/java/gregtech/api/recipe/check/CheckRecipeResult.java new file mode 100644 index 0000000000..8af5c58f5e --- /dev/null +++ b/src/main/java/gregtech/api/recipe/check/CheckRecipeResult.java @@ -0,0 +1,54 @@ +package gregtech.api.recipe.check; + +import javax.annotation.Nonnull; + +import net.minecraft.network.PacketBuffer; + +/** + * Class to indicate the result of recipe check in the machine. It doesn't need to be actual result of recipemap check, + * but can also be status of whether to start the machine. Examples can be found at {@link CheckRecipeResultRegistry}. + * <p> + * Sample instance must be registered to {@link CheckRecipeResultRegistry}. + */ +public interface CheckRecipeResult { + + /** + * @return Unique registry ID + */ + @Nonnull + String getID(); + + /** + * @return If recipe check is successful + */ + boolean wasSuccessful(); + + /** + * @return Actual text to show on client GUI + */ + @Nonnull + String getDisplayString(); + + /** + * Create new instance to receive packet. + */ + @Nonnull + CheckRecipeResult newInstance(); + + /** + * Encode value to sync. + */ + void encode(@Nonnull PacketBuffer buffer); + + /** + * Decode synced value. + */ + void decode(PacketBuffer buffer); + + /** + * @return If this message should stay on GUI when the machine is shut down. + */ + default boolean persistsOnShutdown() { + return false; + } +} diff --git a/src/main/java/gregtech/api/recipe/check/CheckRecipeResultRegistry.java b/src/main/java/gregtech/api/recipe/check/CheckRecipeResultRegistry.java new file mode 100644 index 0000000000..e141c39a67 --- /dev/null +++ b/src/main/java/gregtech/api/recipe/check/CheckRecipeResultRegistry.java @@ -0,0 +1,150 @@ +package gregtech.api.recipe.check; + +import java.util.HashMap; +import java.util.Map; + +import javax.annotation.Nonnull; + +public final class CheckRecipeResultRegistry { + + private static final Map<String, CheckRecipeResult> registry = new HashMap<>(); + + /** + * Registers CheckRecipeResult. No duplicated IDs are allowed. + * + * @param sample Sample object to register + */ + public static void register(CheckRecipeResult sample) { + if (isRegistered(sample.getID())) { + throw new IllegalStateException( + String.format( + "ID %s is already registered for %s", + sample.getID(), + registry.get(sample.getID()) + .getClass() + .getCanonicalName())); + } + registry.put(sample.getID(), sample); + } + + public static CheckRecipeResult getSampleFromRegistry(String id) { + if (!isRegistered(id)) { + throw new RuntimeException("Unknown id: " + id); + } + return registry.get(id); + } + + public static boolean isRegistered(String id) { + return registry.containsKey(id); + } + + /** + * Successfully found recipe. + */ + @Nonnull + public static final CheckRecipeResult SUCCESSFUL = SimpleCheckRecipeResult.ofSuccess("success"); + /** + * All requirements met to generator power. + */ + @Nonnull + public static final CheckRecipeResult GENERATING = SimpleCheckRecipeResult.ofSuccess("generating"); + /** + * Cannot find recipe. + */ + @Nonnull + public static final CheckRecipeResult NO_RECIPE = SimpleCheckRecipeResult.ofFailure("no_recipe"); + /** + * Cannot process recipe because item output is full. + */ + public static final CheckRecipeResult ITEM_OUTPUT_FULL = SimpleCheckRecipeResult.ofFailure("item_output_full"); + /** + * Cannot process recipe because fluid output is full. + */ + public static final CheckRecipeResult FLUID_OUTPUT_FULL = SimpleCheckRecipeResult.ofFailure("fluid_output_full"); + /** + * Default unknown state. + */ + @Nonnull + public static final CheckRecipeResult NONE = SimpleCheckRecipeResult.ofFailure("none"); + /** + * Code crashed. + */ + public static final CheckRecipeResult CRASH = SimpleCheckRecipeResult.ofFailurePersistOnShutdown("crash"); + /** + * Cannot find valid fuel for generator. + */ + @Nonnull + public static final CheckRecipeResult NO_FUEL_FOUND = SimpleCheckRecipeResult.ofFailure("no_fuel"); + /** + * Cannot find valid turbine. + */ + @Nonnull + public static final CheckRecipeResult NO_TURBINE_FOUND = SimpleCheckRecipeResult.ofFailure("no_turbine"); + /** + * No data sticks found for Assembly Line. + */ + @Nonnull + public static final CheckRecipeResult NO_DATA_STICKS = SimpleCheckRecipeResult.ofFailure("no_data_sticks"); + /** + * EU/t overflowed. + */ + @Nonnull + public static final CheckRecipeResult POWER_OVERFLOW = SimpleCheckRecipeResult.ofFailure("power_overflow"); + /** + * Progress time overflowed. + */ + @Nonnull + public static final CheckRecipeResult DURATION_OVERFLOW = SimpleCheckRecipeResult.ofFailure("duration_overflow"); + /** + * Machine had an internal error + */ + @Nonnull + public static final CheckRecipeResult INTERNAL_ERROR = SimpleCheckRecipeResult.ofFailure("internal_error"); + /** Multiblock ore drill has no drilling fluid */ + public static final CheckRecipeResult NO_DRILLING_FLUID = SimpleCheckRecipeResult.ofFailure("no_drilling_fluid"); + /** Multiblock drill is missing mining pipe */ + public static final CheckRecipeResult MISSING_MINING_PIPE = SimpleCheckRecipeResult.ofFailure("no_mining_pipe"); + /** Concrete backfiller is out of concrete */ + public static final CheckRecipeResult BACKFILLER_NO_CONCRETE = SimpleCheckRecipeResult + .ofFailure("backfiller_no_concrete"); + + /** + * Cannot process recipe because the machine cannot handle required EUt. + */ + @Nonnull + public static CheckRecipeResult insufficientPower(long required) { + return new ResultInsufficientPower(required); + } + + /** + * Cannot process recipe because the machine cannot handle its heat. + */ + @Nonnull + public static CheckRecipeResult insufficientHeat(int required) { + return new ResultInsufficientHeat(required); + } + + /** + * Cannot process recipe because the machine is tiered and its tier is too low. + */ + @Nonnull + public static CheckRecipeResult insufficientMachineTier(int required) { + return new ResultInsufficientMachineTier(required); + } + + /** + * Cannot process recipe because the machine doesn't have enough startup power. + */ + @Nonnull + public static CheckRecipeResult insufficientStartupPower(int required) { + return new ResultInsufficientStartupPower(required); + } + + static { + register(new SimpleCheckRecipeResult(false, "", false)); + register(new ResultInsufficientPower(0)); + register(new ResultInsufficientHeat(0)); + register(new ResultInsufficientMachineTier(0)); + register(new ResultInsufficientStartupPower(0)); + } +} diff --git a/src/main/java/gregtech/api/recipe/check/ResultInsufficientHeat.java b/src/main/java/gregtech/api/recipe/check/ResultInsufficientHeat.java new file mode 100644 index 0000000000..26c3530ba3 --- /dev/null +++ b/src/main/java/gregtech/api/recipe/check/ResultInsufficientHeat.java @@ -0,0 +1,65 @@ +package gregtech.api.recipe.check; + +import java.util.Objects; + +import javax.annotation.Nonnull; + +import net.minecraft.network.PacketBuffer; +import net.minecraft.util.StatCollector; + +import gregtech.api.enums.HeatingCoilLevel; +import gregtech.api.util.GT_Utility; + +public class ResultInsufficientHeat implements CheckRecipeResult { + + private int required; + + ResultInsufficientHeat(int required) { + this.required = required; + } + + @Override + @Nonnull + public String getID() { + return "insufficient_heat"; + } + + @Override + public boolean wasSuccessful() { + return false; + } + + @Override + @Nonnull + public String getDisplayString() { + return Objects.requireNonNull( + StatCollector.translateToLocalFormatted( + "GT5U.gui.text.insufficient_heat", + GT_Utility.formatNumbers(required), + HeatingCoilLevel.getDisplayNameFromHeat(required, true))); + } + + @Override + @Nonnull + public CheckRecipeResult newInstance() { + return new ResultInsufficientHeat(0); + } + + @Override + public void encode(@Nonnull PacketBuffer buffer) { + buffer.writeVarIntToBuffer(required); + } + + @Override + public void decode(@Nonnull PacketBuffer buffer) { + required = buffer.readVarIntFromBuffer(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ResultInsufficientHeat that = (ResultInsufficientHeat) o; + return required == that.required; + } +} diff --git a/src/main/java/gregtech/api/recipe/check/ResultInsufficientMachineTier.java b/src/main/java/gregtech/api/recipe/check/ResultInsufficientMachineTier.java new file mode 100644 index 0000000000..742eb3ef7a --- /dev/null +++ b/src/main/java/gregtech/api/recipe/check/ResultInsufficientMachineTier.java @@ -0,0 +1,63 @@ +package gregtech.api.recipe.check; + +import java.util.Objects; + +import javax.annotation.Nonnull; + +import net.minecraft.network.PacketBuffer; +import net.minecraft.util.StatCollector; + +import gregtech.api.util.GT_Utility; + +public class ResultInsufficientMachineTier implements CheckRecipeResult { + + private int required; + + ResultInsufficientMachineTier(int required) { + this.required = required; + } + + @Override + @Nonnull + public String getID() { + return "insufficient_machine_tier"; + } + + @Override + public boolean wasSuccessful() { + return false; + } + + @Override + @Nonnull + public String getDisplayString() { + return Objects.requireNonNull( + StatCollector.translateToLocalFormatted( + "GT5U.gui.text.insufficient_machine_tier", + GT_Utility.formatNumbers(required))); + } + + @Override + @Nonnull + public CheckRecipeResult newInstance() { + return new ResultInsufficientMachineTier(0); + } + + @Override + public void encode(@Nonnull PacketBuffer buffer) { + buffer.writeVarIntToBuffer(required); + } + + @Override + public void decode(@Nonnull PacketBuffer buffer) { + required = buffer.readVarIntFromBuffer(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ResultInsufficientMachineTier that = (ResultInsufficientMachineTier) o; + return required == that.required; + } +} diff --git a/src/main/java/gregtech/api/recipe/check/ResultInsufficientPower.java b/src/main/java/gregtech/api/recipe/check/ResultInsufficientPower.java new file mode 100644 index 0000000000..fdc06c0c07 --- /dev/null +++ b/src/main/java/gregtech/api/recipe/check/ResultInsufficientPower.java @@ -0,0 +1,64 @@ +package gregtech.api.recipe.check; + +import java.util.Objects; + +import javax.annotation.Nonnull; + +import net.minecraft.network.PacketBuffer; +import net.minecraft.util.StatCollector; + +import gregtech.api.util.GT_Utility; + +public class ResultInsufficientPower implements CheckRecipeResult { + + private long required; + + ResultInsufficientPower(long required) { + this.required = required; + } + + @Override + @Nonnull + public String getID() { + return "insufficient_power"; + } + + @Override + public boolean wasSuccessful() { + return false; + } + + @Override + @Nonnull + public String getDisplayString() { + return Objects.requireNonNull( + StatCollector.translateToLocalFormatted( + "GT5U.gui.text.insufficient_power", + GT_Utility.formatNumbers(required), + GT_Utility.getColoredTierNameFromVoltage(required))); + } + + @Override + @Nonnull + public CheckRecipeResult newInstance() { + return new ResultInsufficientPower(0); + } + + @Override + public void encode(@Nonnull PacketBuffer buffer) { + buffer.writeLong(required); + } + + @Override + public void decode(@Nonnull PacketBuffer buffer) { + required = buffer.readLong(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ResultInsufficientPower that = (ResultInsufficientPower) o; + return required == that.required; + } +} diff --git a/src/main/java/gregtech/api/recipe/check/ResultInsufficientStartupPower.java b/src/main/java/gregtech/api/recipe/check/ResultInsufficientStartupPower.java new file mode 100644 index 0000000000..62d2dd1fb2 --- /dev/null +++ b/src/main/java/gregtech/api/recipe/check/ResultInsufficientStartupPower.java @@ -0,0 +1,63 @@ +package gregtech.api.recipe.check; + +import java.util.Objects; + +import javax.annotation.Nonnull; + +import net.minecraft.network.PacketBuffer; +import net.minecraft.util.StatCollector; + +import gregtech.api.util.GT_Utility; + +public class ResultInsufficientStartupPower implements CheckRecipeResult { + + private int required; + + ResultInsufficientStartupPower(int required) { + this.required = required; + } + + @Override + @Nonnull + public String getID() { + return "insufficient_startup_power"; + } + + @Override + public boolean wasSuccessful() { + return false; + } + + @Override + @Nonnull + public String getDisplayString() { + return Objects.requireNonNull( + StatCollector.translateToLocalFormatted( + "GT5U.gui.text.insufficient_startup_power", + GT_Utility.formatNumbers(required))); + } + + @Override + @Nonnull + public CheckRecipeResult newInstance() { + return new ResultInsufficientStartupPower(0); + } + + @Override + public void encode(@Nonnull PacketBuffer buffer) { + buffer.writeVarIntToBuffer(required); + } + + @Override + public void decode(@Nonnull PacketBuffer buffer) { + required = buffer.readVarIntFromBuffer(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ResultInsufficientStartupPower that = (ResultInsufficientStartupPower) o; + return required == that.required; + } +} diff --git a/src/main/java/gregtech/api/recipe/check/SimpleCheckRecipeResult.java b/src/main/java/gregtech/api/recipe/check/SimpleCheckRecipeResult.java new file mode 100644 index 0000000000..58c85bbe9d --- /dev/null +++ b/src/main/java/gregtech/api/recipe/check/SimpleCheckRecipeResult.java @@ -0,0 +1,102 @@ +package gregtech.api.recipe.check; + +import java.util.Objects; + +import javax.annotation.Nonnull; + +import net.minecraft.network.PacketBuffer; +import net.minecraft.util.StatCollector; + +import com.gtnewhorizons.modularui.common.internal.network.NetworkUtils; + +/** + * Simple implementation of {@link CheckRecipeResult}. You can create new object without registering it. + */ +public class SimpleCheckRecipeResult implements CheckRecipeResult { + + private boolean success; + private String key; + private boolean persistsOnShutdown; + + SimpleCheckRecipeResult(boolean success, String key, boolean persistsOnShutdown) { + this.success = success; + this.key = key; + this.persistsOnShutdown = persistsOnShutdown; + } + + @Override + public String getID() { + return "simple_result"; + } + + @Override + public boolean wasSuccessful() { + return success; + } + + @Override + @Nonnull + public String getDisplayString() { + return Objects.requireNonNull(StatCollector.translateToLocal("GT5U.gui.text." + key)); + } + + @Override + @Nonnull + public CheckRecipeResult newInstance() { + return new SimpleCheckRecipeResult(false, "", false); + } + + @Override + public void encode(@Nonnull PacketBuffer buffer) { + buffer.writeBoolean(success); + NetworkUtils.writeStringSafe(buffer, key); + buffer.writeBoolean(persistsOnShutdown); + } + + @Override + public void decode(@Nonnull PacketBuffer buffer) { + success = buffer.readBoolean(); + key = NetworkUtils.readStringSafe(buffer); + persistsOnShutdown = buffer.readBoolean(); + } + + @Override + public boolean persistsOnShutdown() { + return persistsOnShutdown; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SimpleCheckRecipeResult that = (SimpleCheckRecipeResult) o; + return success == that.success && Objects.equals(key, that.key) + && persistsOnShutdown == that.persistsOnShutdown; + } + + /** + * Creates new result with successful state. Add your localized description with `GT5U.gui.text.{key}`. + * This is already registered to registry. + */ + @Nonnull + public static CheckRecipeResult ofSuccess(String key) { + return new SimpleCheckRecipeResult(true, key, false); + } + + /** + * Creates new result with failed state. Add your localized description with `GT5U.gui.text.{key}`. + * This is already registered to registry. + */ + @Nonnull + public static CheckRecipeResult ofFailure(String key) { + return new SimpleCheckRecipeResult(false, key, false); + } + + /** + * Creates new result object with failed state that does not get reset on shutdown. Add your localized description + * with `GT5U.gui.text.{key}`. This is already registered to registry. + */ + public static CheckRecipeResult ofFailurePersistOnShutdown(String key) { + return new SimpleCheckRecipeResult(false, key, true); + } +} diff --git a/src/main/java/gregtech/api/recipe/check/SingleRecipeCheck.java b/src/main/java/gregtech/api/recipe/check/SingleRecipeCheck.java new file mode 100644 index 0000000000..8683812d84 --- /dev/null +++ b/src/main/java/gregtech/api/recipe/check/SingleRecipeCheck.java @@ -0,0 +1,405 @@ +package gregtech.api.recipe.check; + +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.function.Function; +import java.util.stream.Collectors; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTBase; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraftforge.common.util.Constants; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidRegistry; +import net.minecraftforge.fluids.FluidStack; + +import com.google.common.collect.ImmutableMap; + +import gregtech.api.enums.GT_Values; +import gregtech.api.recipe.RecipeMap; +import gregtech.api.util.GT_Recipe; +import gregtech.api.util.GT_Utility; +import gregtech.api.util.GT_Utility.ItemId; + +/** + * Used by machines that are locked to a single recipe, for faster recipe check. + * <p> + * Computation time will be like these: + * <ul> + * Normal recipe check: + * <ul> + * {@link gregtech.api.recipe.FindRecipeQuery#find Find recipe from recipemap}: O(NCR) + * where N = number of machine inputs, C = average amount of recipe candidates found for specific input, + * R = computation time to {@link GT_Recipe#isRecipeInputEqual check if inputs match to recipe} + * </ul> + * <ul> + * {@link GT_Recipe#isRecipeInputEqual Check if inputs match to recipe}: O(NM) + * where N = number of machine inputs, M = number of recipe inputs + * </ul> + * </ul> + * <ul> + * {@link #checkRecipeInputs Single recipe check}: O(N + M) + * where N = number of machine inputs, M = number of recipe inputs + * </ul> + */ +public class SingleRecipeCheck { + + @Nonnull + private final GT_Recipe recipe; + @Nonnull + private final RecipeMap<?> recipeMap; + @Nonnull + private final ImmutableMap<ItemId, Integer> itemCost; + @Nonnull + private final ImmutableMap<Fluid, Integer> fluidCost; + + private final int totalItemCost; + private final int totalFluidCost; + + private SingleRecipeCheck(@Nonnull GT_Recipe recipe, @Nonnull RecipeMap<?> recipeMap, + @Nonnull ImmutableMap<ItemId, Integer> itemCost, @Nonnull ImmutableMap<Fluid, Integer> fluidCost) { + this.recipe = recipe; + this.recipeMap = recipeMap; + this.itemCost = itemCost; + this.fluidCost = fluidCost; + + this.totalItemCost = itemCost.values() + .stream() + .mapToInt(Integer::intValue) + .sum(); + this.totalFluidCost = fluidCost.values() + .stream() + .mapToInt(Integer::intValue) + .sum(); + } + + @Nonnull + public GT_Recipe getRecipe() { + return recipe; + } + + @Nonnull + public RecipeMap<?> getRecipeMap() { + return recipeMap; + } + + /** + * Returns the number of parallel recipes, or 0 if recipe is not satisfied at all. + */ + public int checkRecipeInputs(boolean consumeInputs, int maxParallel, ItemStack[] itemInputs, + FluidStack[] fluidInputs) { + int currentParallel = maxParallel; + + if (totalItemCost > 0) { + // Create map for item -> stored amount + Map<ItemId, Integer> itemMap = new HashMap<>(); + for (ItemStack itemStack : itemInputs) { + if (itemStack == null) continue; + itemMap.merge(ItemId.createNoCopy(itemStack), itemStack.stackSize, Integer::sum); + } + + // Check how many parallels can it perform for each item + for (Map.Entry<ItemId, Integer> costEntry : itemCost.entrySet()) { + currentParallel = Math + .min(currentParallel, itemMap.getOrDefault(costEntry.getKey(), 0) / costEntry.getValue()); + if (currentParallel <= 0) { + return 0; + } + } + } + + if (totalFluidCost > 0) { + // Create map for fluid -> stored amount + Map<Fluid, Integer> fluidMap = new HashMap<>(); + for (FluidStack fluidStack : fluidInputs) { + if (fluidStack == null) continue; + fluidMap.merge(fluidStack.getFluid(), fluidStack.amount, Integer::sum); + } + + // Check how many parallels can it perform for each fluid + for (Map.Entry<Fluid, Integer> costEntry : fluidCost.entrySet()) { + currentParallel = Math + .min(currentParallel, fluidMap.getOrDefault(costEntry.getKey(), 0) / costEntry.getValue()); + if (currentParallel <= 0) { + return 0; + } + } + } + + final int finalParallel = currentParallel; + if (consumeInputs) { + if (totalItemCost > 0) { + int remainingItemCost = totalItemCost * finalParallel; + Map<ItemId, Integer> runningItemCost = itemCost.entrySet() + .stream() + .collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue() * finalParallel)); + + for (ItemStack itemStack : itemInputs) { + if (itemStack == null) continue; + ItemId key = ItemId.createNoCopy(itemStack); + int runningCost = runningItemCost.getOrDefault(key, 0); + int paid = Math.min(itemStack.stackSize, runningCost); + itemStack.stackSize -= paid; + runningItemCost.put(key, runningCost - paid); + + remainingItemCost -= paid; + // If all item costs are paid, we don't need to iterate inputs furthermore + if (remainingItemCost <= 0) { + break; + } + } + } + + if (totalFluidCost > 0) { + int remainingFluidCost = totalFluidCost * finalParallel; + Map<Fluid, Integer> runningFluidCost = fluidCost.entrySet() + .stream() + .collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue() * finalParallel)); + + for (FluidStack fluidStack : fluidInputs) { + if (fluidStack == null) continue; + Fluid key = fluidStack.getFluid(); + int runningCost = runningFluidCost.getOrDefault(key, 0); + int paid = Math.min(fluidStack.amount, runningCost); + fluidStack.amount -= paid; + runningFluidCost.put(key, runningCost - paid); + + remainingFluidCost -= paid; + // If all fluid costs are paid, we don't need to iterate inputs furthermore + if (remainingFluidCost <= 0) { + break; + } + } + } + } + + return finalParallel; + } + + public NBTTagCompound writeToNBT() { + // Here we encode recipe input, output and all other important values. + // At load time we do a recipe check again, so in case the recipe is gone, we can stop tracking. + // Of course the next step would be auto migrating to new recipe (if any), but given + // we don't yet have a mean to uniquely name a recipe, this will have to make do. + // Consider move serialization code to GT_Recipe once this has been proven to work + NBTTagCompound tag = new NBTTagCompound(); + tag.setString("recipemap", recipeMap.unlocalizedName); + if (recipe.mInputs != null) { + tag.setTag("inputs", writeList(recipe.mInputs, GT_Utility::saveItem)); + } + if (recipe.mOutputs != null) { + tag.setTag("outputs", writeList(recipe.mOutputs, GT_Utility::saveItem)); + } + if (recipe.mChances != null) { + tag.setIntArray("chances", recipe.mChances); + } + if (recipe.mFluidInputs != null) { + tag.setTag( + "fInputs", + writeList( + recipe.mFluidInputs, + s -> s == null ? new NBTTagCompound() : s.writeToNBT(new NBTTagCompound()))); + } + if (recipe.mFluidOutputs != null) { + tag.setTag( + "fOutputs", + writeList( + recipe.mFluidOutputs, + s -> s == null ? new NBTTagCompound() : s.writeToNBT(new NBTTagCompound()))); + } + tag.setInteger("eut", recipe.mEUt); + tag.setInteger("duration", recipe.mDuration); + tag.setInteger("specialValue", recipe.mSpecialValue); + tag.setTag("itemCost", writeList(itemCost.entrySet(), e -> { + NBTTagCompound ret = new NBTTagCompound(); + ret.setTag( + "id", + e.getKey() + .writeToNBT()); + ret.setInteger("count", e.getValue()); + return ret; + })); + tag.setTag("fluidCost", writeList(fluidCost.entrySet(), e -> { + NBTTagCompound ret = new NBTTagCompound(); + ret.setString( + "id", + e.getKey() + .getName()); + ret.setInteger("count", e.getValue()); + return ret; + })); + return tag; + } + + private static <T, NBT extends NBTBase> NBTTagList writeList(T[] arr, Function<T, NBT> ser) { + return writeList(Arrays.asList(arr), ser); + } + + private static <T, NBT extends NBTBase> NBTTagList writeList(Collection<T> arr, Function<T, NBT> ser) { + NBTTagList l = new NBTTagList(); + for (T t : arr) { + l.appendTag(ser.apply(t)); + } + return l; + } + + @Nullable + public static SingleRecipeCheck tryLoad(RecipeMap<?> recipeMap, NBTTagCompound tag) { + if (tag == null || tag.hasNoTags()) return null; + + RecipeMap<?> mapToUse; + if (tag.hasKey("recipemap")) { + String mapName = tag.getString("recipemap"); + RecipeMap<?> foundMap = RecipeMap.ALL_RECIPE_MAPS.get(mapName); + if (foundMap != null) { + mapToUse = foundMap; + } else { + mapToUse = recipeMap; + } + } else { + mapToUse = recipeMap; + } + if (mapToUse == null) { + return null; + } + + GT_Recipe foundRecipe = tryFindRecipe(mapToUse, tag); + if (foundRecipe == null) return null; + return new SingleRecipeCheck(foundRecipe, mapToUse, loadItemCost(tag), loadFluidCost(tag)); + } + + private static ImmutableMap<Fluid, Integer> loadFluidCost(NBTTagCompound tag) { + return GT_Utility.streamCompounds(tag.getTagList("fluidCost", Constants.NBT.TAG_COMPOUND)) + .collect( + GT_Utility + .toImmutableMapSerial(t -> FluidRegistry.getFluid(t.getString("id")), t -> t.getInteger("count"))); + } + + private static ImmutableMap<ItemId, Integer> loadItemCost(NBTTagCompound tag) { + return GT_Utility.streamCompounds(tag.getTagList("itemCost", Constants.NBT.TAG_COMPOUND)) + .collect( + GT_Utility + .toImmutableMapSerial(t -> ItemId.create(t.getCompoundTag("id")), t -> t.getInteger("count"))); + } + + private static GT_Recipe tryFindRecipe(@Nonnull RecipeMap<?> recipeMap, NBTTagCompound tag) { + ItemStack[] inputs = GT_Utility.streamCompounds(tag.getTagList("inputs", Constants.NBT.TAG_COMPOUND)) + .map(GT_Utility::loadItem) + .toArray(ItemStack[]::new); + ItemStack[] outputs = GT_Utility.streamCompounds(tag.getTagList("outputs", Constants.NBT.TAG_COMPOUND)) + .map(GT_Utility::loadItem) + .toArray(ItemStack[]::new); + FluidStack[] fInputs = GT_Utility.streamCompounds(tag.getTagList("fInputs", Constants.NBT.TAG_COMPOUND)) + .map(FluidStack::loadFluidStackFromNBT) + .toArray(FluidStack[]::new); + FluidStack[] fOutputs = GT_Utility.streamCompounds(tag.getTagList("fOutputs", Constants.NBT.TAG_COMPOUND)) + .map(FluidStack::loadFluidStackFromNBT) + .toArray(FluidStack[]::new); + int eut = tag.getInteger("eut"); + GT_Recipe found = recipeMap.findRecipe(null, false, GT_Values.V[GT_Utility.getTier(eut)], fInputs, inputs); + int[] chances = tag.getIntArray("chances"); + if (chances.length == 0) chances = null; + if (found == null || !GT_Utility.equals(inputs, found.mInputs) + || !Arrays.equals(fInputs, found.mFluidInputs) + || !GT_Utility.equals(outputs, found.mOutputs) + || !Arrays.equals(fOutputs, found.mFluidOutputs) + || !Arrays.equals(chances, found.mChances) + || found.mDuration != tag.getInteger("duration") + || found.mEUt != eut + || found.mSpecialValue != tag.getInteger("specialValue")) return null; + return found; + } + + private static ImmutableMap<ItemId, Integer> buildItemMap(ItemStack[] inputs) { + Map<ItemId, Integer> itemMap = new HashMap<>(); + for (ItemStack itemStack : inputs) { + if (itemStack == null) continue; + itemMap.merge(ItemId.create(itemStack), itemStack.stackSize, Integer::sum); + } + return ImmutableMap.copyOf(itemMap); + } + + private static ImmutableMap<Fluid, Integer> buildFluidMap(FluidStack[] fluids) { + Map<Fluid, Integer> fluidMap = new HashMap<>(); + for (FluidStack fluidStack : fluids) { + if (fluidStack == null) continue; + fluidMap.merge(fluidStack.getFluid(), fluidStack.amount, Integer::sum); + } + return ImmutableMap.copyOf(fluidMap); + } + + public static Builder builder(@Nonnull RecipeMap<?> recipeMap) { + return new Builder(Objects.requireNonNull(recipeMap)); + } + + public static class Builder { + + private final RecipeMap<?> recipeMap; + + // In order to compute which items and fluids are consumed by the recipe, we compare the + // multi-block's items and fluids before and after inputs are consumed by the recipe. + private Map<ItemId, Integer> beforeItems; + private Map<Fluid, Integer> beforeFluids; + private Map<ItemId, Integer> afterItems; + private Map<Fluid, Integer> afterFluids; + + private GT_Recipe recipe; + + private Builder(@Nonnull RecipeMap<?> recipeMap) { + this.recipeMap = recipeMap; + } + + public Builder setBefore(ItemStack[] inputs, FluidStack[] fluids) { + beforeItems = buildItemMap(inputs); + beforeFluids = buildFluidMap(fluids); + return this; + } + + public Builder setAfter(ItemStack[] inputs, FluidStack[] fluids) { + afterItems = buildItemMap(inputs); + afterFluids = buildFluidMap(fluids); + return this; + } + + public Builder setRecipe(@Nonnull GT_Recipe recipe) { + this.recipe = recipe; + return this; + } + + private ImmutableMap<ItemId, Integer> buildItemCost() { + ImmutableMap.Builder<ItemId, Integer> itemCostBuilder = ImmutableMap.builder(); + for (Map.Entry<ItemId, Integer> entry : beforeItems.entrySet()) { + int cost = entry.getValue() - afterItems.getOrDefault(entry.getKey(), 0); + if (cost > 0) { + itemCostBuilder.put(entry.getKey(), cost); + } + } + return itemCostBuilder.build(); + } + + private ImmutableMap<Fluid, Integer> buildFluidCost() { + ImmutableMap.Builder<Fluid, Integer> fluidCostBuilder = ImmutableMap.builder(); + for (Map.Entry<Fluid, Integer> entry : beforeFluids.entrySet()) { + int cost = entry.getValue() - afterFluids.getOrDefault(entry.getKey(), 0); + if (cost > 0) { + fluidCostBuilder.put(entry.getKey(), cost); + } + } + return fluidCostBuilder.build(); + } + + public SingleRecipeCheck build() { + if (recipe == null) { + throw new IllegalStateException("recipe is not set"); + } + return new SingleRecipeCheck(recipe, recipeMap, buildItemCost(), buildFluidCost()); + } + } +} diff --git a/src/main/java/gregtech/api/recipe/maps/AssemblerBackend.java b/src/main/java/gregtech/api/recipe/maps/AssemblerBackend.java new file mode 100644 index 0000000000..cfa25e9fe2 --- /dev/null +++ b/src/main/java/gregtech/api/recipe/maps/AssemblerBackend.java @@ -0,0 +1,35 @@ +package gregtech.api.recipe.maps; + +import javax.annotation.Nullable; +import javax.annotation.ParametersAreNonnullByDefault; + +import net.minecraft.item.ItemStack; +import net.minecraftforge.fluids.FluidStack; + +import gregtech.api.enums.ItemList; +import gregtech.api.recipe.RecipeMapBackend; +import gregtech.api.recipe.RecipeMapBackendPropertiesBuilder; +import gregtech.api.util.GT_Recipe; +import gregtech.api.util.MethodsReturnNonnullByDefault; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +public class AssemblerBackend extends RecipeMapBackend { + + public AssemblerBackend(RecipeMapBackendPropertiesBuilder propertiesBuilder) { + super(propertiesBuilder); + } + + @Override + protected GT_Recipe modifyFoundRecipe(GT_Recipe recipe, ItemStack[] items, FluidStack[] fluids, + @Nullable ItemStack specialSlot) { + for (ItemStack item : items) { + if (ItemList.Paper_Printed_Pages.isStackEqual(item, false, true)) { + recipe = recipe.copy(); + recipe.mCanBeBuffered = false; + recipe.mOutputs[0].setTagCompound(item.getTagCompound()); + } + } + return recipe; + } +} diff --git a/src/main/java/gregtech/api/recipe/maps/AssemblyLineFrontend.java b/src/main/java/gregtech/api/recipe/maps/AssemblyLineFrontend.java new file mode 100644 index 0000000000..3d56c96b82 --- /dev/null +++ b/src/main/java/gregtech/api/recipe/maps/AssemblyLineFrontend.java @@ -0,0 +1,76 @@ +package gregtech.api.recipe.maps; + +import java.util.Collections; +import java.util.List; +import java.util.function.Supplier; + +import javax.annotation.ParametersAreNonnullByDefault; + +import com.gtnewhorizons.modularui.api.math.Pos2d; +import com.gtnewhorizons.modularui.api.screen.ModularWindow; +import com.gtnewhorizons.modularui.common.widget.ProgressBar; + +import gregtech.api.gui.modularui.GT_UITextures; +import gregtech.api.recipe.BasicUIPropertiesBuilder; +import gregtech.api.recipe.NEIRecipePropertiesBuilder; +import gregtech.api.recipe.RecipeMapFrontend; +import gregtech.api.util.MethodsReturnNonnullByDefault; +import gregtech.common.gui.modularui.UIHelper; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +public class AssemblyLineFrontend extends RecipeMapFrontend { + + public AssemblyLineFrontend(BasicUIPropertiesBuilder uiPropertiesBuilder, + NEIRecipePropertiesBuilder neiPropertiesBuilder) { + super(uiPropertiesBuilder, neiPropertiesBuilder); + } + + @Override + public List<Pos2d> getItemInputPositions(int itemInputCount) { + return UIHelper.getGridPositions(itemInputCount, 16, 8, 4); + } + + @Override + public List<Pos2d> getItemOutputPositions(int itemOutputCount) { + return Collections.singletonList(new Pos2d(142, 8)); + } + + @Override + public Pos2d getSpecialItemPosition() { + return new Pos2d(142, 44); + } + + @Override + public List<Pos2d> getFluidInputPositions(int fluidInputCount) { + return UIHelper.getGridPositions(fluidInputCount, 106, 8, 1); + } + + @Override + public void addProgressBar(ModularWindow.Builder builder, Supplier<Float> progressSupplier, Pos2d windowOffset) { + int bar1Width = 17; + int bar2Width = 18; + List<Supplier<Float>> splitProgress = splitProgress(progressSupplier, bar1Width, bar2Width); + builder.widget( + new ProgressBar().setTexture(GT_UITextures.PROGRESSBAR_ASSEMBLY_LINE_1, bar1Width) + .setDirection(ProgressBar.Direction.RIGHT) + .setProgress(splitProgress.get(0)) + .setSynced(false, false) + .setPos(new Pos2d(88, 8).add(windowOffset)) + .setSize(bar1Width, 72)); + builder.widget( + new ProgressBar().setTexture(GT_UITextures.PROGRESSBAR_ASSEMBLY_LINE_2, bar2Width) + .setDirection(ProgressBar.Direction.RIGHT) + .setProgress(splitProgress.get(1)) + .setSynced(false, false) + .setPos(new Pos2d(124, 8).add(windowOffset)) + .setSize(bar2Width, 72)); + builder.widget( + new ProgressBar().setTexture(GT_UITextures.PROGRESSBAR_ASSEMBLY_LINE_3, 18) + .setDirection(ProgressBar.Direction.UP) + .setProgress(progressSupplier) + .setSynced(false, false) + .setPos(new Pos2d(146, 26).add(windowOffset)) + .setSize(10, 18)); + } +} diff --git a/src/main/java/gregtech/api/recipe/maps/DistillationTowerFrontend.java b/src/main/java/gregtech/api/recipe/maps/DistillationTowerFrontend.java new file mode 100644 index 0000000000..b061d10d55 --- /dev/null +++ b/src/main/java/gregtech/api/recipe/maps/DistillationTowerFrontend.java @@ -0,0 +1,38 @@ +package gregtech.api.recipe.maps; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import javax.annotation.ParametersAreNonnullByDefault; + +import com.gtnewhorizons.modularui.api.math.Pos2d; + +import gregtech.api.recipe.BasicUIPropertiesBuilder; +import gregtech.api.recipe.NEIRecipePropertiesBuilder; +import gregtech.api.recipe.RecipeMapFrontend; +import gregtech.api.util.MethodsReturnNonnullByDefault; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +public class DistillationTowerFrontend extends RecipeMapFrontend { + + public DistillationTowerFrontend(BasicUIPropertiesBuilder uiPropertiesBuilder, + NEIRecipePropertiesBuilder neiPropertiesBuilder) { + super(uiPropertiesBuilder, neiPropertiesBuilder); + } + + @Override + public List<Pos2d> getItemOutputPositions(int itemOutputCount) { + return Collections.singletonList(new Pos2d(106, 62)); + } + + @Override + public List<Pos2d> getFluidOutputPositions(int fluidOutputCount) { + List<Pos2d> results = new ArrayList<>(); + for (int i = 1; i < fluidOutputCount + 1; i++) { + results.add(new Pos2d(106 + (i % 3) * 18, 62 - (i / 3) * 18)); + } + return results; + } +} diff --git a/src/main/java/gregtech/api/recipe/maps/FluidCannerBackend.java b/src/main/java/gregtech/api/recipe/maps/FluidCannerBackend.java new file mode 100644 index 0000000000..e5681f59aa --- /dev/null +++ b/src/main/java/gregtech/api/recipe/maps/FluidCannerBackend.java @@ -0,0 +1,73 @@ +package gregtech.api.recipe.maps; + +import javax.annotation.Nullable; +import javax.annotation.ParametersAreNonnullByDefault; + +import net.minecraft.item.ItemStack; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.IFluidContainerItem; + +import gregtech.api.enums.GT_Values; +import gregtech.api.recipe.RecipeMapBackend; +import gregtech.api.recipe.RecipeMapBackendPropertiesBuilder; +import gregtech.api.util.GT_Recipe; +import gregtech.api.util.GT_Utility; +import gregtech.api.util.MethodsReturnNonnullByDefault; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +public class FluidCannerBackend extends RecipeMapBackend { + + public FluidCannerBackend(RecipeMapBackendPropertiesBuilder propertiesBuilder) { + super(propertiesBuilder); + } + + @Override + protected GT_Recipe findFallback(ItemStack[] items, FluidStack[] fluids, @Nullable ItemStack specialSlot) { + if (items.length == 0 || items[0] == null) { + return null; + } + + if (fluids.length > 0 && fluids[0] != null) { + ItemStack filledItem = GT_Utility.fillFluidContainer(fluids[0], items[0], false, true); + FluidStack fluidToTake = GT_Utility.getFluidForFilledItem(filledItem, true); + if (fluidToTake != null) { + return GT_Values.RA.stdBuilder() + .itemInputs(GT_Utility.copyAmount(1, items[0])) + .itemOutputs(filledItem) + .fluidInputs(fluidToTake) + .duration(Math.max(fluidToTake.amount / 64, 16)) + .eut(1) + .noOptimize() + .noBuffer() + .build() + .orElse(null); + } + } + FluidStack drainedFluid = GT_Utility.getFluidForFilledItem(items[0], true); + if (drainedFluid != null) { + return GT_Values.RA.stdBuilder() + .itemInputs(GT_Utility.copyAmount(1, items[0])) + .itemOutputs(GT_Utility.getContainerItem(items[0], true)) + .fluidOutputs(drainedFluid) + .duration(Math.max(drainedFluid.amount / 64, 16)) + .eut(1) + .noBuffer() + .build() + .orElse(null); + } + return null; + } + + @Override + public boolean containsInput(ItemStack item) { + return super.containsInput(item) || (item.getItem() instanceof IFluidContainerItem + && ((IFluidContainerItem) item.getItem()).getCapacity(item) > 0); + } + + @Override + public boolean containsInput(Fluid fluid) { + return true; + } +} diff --git a/src/main/java/gregtech/api/recipe/maps/FluidOnlyFrontend.java b/src/main/java/gregtech/api/recipe/maps/FluidOnlyFrontend.java new file mode 100644 index 0000000000..8b37f6388c --- /dev/null +++ b/src/main/java/gregtech/api/recipe/maps/FluidOnlyFrontend.java @@ -0,0 +1,33 @@ +package gregtech.api.recipe.maps; + +import java.util.List; + +import com.gtnewhorizons.modularui.api.math.Pos2d; + +import gregtech.api.recipe.BasicUIPropertiesBuilder; +import gregtech.api.recipe.NEIRecipePropertiesBuilder; +import gregtech.api.recipe.RecipeMapFrontend; +import gregtech.api.util.MethodsReturnNonnullByDefault; +import gregtech.common.gui.modularui.UIHelper; + +/** + * Display fluids where normally items are placed on NEI. + */ +@MethodsReturnNonnullByDefault +public class FluidOnlyFrontend extends RecipeMapFrontend { + + public FluidOnlyFrontend(BasicUIPropertiesBuilder uiPropertiesBuilder, + NEIRecipePropertiesBuilder neiPropertiesBuilder) { + super(uiPropertiesBuilder, neiPropertiesBuilder); + } + + @Override + public List<Pos2d> getFluidInputPositions(int fluidInputCount) { + return UIHelper.getItemInputPositions(fluidInputCount); + } + + @Override + public List<Pos2d> getFluidOutputPositions(int fluidOutputCount) { + return UIHelper.getItemOutputPositions(fluidOutputCount); + } +} diff --git a/src/main/java/gregtech/api/recipe/maps/FormingPressBackend.java b/src/main/java/gregtech/api/recipe/maps/FormingPressBackend.java new file mode 100644 index 0000000000..ce3ea3e89c --- /dev/null +++ b/src/main/java/gregtech/api/recipe/maps/FormingPressBackend.java @@ -0,0 +1,92 @@ +package gregtech.api.recipe.maps; + +import javax.annotation.Nullable; +import javax.annotation.ParametersAreNonnullByDefault; + +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraftforge.fluids.FluidStack; + +import gregtech.api.enums.GT_Values; +import gregtech.api.enums.ItemList; +import gregtech.api.recipe.RecipeMapBackend; +import gregtech.api.recipe.RecipeMapBackendPropertiesBuilder; +import gregtech.api.util.GT_Recipe; +import gregtech.api.util.GT_Utility; +import gregtech.api.util.MethodsReturnNonnullByDefault; + +/** + * Special Class for Forming Press handling. + */ +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +public class FormingPressBackend extends RecipeMapBackend { + + public FormingPressBackend(RecipeMapBackendPropertiesBuilder propertiesBuilder) { + super(propertiesBuilder); + } + + @Override + protected GT_Recipe modifyFoundRecipe(GT_Recipe recipe, ItemStack[] items, FluidStack[] fluids, + @Nullable ItemStack specialSlot) { + for (ItemStack mold : items) { + if (ItemList.Shape_Mold_Credit.isStackEqual(mold, false, true)) { + NBTTagCompound nbt = mold.getTagCompound(); + if (nbt == null) nbt = new NBTTagCompound(); + if (!nbt.hasKey("credit_security_id")) nbt.setLong("credit_security_id", System.nanoTime()); + mold.setTagCompound(nbt); + + recipe = recipe.copy(); + recipe.mCanBeBuffered = false; + recipe.mOutputs[0].setTagCompound(nbt); + return recipe; + } + } + return recipe; + } + + @Override + protected GT_Recipe findFallback(ItemStack[] items, FluidStack[] fluids, @Nullable ItemStack specialSlot) { + if (items.length < 2) { + return null; + } + return findRenamingRecipe(items); + } + + @Nullable + private GT_Recipe findRenamingRecipe(ItemStack[] inputs) { + ItemStack mold = findNameMoldIndex(inputs); + if (mold == null) return null; + ItemStack input = findStackToRename(inputs, mold); + if (input == null) return null; + ItemStack output = GT_Utility.copyAmount(1, input); + if (output == null) return null; + output.setStackDisplayName(mold.getDisplayName()); + return GT_Values.RA.stdBuilder() + .itemInputs(GT_Utility.copyAmount(0, mold), GT_Utility.copyAmount(1, input)) + .itemOutputs(output) + .duration(128) + .eut(8) + .noBuffer() + .nbtSensitive() + .build() + .orElse(null); + } + + @Nullable + private ItemStack findNameMoldIndex(ItemStack[] inputs) { + for (ItemStack stack : inputs) { + if (ItemList.Shape_Mold_Name.isStackEqual(stack, false, true)) return stack; + } + return null; + } + + @Nullable + private ItemStack findStackToRename(ItemStack[] inputs, ItemStack mold) { + for (ItemStack stack : inputs) { + if (stack == mold || stack == null) continue; + return stack; + } + return null; + } +} diff --git a/src/main/java/gregtech/api/recipe/maps/FuelBackend.java b/src/main/java/gregtech/api/recipe/maps/FuelBackend.java new file mode 100644 index 0000000000..49c989e174 --- /dev/null +++ b/src/main/java/gregtech/api/recipe/maps/FuelBackend.java @@ -0,0 +1,75 @@ +package gregtech.api.recipe.maps; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import javax.annotation.Nullable; +import javax.annotation.ParametersAreNonnullByDefault; + +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidStack; + +import gregtech.api.recipe.RecipeMapBackend; +import gregtech.api.recipe.RecipeMapBackendPropertiesBuilder; +import gregtech.api.util.GT_Recipe; +import gregtech.api.util.GT_RecipeBuilder; +import gregtech.api.util.GT_Utility; +import gregtech.api.util.MethodsReturnNonnullByDefault; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +public class FuelBackend extends RecipeMapBackend { + + private final Map<String, GT_Recipe> recipesByFluidInput = new HashMap<>(); + + public FuelBackend(RecipeMapBackendPropertiesBuilder propertiesBuilder) { + super(propertiesBuilder.disableOptimize()); + } + + @Override + protected Collection<GT_Recipe> doAdd(GT_RecipeBuilder builder) { + if (builder.getDuration() == -1) { + builder.duration(0); + } + if (builder.getEUt() == -1) { + builder.eut(0); + } + return super.doAdd(builder); + } + + @Override + public GT_Recipe compileRecipe(GT_Recipe recipe) { + super.compileRecipe(recipe); + if (recipe.mInputs != null && GT_Utility.getNonnullElementCount(recipe.mInputs) == 1 + && (recipe.mFluidInputs == null || GT_Utility.getNonnullElementCount(recipe.mFluidInputs) == 0)) { + FluidStack fluidStack = GT_Utility.getFluidForFilledItem(recipe.mInputs[0], true); + if (fluidStack != null) { + fluidStack.amount = 0; + recipesByFluidInput.put( + fluidStack.getFluid() + .getName(), + recipe); + } + } else if ((recipe.mInputs == null || GT_Utility.getNonnullElementCount(recipe.mInputs) == 0) + && recipe.mFluidInputs != null + && GT_Utility.getNonnullElementCount(recipe.mFluidInputs) >= 1 + && recipe.mFluidInputs[0] != null) { + recipesByFluidInput.put( + recipe.mFluidInputs[0].getFluid() + .getName(), + recipe); + } + return recipe; + } + + @Nullable + public GT_Recipe findFuel(FluidStack fluidStack) { + return findFuel(fluidStack.getFluid()); + } + + @Nullable + public GT_Recipe findFuel(Fluid fluid) { + return recipesByFluidInput.get(fluid.getName()); + } +} diff --git a/src/main/java/gregtech/api/recipe/maps/FurnaceBackend.java b/src/main/java/gregtech/api/recipe/maps/FurnaceBackend.java new file mode 100644 index 0000000000..c4095eeb4e --- /dev/null +++ b/src/main/java/gregtech/api/recipe/maps/FurnaceBackend.java @@ -0,0 +1,52 @@ +package gregtech.api.recipe.maps; + +import javax.annotation.Nullable; +import javax.annotation.ParametersAreNonnullByDefault; + +import net.minecraft.item.ItemStack; +import net.minecraftforge.fluids.FluidStack; + +import gregtech.api.enums.GT_Values; +import gregtech.api.recipe.RecipeMapBackendPropertiesBuilder; +import gregtech.api.util.GT_ModHandler; +import gregtech.api.util.GT_Recipe; +import gregtech.api.util.GT_Utility; +import gregtech.api.util.MethodsReturnNonnullByDefault; + +/** + * Special Class for Furnace Recipe handling. + */ +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +public class FurnaceBackend extends NonGTBackend { + + public FurnaceBackend(RecipeMapBackendPropertiesBuilder propertiesBuilder) { + super(propertiesBuilder); + } + + @Override + protected GT_Recipe overwriteFindRecipe(ItemStack[] items, FluidStack[] fluids, @Nullable ItemStack specialSlot, + @Nullable GT_Recipe cachedRecipe) { + if (items.length == 0 || items[0] == null) { + return null; + } + if (cachedRecipe != null && cachedRecipe.isRecipeInputEqual(false, true, fluids, items)) { + return cachedRecipe; + } + ItemStack output = GT_ModHandler.getSmeltingOutput(items[0], false, null); + return output == null ? null + : GT_Values.RA.stdBuilder() + .itemInputs(GT_Utility.copyAmount(1, items[0])) + .itemOutputs(output) + .duration(128) + .eut(4) + .noOptimize() + .build() + .orElse(null); + } + + @Override + public boolean containsInput(ItemStack item) { + return GT_ModHandler.getSmeltingOutput(item, false, null) != null; + } +} diff --git a/src/main/java/gregtech/api/recipe/maps/LargeBoilerFuelBackend.java b/src/main/java/gregtech/api/recipe/maps/LargeBoilerFuelBackend.java new file mode 100644 index 0000000000..53152312f4 --- /dev/null +++ b/src/main/java/gregtech/api/recipe/maps/LargeBoilerFuelBackend.java @@ -0,0 +1,132 @@ +package gregtech.api.recipe.maps; + +import java.util.Arrays; +import java.util.List; + +import javax.annotation.Nullable; +import javax.annotation.ParametersAreNonnullByDefault; + +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; + +import gregtech.api.GregTech_API; +import gregtech.api.enums.ConfigCategories; +import gregtech.api.enums.GT_Values; +import gregtech.api.recipe.RecipeMapBackend; +import gregtech.api.recipe.RecipeMapBackendPropertiesBuilder; +import gregtech.api.util.GT_ModHandler; +import gregtech.api.util.GT_Recipe; +import gregtech.api.util.MethodsReturnNonnullByDefault; + +@SuppressWarnings({ "unused", "UnusedReturnValue" }) +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +public class LargeBoilerFuelBackend extends RecipeMapBackend { + + private static boolean addedGeneralDesc = false; + + private static final List<String> ALLOWED_SOLID_FUELS = Arrays.asList( + GregTech_API.sMachineFile.mConfig.getStringList( + "LargeBoiler.allowedFuels", + ConfigCategories.machineconfig.toString(), + new String[] { "gregtech:gt.blockreinforced:6", "gregtech:gt.blockreinforced:7" }, + "Allowed fuels for the Large Titanium Boiler and Large Tungstensteel Boiler")); + + public LargeBoilerFuelBackend(RecipeMapBackendPropertiesBuilder propertiesBuilder) { + super(propertiesBuilder); + } + + public static boolean isAllowedSolidFuel(ItemStack stack) { + return isAllowedSolidFuel(Item.itemRegistry.getNameForObject(stack.getItem()), stack.getItemDamage()); + } + + public static boolean isAllowedSolidFuel(String itemRegistryName, int meta) { + return ALLOWED_SOLID_FUELS.contains(itemRegistryName + ":" + meta); + } + + public static boolean addAllowedSolidFuel(ItemStack stack) { + return addAllowedSolidFuel(Item.itemRegistry.getNameForObject(stack.getItem()), stack.getItemDamage()); + } + + public static boolean addAllowedSolidFuel(String itemregistryName, int meta) { + return ALLOWED_SOLID_FUELS.add(itemregistryName + ":" + meta); + } + + public GT_Recipe addDenseLiquidRecipe(GT_Recipe recipe) { + return addRecipe(recipe, ((double) recipe.mSpecialValue) / 10, false); + } + + public GT_Recipe addDieselRecipe(GT_Recipe recipe) { + return addRecipe(recipe, ((double) recipe.mSpecialValue) / 40, false); + } + + public void addSolidRecipes(ItemStack... itemStacks) { + for (ItemStack itemStack : itemStacks) { + addSolidRecipe(itemStack); + } + } + + @Nullable + public GT_Recipe addSolidRecipe(@Nullable ItemStack fuelItemStack) { + if (fuelItemStack == null) { + return null; + } + if (!addedGeneralDesc) { + GT_Values.RA.stdBuilder() + .duration(1) + .eut(1) + .specialValue(1) + .setNEIDesc( + "Not all solid fuels are listed.", + "Any item that burns in a", + "vanilla furnace will burn in", + "a Large Bronze or Steel Boiler.") + .build() + .map(this::compileRecipe); + addedGeneralDesc = true; + } + + String registryName = Item.itemRegistry.getNameForObject(fuelItemStack.getItem()); + boolean isHighTierAllowed = ALLOWED_SOLID_FUELS.contains(registryName + ":" + fuelItemStack.getItemDamage()); + return GT_Values.RA.stdBuilder() + .itemInputs(fuelItemStack) + .duration(1) + .eut(0) + .specialValue(GT_ModHandler.getFuelValue(fuelItemStack) / 1600) + .build() + .map(r -> addRecipe(r, ((double) GT_ModHandler.getFuelValue(fuelItemStack)) / 1600, isHighTierAllowed)) + .orElse(null); + } + + private GT_Recipe addRecipe(GT_Recipe recipe, double baseBurnTime, boolean isHighTierAllowed) { + // Some recipes will have a burn time like 15.9999999 and % always rounds down + double floatErrorCorrection = 0.0001; + + double bronzeBurnTime = baseBurnTime * 2 + floatErrorCorrection; + bronzeBurnTime -= bronzeBurnTime % 0.05; + double steelBurnTime = baseBurnTime + floatErrorCorrection; + steelBurnTime -= steelBurnTime % 0.05; + double titaniumBurnTime = baseBurnTime * 0.3 + floatErrorCorrection; + titaniumBurnTime -= titaniumBurnTime % 0.05; + double tungstensteelBurnTime = baseBurnTime * 0.15 + floatErrorCorrection; + tungstensteelBurnTime -= tungstensteelBurnTime % 0.05; + + if (isHighTierAllowed) { + recipe.setNeiDesc( + "Burn time in seconds:", + String.format("Bronze Boiler: %.4f", bronzeBurnTime), + String.format("Steel Boiler: %.4f", steelBurnTime), + String.format("Titanium Boiler: %.4f", titaniumBurnTime), + String.format("Tungstensteel Boiler: %.4f", tungstensteelBurnTime)); + } else { + recipe.setNeiDesc( + "Burn time in seconds:", + String.format("Bronze Boiler: %.4f", bronzeBurnTime), + String.format("Steel Boiler: %.4f", steelBurnTime), + "Titanium Boiler: Not allowed", + "Tungstenst. Boiler: Not allowed"); + } + + return compileRecipe(recipe); + } +} diff --git a/src/main/java/gregtech/api/recipe/maps/LargeBoilerFuelFrontend.java b/src/main/java/gregtech/api/recipe/maps/LargeBoilerFuelFrontend.java new file mode 100644 index 0000000000..dbe7f6fe2f --- /dev/null +++ b/src/main/java/gregtech/api/recipe/maps/LargeBoilerFuelFrontend.java @@ -0,0 +1,25 @@ +package gregtech.api.recipe.maps; + +import javax.annotation.ParametersAreNonnullByDefault; + +import gregtech.api.recipe.BasicUIPropertiesBuilder; +import gregtech.api.recipe.NEIRecipePropertiesBuilder; +import gregtech.api.recipe.RecipeMapFrontend; +import gregtech.api.util.MethodsReturnNonnullByDefault; +import gregtech.nei.RecipeDisplayInfo; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +public class LargeBoilerFuelFrontend extends RecipeMapFrontend { + + public LargeBoilerFuelFrontend(BasicUIPropertiesBuilder uiPropertiesBuilder, + NEIRecipePropertiesBuilder neiPropertiesBuilder) { + super(uiPropertiesBuilder, neiPropertiesBuilder); + } + + @Override + protected void drawEnergyInfo(RecipeDisplayInfo recipeInfo) {} + + @Override + protected void drawDurationInfo(RecipeDisplayInfo recipeInfo) {} +} diff --git a/src/main/java/gregtech/api/recipe/maps/LargeNEIFrontend.java b/src/main/java/gregtech/api/recipe/maps/LargeNEIFrontend.java new file mode 100644 index 0000000000..70184a83de --- /dev/null +++ b/src/main/java/gregtech/api/recipe/maps/LargeNEIFrontend.java @@ -0,0 +1,65 @@ +package gregtech.api.recipe.maps; + +import java.util.List; + +import javax.annotation.ParametersAreNonnullByDefault; + +import com.gtnewhorizons.modularui.api.math.Pos2d; +import com.gtnewhorizons.modularui.api.math.Size; + +import gregtech.api.recipe.BasicUIPropertiesBuilder; +import gregtech.api.recipe.NEIRecipePropertiesBuilder; +import gregtech.api.recipe.RecipeMapFrontend; +import gregtech.api.util.MethodsReturnNonnullByDefault; +import gregtech.common.gui.modularui.UIHelper; + +/** + * Nicely display NEI with many items and fluids. Remember to call + * If row count >= 6, it doesn't fit in 2 recipes per page, so change it via IMC. + */ +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +public class LargeNEIFrontend extends RecipeMapFrontend { + + private static final int xDirMaxCount = 3; + private static final int yOrigin = 8; + + private final int itemRowCount; + private final int fluidRowCount; + + public LargeNEIFrontend(BasicUIPropertiesBuilder uiPropertiesBuilder, + NEIRecipePropertiesBuilder neiPropertiesBuilder) { + super(uiPropertiesBuilder.logoPos(new Pos2d(80, 62)), neiPropertiesBuilder); + this.itemRowCount = getItemRowCount(); + this.fluidRowCount = getFluidRowCount(); + neiProperties.recipeBackgroundSize = new Size(170, 82 + (Math.max(itemRowCount + fluidRowCount - 4, 0)) * 18); + } + + @Override + public List<Pos2d> getItemInputPositions(int itemInputCount) { + return UIHelper.getGridPositions(itemInputCount, 16, yOrigin, xDirMaxCount); + } + + @Override + public List<Pos2d> getItemOutputPositions(int itemOutputCount) { + return UIHelper.getGridPositions(itemOutputCount, 106, yOrigin, xDirMaxCount); + } + + @Override + public List<Pos2d> getFluidInputPositions(int fluidInputCount) { + return UIHelper.getGridPositions(fluidInputCount, 16, yOrigin + itemRowCount * 18, xDirMaxCount); + } + + @Override + public List<Pos2d> getFluidOutputPositions(int fluidOutputCount) { + return UIHelper.getGridPositions(fluidOutputCount, 106, yOrigin + itemRowCount * 18, xDirMaxCount); + } + + private int getItemRowCount() { + return (Math.max(uiProperties.maxItemInputs, uiProperties.maxItemOutputs) - 1) / xDirMaxCount + 1; + } + + private int getFluidRowCount() { + return (Math.max(uiProperties.maxFluidInputs, uiProperties.maxFluidOutputs) - 1) / xDirMaxCount + 1; + } +} diff --git a/src/main/java/gregtech/api/recipe/maps/MicrowaveBackend.java b/src/main/java/gregtech/api/recipe/maps/MicrowaveBackend.java new file mode 100644 index 0000000000..53623cb0c7 --- /dev/null +++ b/src/main/java/gregtech/api/recipe/maps/MicrowaveBackend.java @@ -0,0 +1,145 @@ +package gregtech.api.recipe.maps; + +import static gregtech.api.enums.GT_Values.W; +import static gregtech.api.util.GT_RecipeConstants.EXPLODE; +import static gregtech.api.util.GT_RecipeConstants.ON_FIRE; + +import javax.annotation.Nullable; +import javax.annotation.ParametersAreNonnullByDefault; + +import net.minecraft.init.Blocks; +import net.minecraft.init.Items; +import net.minecraft.item.ItemStack; +import net.minecraft.tileentity.TileEntityFurnace; +import net.minecraftforge.fluids.FluidStack; + +import gregtech.api.enums.GT_Values; +import gregtech.api.enums.ItemList; +import gregtech.api.enums.SubTag; +import gregtech.api.objects.ItemData; +import gregtech.api.objects.MaterialStack; +import gregtech.api.recipe.RecipeMapBackendPropertiesBuilder; +import gregtech.api.util.GT_Log; +import gregtech.api.util.GT_ModHandler; +import gregtech.api.util.GT_OreDictUnificator; +import gregtech.api.util.GT_Recipe; +import gregtech.api.util.GT_RecipeBuilder; +import gregtech.api.util.GT_Utility; +import gregtech.api.util.MethodsReturnNonnullByDefault; + +/** + * Special Class for Microwave Recipe handling. + */ +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +public class MicrowaveBackend extends NonGTBackend { + + public MicrowaveBackend(RecipeMapBackendPropertiesBuilder propertiesBuilder) { + super(propertiesBuilder); + } + + @Override + protected GT_Recipe overwriteFindRecipe(ItemStack[] items, FluidStack[] fluids, @Nullable ItemStack specialSlot, + @Nullable GT_Recipe cachedRecipe) { + if (items.length == 0 || items[0] == null) { + return null; + } + if (cachedRecipe != null && cachedRecipe.isRecipeInputEqual(false, true, fluids, items)) { + return cachedRecipe; + } + + ItemStack output = GT_ModHandler.getSmeltingOutput(items[0], false, null); + + if (GT_Utility.areStacksEqual(items[0], new ItemStack(Items.book, 1, W))) { + return GT_Values.RA.stdBuilder() + .itemInputs(GT_Utility.copyAmount(1, items[0])) + .itemOutputs(GT_Utility.getWrittenBook("Manual_Microwave", ItemList.Book_Written_03.get(1))) + .duration(32) + .eut(4) + .noOptimize() + .build() + .orElse(null); + } + + // Check Container Item of Input since it is around the Input, then the Input itself, then Container Item of + // Output and last check the Output itself + for (ItemStack item : new ItemStack[] { GT_Utility.getContainerItem(items[0], true), items[0], + GT_Utility.getContainerItem(output, true), output }) { + if (item == null) continue; + if (GT_Utility.areStacksEqual(item, new ItemStack(Blocks.netherrack, 1, W), true) + || GT_Utility.areStacksEqual(item, new ItemStack(Blocks.tnt, 1, W), true) + || GT_Utility.areStacksEqual(item, new ItemStack(Items.egg, 1, W), true) + || GT_Utility.areStacksEqual(item, new ItemStack(Items.firework_charge, 1, W), true) + || GT_Utility.areStacksEqual(item, new ItemStack(Items.fireworks, 1, W), true) + || GT_Utility.areStacksEqual(item, new ItemStack(Items.fire_charge, 1, W), true)) { + GT_Log.exp + .println("Microwave Explosion due to TNT || EGG || FIREWORKCHARGE || FIREWORK || FIRE CHARGE"); + return GT_RecipeBuilder.empty() + .metadata(EXPLODE, true) + .build() + .orElse(null); + } + + ItemData itemData = GT_OreDictUnificator.getItemData(item); + if (itemData != null) { + if (itemData.mMaterial != null && itemData.mMaterial.mMaterial != null) { + if (itemData.mMaterial.mMaterial.contains(SubTag.METAL) + || itemData.mMaterial.mMaterial.contains(SubTag.EXPLOSIVE)) { + GT_Log.exp.println("Microwave Explosion due to METAL insertion"); + return GT_RecipeBuilder.empty() + .metadata(EXPLODE, true) + .build() + .orElse(null); + } + if (itemData.mMaterial.mMaterial.contains(SubTag.FLAMMABLE)) { + GT_Log.exp.println("Microwave INFLAMMATION due to FLAMMABLE insertion"); + return GT_RecipeBuilder.empty() + .metadata(ON_FIRE, true) + .build() + .orElse(null); + } + } + for (MaterialStack materialStack : itemData.mByProducts) { + if (materialStack == null) continue; + if (materialStack.mMaterial.contains(SubTag.METAL) + || materialStack.mMaterial.contains(SubTag.EXPLOSIVE)) { + GT_Log.exp.println("Microwave Explosion due to METAL insertion"); + return GT_RecipeBuilder.empty() + .metadata(EXPLODE, true) + .build() + .orElse(null); + } + if (materialStack.mMaterial.contains(SubTag.FLAMMABLE)) { + GT_Log.exp.println("Microwave INFLAMMATION due to FLAMMABLE insertion"); + return GT_RecipeBuilder.empty() + .metadata(ON_FIRE, true) + .build() + .orElse(null); + } + } + } + if (TileEntityFurnace.getItemBurnTime(item) > 0) { + GT_Log.exp.println("Microwave INFLAMMATION due to BURNABLE insertion"); + return GT_RecipeBuilder.empty() + .metadata(ON_FIRE, true) + .build() + .orElse(null); + } + } + + return output == null ? null + : GT_Values.RA.stdBuilder() + .itemInputs(GT_Utility.copyAmount(1, items[0])) + .itemOutputs(output) + .duration(32) + .eut(4) + .noOptimize() + .build() + .orElse(null); + } + + @Override + public boolean containsInput(ItemStack item) { + return GT_ModHandler.getSmeltingOutput(item, false, null) != null; + } +} diff --git a/src/main/java/gregtech/api/recipe/maps/NonGTBackend.java b/src/main/java/gregtech/api/recipe/maps/NonGTBackend.java new file mode 100644 index 0000000000..1e871df372 --- /dev/null +++ b/src/main/java/gregtech/api/recipe/maps/NonGTBackend.java @@ -0,0 +1,52 @@ +package gregtech.api.recipe.maps; + +import javax.annotation.Nullable; +import javax.annotation.ParametersAreNonnullByDefault; + +import net.minecraft.item.ItemStack; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidStack; + +import gregtech.api.recipe.RecipeMapBackend; +import gregtech.api.recipe.RecipeMapBackendPropertiesBuilder; +import gregtech.api.util.GT_Recipe; +import gregtech.api.util.MethodsReturnNonnullByDefault; + +/** + * Abstract class for general recipe handling of non GT recipes + */ +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +public abstract class NonGTBackend extends RecipeMapBackend { + + public NonGTBackend(RecipeMapBackendPropertiesBuilder propertiesBuilder) { + super(propertiesBuilder); + } + + @Override + protected abstract GT_Recipe overwriteFindRecipe(ItemStack[] items, FluidStack[] fluids, + @Nullable ItemStack specialSlot, @Nullable GT_Recipe cachedRecipe); + + @Override + protected boolean doesOverwriteFindRecipe() { + return true; + } + + @Override + public boolean containsInput(ItemStack item) { + return false; + } + + @Override + public boolean containsInput(Fluid fluid) { + return false; + } + + @Override + public void reInit() {} + + @Override + protected GT_Recipe addToItemMap(GT_Recipe recipe) { + return recipe; + } +} diff --git a/src/main/java/gregtech/api/recipe/maps/OilCrackerBackend.java b/src/main/java/gregtech/api/recipe/maps/OilCrackerBackend.java new file mode 100644 index 0000000000..c2c312a48a --- /dev/null +++ b/src/main/java/gregtech/api/recipe/maps/OilCrackerBackend.java @@ -0,0 +1,41 @@ +package gregtech.api.recipe.maps; + +import java.util.HashSet; +import java.util.Set; + +import javax.annotation.ParametersAreNonnullByDefault; + +import net.minecraftforge.fluids.FluidStack; + +import gregtech.api.recipe.RecipeMapBackend; +import gregtech.api.recipe.RecipeMapBackendPropertiesBuilder; +import gregtech.api.util.GT_Recipe; +import gregtech.api.util.MethodsReturnNonnullByDefault; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +public class OilCrackerBackend extends RecipeMapBackend { + + private final Set<String> validCatalystFluidNames = new HashSet<>(); + + public OilCrackerBackend(RecipeMapBackendPropertiesBuilder propertiesBuilder) { + super(propertiesBuilder); + } + + @Override + public GT_Recipe compileRecipe(GT_Recipe recipe) { + super.compileRecipe(recipe); + if (recipe.mFluidInputs != null && recipe.mFluidInputs.length > 1 && recipe.mFluidInputs[1] != null) { + validCatalystFluidNames.add( + recipe.mFluidInputs[1].getFluid() + .getName()); + } + return recipe; + } + + public boolean isValidCatalystFluid(FluidStack fluid) { + return validCatalystFluidNames.contains( + fluid.getFluid() + .getName()); + } +} diff --git a/src/main/java/gregtech/api/recipe/maps/PrinterBackend.java b/src/main/java/gregtech/api/recipe/maps/PrinterBackend.java new file mode 100644 index 0000000000..a933886447 --- /dev/null +++ b/src/main/java/gregtech/api/recipe/maps/PrinterBackend.java @@ -0,0 +1,142 @@ +package gregtech.api.recipe.maps; + +import static gregtech.api.enums.GT_Values.L; + +import javax.annotation.Nullable; +import javax.annotation.ParametersAreNonnullByDefault; + +import net.minecraft.init.Items; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidStack; + +import gregtech.api.enums.Dyes; +import gregtech.api.enums.GT_Values; +import gregtech.api.enums.ItemList; +import gregtech.api.recipe.RecipeMapBackend; +import gregtech.api.recipe.RecipeMapBackendPropertiesBuilder; +import gregtech.api.util.GT_ModHandler; +import gregtech.api.util.GT_Recipe; +import gregtech.api.util.GT_Utility; +import gregtech.api.util.MethodsReturnNonnullByDefault; + +/** + * Special Class for Printer handling. + */ +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +public class PrinterBackend extends RecipeMapBackend { + + public PrinterBackend(RecipeMapBackendPropertiesBuilder propertiesBuilder) { + super(propertiesBuilder); + } + + @Override + protected GT_Recipe modifyFoundRecipe(GT_Recipe recipe, ItemStack[] items, FluidStack[] fluids, + @Nullable ItemStack specialSlot) { + if (items[0].getItem() == Items.paper) { + assert specialSlot != null; + if (!ItemList.Tool_DataStick.isStackEqual(specialSlot, false, true)) return null; + NBTTagCompound nbt = specialSlot.getTagCompound(); + if (nbt == null || GT_Utility.isStringInvalid(nbt.getString("title")) + || GT_Utility.isStringInvalid(nbt.getString("author"))) return null; + + recipe = recipe.copy(); + recipe.mCanBeBuffered = false; + recipe.mOutputs[0].setTagCompound(nbt); + return recipe; + } + if (items[0].getItem() == Items.map) { + assert specialSlot != null; + if (!ItemList.Tool_DataStick.isStackEqual(specialSlot, false, true)) return null; + NBTTagCompound nbt = specialSlot.getTagCompound(); + if (nbt == null || !nbt.hasKey("map_id")) return null; + + recipe = recipe.copy(); + recipe.mCanBeBuffered = false; + recipe.mOutputs[0].setItemDamage(nbt.getShort("map_id")); + return recipe; + } + if (ItemList.Paper_Punch_Card_Empty.isStackEqual(items[0], false, true)) { + assert specialSlot != null; + if (!ItemList.Tool_DataStick.isStackEqual(specialSlot, false, true)) return null; + NBTTagCompound nbt = specialSlot.getTagCompound(); + if (nbt == null || !nbt.hasKey("GT.PunchCardData")) return null; + + recipe = recipe.copy(); + recipe.mCanBeBuffered = false; + recipe.mOutputs[0].setTagCompound( + GT_Utility.getNBTContainingString( + new NBTTagCompound(), + "GT.PunchCardData", + nbt.getString("GT.PunchCardData"))); + return recipe; + } + return recipe; + } + + @Override + protected GT_Recipe findFallback(ItemStack[] items, FluidStack[] fluids, @Nullable ItemStack specialSlot) { + if (items.length == 0 || items[0] == null || fluids.length == 0 || fluids[0] == null) { + return null; + } + Dyes dye = null; + for (Dyes tDye : Dyes.VALUES) if (tDye.isFluidDye(fluids[0])) { + dye = tDye; + break; + } + if (dye == null) return null; + + ItemStack batchRecolorOutput = GT_ModHandler.getAllRecipeOutput( + null, + items[0], + items[0], + items[0], + items[0], + ItemList.DYE_ONLY_ITEMS[dye.mIndex].get(1), + items[0], + items[0], + items[0], + items[0]); + if (batchRecolorOutput != null) { + return GT_Values.RA.stdBuilder() + .itemInputs(GT_Utility.copyAmount(8, items[0])) + .itemOutputs(batchRecolorOutput) + .fluidInputs(new FluidStack(fluids[0].getFluid(), (int) L)) + .duration(256) + .eut(2) + .hidden() + .build() + .map(this::compileRecipe) + .orElse(null); + } + + ItemStack singleRecolorOutput = GT_ModHandler + .getAllRecipeOutput(null, items[0], ItemList.DYE_ONLY_ITEMS[dye.mIndex].get(1)); + if (singleRecolorOutput != null) { + return GT_Values.RA.stdBuilder() + .itemInputs(GT_Utility.copyAmount(1, items[0])) + .itemOutputs(singleRecolorOutput) + .fluidInputs(new FluidStack(fluids[0].getFluid(), (int) L)) + .duration(32) + .eut(2) + .hidden() + .build() + .map(this::compileRecipe) + .orElse(null); + } + + return null; + } + + @Override + public boolean containsInput(ItemStack item) { + return true; + } + + @Override + public boolean containsInput(Fluid fluid) { + return super.containsInput(fluid) || Dyes.isAnyFluidDye(fluid); + } +} diff --git a/src/main/java/gregtech/api/recipe/maps/RecyclerBackend.java b/src/main/java/gregtech/api/recipe/maps/RecyclerBackend.java new file mode 100644 index 0000000000..55fb9b4cc4 --- /dev/null +++ b/src/main/java/gregtech/api/recipe/maps/RecyclerBackend.java @@ -0,0 +1,55 @@ +package gregtech.api.recipe.maps; + +import javax.annotation.Nullable; +import javax.annotation.ParametersAreNonnullByDefault; + +import net.minecraft.item.ItemStack; +import net.minecraftforge.fluids.FluidStack; + +import gregtech.api.enums.GT_Values; +import gregtech.api.recipe.RecipeMapBackendPropertiesBuilder; +import gregtech.api.util.GT_ModHandler; +import gregtech.api.util.GT_Recipe; +import gregtech.api.util.GT_RecipeBuilder; +import gregtech.api.util.GT_Utility; +import gregtech.api.util.MethodsReturnNonnullByDefault; + +/** + * Special Class for Recycler Recipe handling. + */ +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +public class RecyclerBackend extends NonGTBackend { + + public RecyclerBackend(RecipeMapBackendPropertiesBuilder propertiesBuilder) { + super(propertiesBuilder); + } + + @Override + protected GT_Recipe overwriteFindRecipe(ItemStack[] items, FluidStack[] fluids, @Nullable ItemStack specialSlot, + @Nullable GT_Recipe cachedRecipe) { + if (items.length == 0 || items[0] == null) { + return null; + } + if (cachedRecipe != null && cachedRecipe.isRecipeInputEqual(false, true, fluids, items)) { + return cachedRecipe; + } + GT_RecipeBuilder builder = GT_Values.RA.stdBuilder() + .itemInputs(GT_Utility.copyAmount(1, items[0])); + ItemStack output = GT_ModHandler.getRecyclerOutput(items[0], 0); + if (output != null) { + builder.itemOutputs(output) + .outputChances(1250); + } + return builder.duration(45) + .eut(1) + .noOptimize() + .build() + .orElse(null); + } + + @Override + public boolean containsInput(ItemStack item) { + return GT_ModHandler.getRecyclerOutput(item, 0) != null; + } +} diff --git a/src/main/java/gregtech/api/recipe/maps/ReplicatorBackend.java b/src/main/java/gregtech/api/recipe/maps/ReplicatorBackend.java new file mode 100644 index 0000000000..f201698457 --- /dev/null +++ b/src/main/java/gregtech/api/recipe/maps/ReplicatorBackend.java @@ -0,0 +1,100 @@ +package gregtech.api.recipe.maps; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +import javax.annotation.Nullable; +import javax.annotation.ParametersAreNonnullByDefault; + +import net.minecraft.item.ItemStack; +import net.minecraftforge.fluids.FluidStack; + +import gregtech.GT_Mod; +import gregtech.api.enums.Element; +import gregtech.api.enums.ItemList; +import gregtech.api.enums.Materials; +import gregtech.api.enums.TierEU; +import gregtech.api.recipe.RecipeMapBackend; +import gregtech.api.recipe.RecipeMapBackendPropertiesBuilder; +import gregtech.api.util.GT_Recipe; +import gregtech.api.util.GT_RecipeBuilder; +import gregtech.api.util.GT_RecipeConstants; +import gregtech.api.util.GT_Utility; +import gregtech.api.util.MethodsReturnNonnullByDefault; +import gregtech.common.items.behaviors.Behaviour_DataOrb; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +public class ReplicatorBackend extends RecipeMapBackend { + + private final Map<Materials, GT_Recipe> recipesByMaterial = new HashMap<>(); + + public ReplicatorBackend(RecipeMapBackendPropertiesBuilder propertiesBuilder) { + super(propertiesBuilder.recipeEmitter(ReplicatorBackend::replicatorRecipeEmitter)); + } + + @Override + public GT_Recipe compileRecipe(GT_Recipe recipe) { + super.compileRecipe(recipe); + Materials material = recipe.getMetadata(GT_RecipeConstants.MATERIAL); + assert material != null; // checked by replicatorRecipeEmitter + recipesByMaterial.put(material, recipe); + return recipe; + } + + @Override + protected boolean doesOverwriteFindRecipe() { + return true; + } + + @Override + protected GT_Recipe overwriteFindRecipe(ItemStack[] items, FluidStack[] fluids, @Nullable ItemStack specialSlot, + @Nullable GT_Recipe cachedRecipe) { + if (specialSlot == null) { + return null; + } + Materials foundMaterial = getMaterialFromDataOrb(specialSlot); + if (foundMaterial == null) { + return null; + } + return recipesByMaterial.getOrDefault(foundMaterial, null); + } + + @Nullable + private static Materials getMaterialFromDataOrb(ItemStack stack) { + if (ItemList.Tool_DataOrb.isStackEqual(stack, false, true) && Behaviour_DataOrb.getDataTitle(stack) + .equals("Elemental-Scan")) { + return Element.get(Behaviour_DataOrb.getDataName(stack)).mLinkedMaterials.stream() + .findFirst() + .orElse(null); + } + return null; + } + + private static Collection<GT_Recipe> replicatorRecipeEmitter(GT_RecipeBuilder builder) { + Materials material = builder.getMetadata(GT_RecipeConstants.MATERIAL); + if (material == null) { + throw new IllegalStateException("GT_RecipeConstants.MATERIAL must be set for replicator recipe"); + } + return Optional.of(material) + .map(material1 -> material1.mElement) + .map(Element::getMass) + .map(ReplicatorBackend::getUUMAmountFromMass) + .flatMap( + uum -> builder.fluidInputs(Materials.UUMatter.getFluid(uum)) + .duration(GT_Utility.safeInt(uum * 512L, 1)) + .eut(TierEU.RECIPE_LV) + .ignoreCollision() + .noOptimize() + .build()) + .map(Collections::singletonList) + .orElse(Collections.emptyList()); + } + + private static int getUUMAmountFromMass(long mass) { + return GT_Utility.safeInt((long) Math.pow(mass, GT_Mod.gregtechproxy.replicatorExponent), 1); + } +} diff --git a/src/main/java/gregtech/api/recipe/maps/SpaceProjectFrontend.java b/src/main/java/gregtech/api/recipe/maps/SpaceProjectFrontend.java new file mode 100644 index 0000000000..98463dcc4d --- /dev/null +++ b/src/main/java/gregtech/api/recipe/maps/SpaceProjectFrontend.java @@ -0,0 +1,131 @@ +package gregtech.api.recipe.maps; + +import static gregtech.api.util.GT_Utility.formatNumbers; +import static net.minecraft.util.EnumChatFormatting.GRAY; +import static net.minecraft.util.StatCollector.translateToLocal; + +import java.util.List; +import java.util.function.Supplier; + +import javax.annotation.ParametersAreNonnullByDefault; + +import net.minecraft.util.EnumChatFormatting; + +import com.gtnewhorizons.modularui.api.drawable.IDrawable; +import com.gtnewhorizons.modularui.api.forge.IItemHandlerModifiable; +import com.gtnewhorizons.modularui.api.math.Alignment; +import com.gtnewhorizons.modularui.api.math.Pos2d; +import com.gtnewhorizons.modularui.api.screen.ModularWindow; +import com.gtnewhorizons.modularui.common.widget.DrawableWidget; +import com.gtnewhorizons.modularui.common.widget.ProgressBar; + +import appeng.util.ReadableNumberConverter; +import codechicken.lib.gui.GuiDraw; +import codechicken.nei.PositionedStack; +import gregtech.api.gui.modularui.GT_UITextures; +import gregtech.api.recipe.BasicUIPropertiesBuilder; +import gregtech.api.recipe.NEIRecipePropertiesBuilder; +import gregtech.api.recipe.RecipeMapFrontend; +import gregtech.api.util.MethodsReturnNonnullByDefault; +import gregtech.common.gui.modularui.UIHelper; +import gregtech.common.misc.spaceprojects.SpaceProjectManager; +import gregtech.common.misc.spaceprojects.SpaceProjectManager.FakeSpaceProjectRecipe; +import gregtech.common.misc.spaceprojects.interfaces.ISpaceProject; +import gregtech.nei.GT_NEI_DefaultHandler; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +public class SpaceProjectFrontend extends RecipeMapFrontend { + + IDrawable projectTexture; + + public SpaceProjectFrontend(BasicUIPropertiesBuilder uiPropertiesBuilder, + NEIRecipePropertiesBuilder neiPropertiesBuilder) { + super(uiPropertiesBuilder, neiPropertiesBuilder); + } + + @Override + public ModularWindow.Builder createNEITemplate(IItemHandlerModifiable itemInputsInventory, + IItemHandlerModifiable itemOutputsInventory, IItemHandlerModifiable specialSlotInventory, + IItemHandlerModifiable fluidInputsInventory, IItemHandlerModifiable fluidOutputsInventory, + Supplier<Float> progressSupplier, Pos2d windowOffset) { + ModularWindow.Builder builder = super.createNEITemplate( + itemInputsInventory, + itemOutputsInventory, + specialSlotInventory, + fluidInputsInventory, + fluidOutputsInventory, + progressSupplier, + windowOffset); + builder.widget( + new DrawableWidget().setDrawable(() -> projectTexture) + .setSize(18, 18) + .setPos(new Pos2d(124, 28).add(windowOffset))); + return builder; + } + + @Override + public List<Pos2d> getItemInputPositions(int itemInputCount) { + return UIHelper.getGridPositions(itemInputCount, 16, 28, 3); + } + + @Override + public List<Pos2d> getFluidInputPositions(int fluidInputCount) { + return UIHelper.getGridPositions(fluidInputCount, 88, 28, 1); + } + + @Override + protected List<String> handleNEIItemInputTooltip(List<String> currentTip, + GT_NEI_DefaultHandler.FixedPositionedStack pStack) { + super.handleNEIItemOutputTooltip(currentTip, pStack); + if (pStack.isFluid()) return currentTip; + currentTip.add(GRAY + translateToLocal("Item Count: ") + formatNumbers(pStack.realStackSize)); + return currentTip; + } + + @Override + public void drawNEIOverlays(GT_NEI_DefaultHandler.CachedDefaultRecipe neiCachedRecipe) { + for (PositionedStack stack : neiCachedRecipe.mInputs) { + if (stack instanceof GT_NEI_DefaultHandler.FixedPositionedStack pStack && stack.item != null + && !pStack.isFluid()) { + int stackSize = ((GT_NEI_DefaultHandler.FixedPositionedStack) stack).realStackSize; + String displayString; + if (stack.item.stackSize > 9999) { + displayString = ReadableNumberConverter.INSTANCE.toWideReadableForm(stackSize); + } else { + displayString = String.valueOf(stackSize); + } + drawNEIOverlayText(displayString, stack, 0xffffff, 0.5f, true, Alignment.BottomRight); + } + } + if (neiCachedRecipe.mRecipe instanceof FakeSpaceProjectRecipe) { + ISpaceProject project = SpaceProjectManager + .getProject(((FakeSpaceProjectRecipe) neiCachedRecipe.mRecipe).projectName); + if (project != null) { + projectTexture = project.getTexture(); + GuiDraw.drawStringC(EnumChatFormatting.BOLD + project.getLocalizedName(), 85, 0, 0x404040, false); + } + } + } + + @Override + public void addProgressBar(ModularWindow.Builder builder, Supplier<Float> progressSupplier, Pos2d windowOffset) { + int bar1Width = 17; + int bar2Width = 18; + List<Supplier<Float>> splitProgress = splitProgress(progressSupplier, bar1Width, bar2Width); + builder.widget( + new ProgressBar().setTexture(GT_UITextures.PROGRESSBAR_ASSEMBLY_LINE_1, 17) + .setDirection(ProgressBar.Direction.RIGHT) + .setProgress(splitProgress.get(0)) + .setSynced(false, false) + .setPos(new Pos2d(70, 28).add(windowOffset)) + .setSize(bar1Width, 72)); + builder.widget( + new ProgressBar().setTexture(GT_UITextures.PROGRESSBAR_ASSEMBLY_LINE_2, 18) + .setDirection(ProgressBar.Direction.RIGHT) + .setProgress(splitProgress.get(1)) + .setSynced(false, false) + .setPos(new Pos2d(106, 28).add(windowOffset)) + .setSize(bar2Width, 72)); + } +} diff --git a/src/main/java/gregtech/api/recipe/maps/TranscendentPlasmaMixerFrontend.java b/src/main/java/gregtech/api/recipe/maps/TranscendentPlasmaMixerFrontend.java new file mode 100644 index 0000000000..7a5d7ff164 --- /dev/null +++ b/src/main/java/gregtech/api/recipe/maps/TranscendentPlasmaMixerFrontend.java @@ -0,0 +1,56 @@ +package gregtech.api.recipe.maps; + +import static gregtech.api.util.GT_Utility.formatNumbers; + +import java.util.List; + +import javax.annotation.ParametersAreNonnullByDefault; + +import com.gtnewhorizons.modularui.api.math.Pos2d; + +import gregtech.api.recipe.BasicUIPropertiesBuilder; +import gregtech.api.recipe.NEIRecipePropertiesBuilder; +import gregtech.api.recipe.RecipeMapFrontend; +import gregtech.api.util.GT_Utility; +import gregtech.api.util.MethodsReturnNonnullByDefault; +import gregtech.common.gui.modularui.UIHelper; +import gregtech.nei.RecipeDisplayInfo; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +public class TranscendentPlasmaMixerFrontend extends RecipeMapFrontend { + + public TranscendentPlasmaMixerFrontend(BasicUIPropertiesBuilder uiPropertiesBuilder, + NEIRecipePropertiesBuilder neiPropertiesBuilder) { + super(uiPropertiesBuilder, neiPropertiesBuilder); + } + + @Override + public List<Pos2d> getItemInputPositions(int itemInputCount) { + return UIHelper.getGridPositions(itemInputCount, 60, 8, 1); + } + + @Override + public List<Pos2d> getFluidInputPositions(int fluidInputCount) { + return UIHelper.getGridPositions(fluidInputCount, 6, 26, 4, 5); + } + + @Override + public List<Pos2d> getFluidOutputPositions(int fluidOutputCount) { + return UIHelper.getGridPositions(fluidOutputCount, 114, 44, 1); + } + + @Override + protected void drawEnergyInfo(RecipeDisplayInfo recipeInfo) { + // These look odd because recipeInfo.recipe.mEUt is actually the EU per litre of fluid processed, not + // the EU/t. + recipeInfo.drawText( + GT_Utility.trans("152", "Total: ") + + formatNumbers(1000L * recipeInfo.recipe.mDuration / 100L * recipeInfo.recipe.mEUt) + + " EU"); + // 1000 / (20 ticks * 5 seconds) = 10L/t. 10L/t * x EU/L = 10 * x EU/t. + long averageUsage = 10L * recipeInfo.recipe.mEUt; + recipeInfo.drawText( + "Average: " + formatNumbers(averageUsage) + " EU/t" + GT_Utility.getTierNameWithParentheses(averageUsage)); + } +} diff --git a/src/main/java/gregtech/api/recipe/maps/UnpackagerBackend.java b/src/main/java/gregtech/api/recipe/maps/UnpackagerBackend.java new file mode 100644 index 0000000000..e7297f0609 --- /dev/null +++ b/src/main/java/gregtech/api/recipe/maps/UnpackagerBackend.java @@ -0,0 +1,53 @@ +package gregtech.api.recipe.maps; + +import javax.annotation.Nullable; +import javax.annotation.ParametersAreNonnullByDefault; + +import net.minecraft.item.ItemStack; +import net.minecraftforge.fluids.FluidStack; + +import gregtech.api.enums.GT_Values; +import gregtech.api.enums.ItemList; +import gregtech.api.recipe.RecipeMapBackend; +import gregtech.api.recipe.RecipeMapBackendPropertiesBuilder; +import gregtech.api.util.GT_ModHandler; +import gregtech.api.util.GT_Recipe; +import gregtech.api.util.MethodsReturnNonnullByDefault; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +public class UnpackagerBackend extends RecipeMapBackend { + + public UnpackagerBackend(RecipeMapBackendPropertiesBuilder propertiesBuilder) { + super(propertiesBuilder); + } + + @Override + protected GT_Recipe findFallback(ItemStack[] items, FluidStack[] fluids, @Nullable ItemStack specialSlot) { + if (items.length == 0 || !ItemList.IC2_Scrapbox.isStackEqual(items[0], false, true)) { + return null; + } + + ItemStack output = GT_ModHandler.getRandomScrapboxDrop(); + if (output == null) { + return null; + } + return GT_Values.RA.stdBuilder() + .itemInputs(ItemList.IC2_Scrapbox.get(1)) + .itemOutputs(output) + .duration(16) + .eut(1) + // It is not allowed to be buffered due to the random Output + .noBuffer() + // Due to its randomness it is not good if there are Items in the Output Slot, because those Items could + // manipulate the outcome. + .needsEmptyOutput() + .build() + .orElse(null); + } + + @Override + public boolean containsInput(ItemStack item) { + return ItemList.IC2_Scrapbox.isStackEqual(item, false, true) || super.containsInput(item); + } +} diff --git a/src/main/java/gregtech/api/recipe/metadata/EmptyRecipeMetadataStorage.java b/src/main/java/gregtech/api/recipe/metadata/EmptyRecipeMetadataStorage.java new file mode 100644 index 0000000000..f7831f1485 --- /dev/null +++ b/src/main/java/gregtech/api/recipe/metadata/EmptyRecipeMetadataStorage.java @@ -0,0 +1,50 @@ +package gregtech.api.recipe.metadata; + +import java.util.Collections; +import java.util.Map; +import java.util.Set; + +import javax.annotation.Nullable; +import javax.annotation.ParametersAreNonnullByDefault; + +import org.jetbrains.annotations.Contract; + +import gregtech.api.recipe.RecipeMetadataKey; +import gregtech.api.util.MethodsReturnNonnullByDefault; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +public final class EmptyRecipeMetadataStorage implements IRecipeMetadataStorage { + + public static EmptyRecipeMetadataStorage INSTANCE = new EmptyRecipeMetadataStorage(); + + private EmptyRecipeMetadataStorage() {} + + @Override + public <T> void store(RecipeMetadataKey<T> key, @Nullable T value) { + throw new UnsupportedOperationException(); + } + + @Nullable + @Override + public <T> T getMetadata(RecipeMetadataKey<T> key) { + return null; + } + + @Contract("_, !null -> !null") + @Nullable + @Override + public <T> T getMetadataOrDefault(RecipeMetadataKey<T> key, @Nullable T defaultValue) { + return defaultValue; + } + + @Override + public Set<Map.Entry<RecipeMetadataKey<?>, Object>> getEntries() { + return Collections.emptySet(); + } + + @Override + public IRecipeMetadataStorage copy() { + throw new UnsupportedOperationException(); + } +} diff --git a/src/main/java/gregtech/api/recipe/metadata/IRecipeMetadataStorage.java b/src/main/java/gregtech/api/recipe/metadata/IRecipeMetadataStorage.java new file mode 100644 index 0000000000..52141937cf --- /dev/null +++ b/src/main/java/gregtech/api/recipe/metadata/IRecipeMetadataStorage.java @@ -0,0 +1,34 @@ +package gregtech.api.recipe.metadata; + +import java.util.Map; +import java.util.Set; + +import javax.annotation.Nullable; +import javax.annotation.ParametersAreNonnullByDefault; + +import org.jetbrains.annotations.Contract; + +import gregtech.api.recipe.RecipeMetadataKey; +import gregtech.api.util.MethodsReturnNonnullByDefault; + +/** + * Stores set of metadata for the recipe with key {@link RecipeMetadataKey}. More explicit way to store various info + * on recipe than special value or special object. Type of the metadata can be anything. + */ +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +public interface IRecipeMetadataStorage { + + <T> void store(RecipeMetadataKey<T> key, @Nullable T value); + + @Nullable + <T> T getMetadata(RecipeMetadataKey<T> key); + + @Contract("_, !null -> !null") + @Nullable + <T> T getMetadataOrDefault(RecipeMetadataKey<T> key, @Nullable T defaultValue); + + Set<Map.Entry<RecipeMetadataKey<?>, Object>> getEntries(); + + IRecipeMetadataStorage copy(); +} diff --git a/src/main/java/gregtech/api/recipe/metadata/PCBFactoryTierKey.java b/src/main/java/gregtech/api/recipe/metadata/PCBFactoryTierKey.java new file mode 100644 index 0000000000..05db919b57 --- /dev/null +++ b/src/main/java/gregtech/api/recipe/metadata/PCBFactoryTierKey.java @@ -0,0 +1,30 @@ +package gregtech.api.recipe.metadata; + +import static gregtech.api.util.GT_Utility.trans; + +import javax.annotation.Nullable; +import javax.annotation.ParametersAreNonnullByDefault; + +import gregtech.api.recipe.RecipeMetadataKey; +import gregtech.api.util.MethodsReturnNonnullByDefault; +import gregtech.nei.RecipeDisplayInfo; + +/** + * Minimum tier required for the PCB factory recipe. + */ +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +public class PCBFactoryTierKey extends RecipeMetadataKey<Integer> { + + public static final PCBFactoryTierKey INSTANCE = new PCBFactoryTierKey(); + + private PCBFactoryTierKey() { + super(Integer.class, "pcb_factory_tier"); + } + + @Override + public void drawInfo(RecipeDisplayInfo recipeInfo, @Nullable Object value) { + int tier = cast(value, 1); + recipeInfo.drawText(trans("336", "PCB Factory Tier: ") + tier); + } +} diff --git a/src/main/java/gregtech/api/recipe/metadata/PCBFactoryUpgrade.java b/src/main/java/gregtech/api/recipe/metadata/PCBFactoryUpgrade.java new file mode 100644 index 0000000000..6d76ae05c3 --- /dev/null +++ b/src/main/java/gregtech/api/recipe/metadata/PCBFactoryUpgrade.java @@ -0,0 +1,7 @@ +package gregtech.api.recipe.metadata; + +public enum PCBFactoryUpgrade { + + NONE, + BIO +} diff --git a/src/main/java/gregtech/api/recipe/metadata/PCBFactoryUpgradeKey.java b/src/main/java/gregtech/api/recipe/metadata/PCBFactoryUpgradeKey.java new file mode 100644 index 0000000000..8257f1e6ef --- /dev/null +++ b/src/main/java/gregtech/api/recipe/metadata/PCBFactoryUpgradeKey.java @@ -0,0 +1,32 @@ +package gregtech.api.recipe.metadata; + +import static gregtech.api.util.GT_Utility.trans; + +import javax.annotation.Nullable; +import javax.annotation.ParametersAreNonnullByDefault; + +import gregtech.api.recipe.RecipeMetadataKey; +import gregtech.api.util.MethodsReturnNonnullByDefault; +import gregtech.nei.RecipeDisplayInfo; + +/** + * If bio upgrade is required for the PCB factory recipe. + */ +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +public class PCBFactoryUpgradeKey extends RecipeMetadataKey<PCBFactoryUpgrade> { + + public static final PCBFactoryUpgradeKey INSTANCE = new PCBFactoryUpgradeKey(); + + private PCBFactoryUpgradeKey() { + super(PCBFactoryUpgrade.class, "pcb_factory_bio_upgrade"); + } + + @Override + public void drawInfo(RecipeDisplayInfo recipeInfo, @Nullable Object value) { + PCBFactoryUpgrade upgrade = cast(value); + if (upgrade == PCBFactoryUpgrade.BIO) { + recipeInfo.drawText(trans("337", "Upgrade Required: ") + trans("338", "Bio")); + } + } +} diff --git a/src/main/java/gregtech/api/recipe/metadata/RecipeMetadataStorage.java b/src/main/java/gregtech/api/recipe/metadata/RecipeMetadataStorage.java new file mode 100644 index 0000000000..5b65d8a600 --- /dev/null +++ b/src/main/java/gregtech/api/recipe/metadata/RecipeMetadataStorage.java @@ -0,0 +1,56 @@ +package gregtech.api.recipe.metadata; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import javax.annotation.Nullable; +import javax.annotation.ParametersAreNonnullByDefault; + +import org.jetbrains.annotations.Contract; + +import gregtech.api.recipe.RecipeMetadataKey; +import gregtech.api.util.FieldsAreNonnullByDefault; +import gregtech.api.util.MethodsReturnNonnullByDefault; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +@FieldsAreNonnullByDefault +public final class RecipeMetadataStorage implements IRecipeMetadataStorage { + + private final Map<RecipeMetadataKey<?>, Object> metadata = new HashMap<>(); + + public RecipeMetadataStorage() {} + + private RecipeMetadataStorage(Map<RecipeMetadataKey<?>, Object> metadata) { + this.metadata.putAll(metadata); + } + + @Override + public <T> void store(RecipeMetadataKey<T> key, @Nullable T value) { + metadata.put(key, key.cast(value)); + } + + @Nullable + @Override + public <T> T getMetadata(RecipeMetadataKey<T> key) { + return key.cast(metadata.get(key)); + } + + @Contract("_, !null -> !null") + @Nullable + @Override + public <T> T getMetadataOrDefault(RecipeMetadataKey<T> key, @Nullable T defaultValue) { + return key.cast(metadata.getOrDefault(key, defaultValue)); + } + + @Override + public Set<Map.Entry<RecipeMetadataKey<?>, Object>> getEntries() { + return metadata.entrySet(); + } + + @Override + public IRecipeMetadataStorage copy() { + return new RecipeMetadataStorage(metadata); + } +} diff --git a/src/main/java/gregtech/api/recipe/metadata/SimpleRecipeMetadataKey.java b/src/main/java/gregtech/api/recipe/metadata/SimpleRecipeMetadataKey.java new file mode 100644 index 0000000000..19395f11a0 --- /dev/null +++ b/src/main/java/gregtech/api/recipe/metadata/SimpleRecipeMetadataKey.java @@ -0,0 +1,27 @@ +package gregtech.api.recipe.metadata; + +import javax.annotation.Nullable; +import javax.annotation.ParametersAreNonnullByDefault; + +import gregtech.api.recipe.RecipeMetadataKey; +import gregtech.api.util.MethodsReturnNonnullByDefault; +import gregtech.nei.RecipeDisplayInfo; + +/** + * Simple metadata key that does not draw anything on NEI. + */ +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +public class SimpleRecipeMetadataKey<T> extends RecipeMetadataKey<T> { + + private SimpleRecipeMetadataKey(Class<T> clazz, String identifier) { + super(clazz, identifier); + } + + public static <T> RecipeMetadataKey<T> create(Class<T> clazz, String identifier) { + return new SimpleRecipeMetadataKey<>(clazz, identifier); + } + + @Override + public void drawInfo(RecipeDisplayInfo recipeInfo, @Nullable Object value) {} +} diff --git a/src/main/java/gregtech/api/render/TextureFactory.java b/src/main/java/gregtech/api/render/TextureFactory.java new file mode 100644 index 0000000000..26142fd606 --- /dev/null +++ b/src/main/java/gregtech/api/render/TextureFactory.java @@ -0,0 +1,157 @@ +package gregtech.api.render; + +import net.minecraft.block.Block; +import net.minecraftforge.common.util.ForgeDirection; + +import gregtech.api.interfaces.IIconContainer; +import gregtech.api.interfaces.ITexture; +import gregtech.api.interfaces.ITextureBuilder; +import gregtech.common.render.GT_TextureBuilder; + +/** + * <p> + * This class contains a collection of static factory methods to access the New Texture API. + * </p> + * <p> + * The {@link #of} methods directly returns ready-to-use instances of {@link ITexture} implementations. + * </p> + * <p> + * To get more specific implementations of {@link ITexture} instances, use the {@link #builder()} method. + * </p> + * <p> + * Example of the {@link #builder()}: + * </p> + * + * <pre> + * {@code + * // Texture that glows in the dark + * TextureFactory.builder().addIcon(OVERLAY_FUSION1_GLOW).glow().build()); + * + * // Texture with same bottom flipped orientation as vanilla + * TextureFactory.builder().addIcon(GRANITE_RED_STONE).stdOrient().build(); + * } + * </pre> + * + * See: the {@link ITextureBuilder} interface + */ +@SuppressWarnings("unused") +public final class TextureFactory { + + private TextureFactory() { + throw new AssertionError("Non-instantiable class"); + } + + /** + * Multi-layered {@link ITexture} factory + * + * @param textures The layers of {@link ITexture} from bottom to top + * @return The instance of an {@link ITexture} implementation + */ + public static ITexture of(final ITexture... textures) { + return builder().addLayer(textures) + .build(); + } + + /** + * All-Sided {@link ITexture} factory + * + * @param bottom The {@link IIconContainer} Icon for the Bottom Side. + * @param top The {@link IIconContainer} Icon for the Top Side. + * @param north The {@link IIconContainer} Icon for the North Side. + * @param south The {@link IIconContainer} Icon for the South Side. + * @param west The {@link IIconContainer} Icon for the West Side. + * @param east The {@link IIconContainer} Icon for the East Side. + * @param rgba The {@code short[]} RGBA tint for all sides. + * @return The instance of an {@link ITexture} implementation + */ + public static ITexture of(final IIconContainer bottom, final IIconContainer top, final IIconContainer north, + final IIconContainer south, final IIconContainer west, final IIconContainer east, final short[] rgba) { + return builder().addIcon(bottom, top, north, south, west, east) + .setRGBA(rgba) + .setAllowAlpha(true) + .build(); + } + + /** + * Bottom-Top-Sides-Sided {@link ITexture} factory + * + * @param bottom The {@link IIconContainer} Icon for the Bottom Side. + * @param top The {@link IIconContainer} Icon for the Top Side. + * @param sides The {@link IIconContainer} Icon for the North, South, West and East Sides. + * @param rgba The {@code short[]} RGBA tint for all sides. + * @return The instance of an {@link ITexture} implementation + */ + public static ITexture of(final IIconContainer bottom, final IIconContainer top, final IIconContainer sides, + final short[] rgba) { + return builder().addIcon(bottom, top, sides, sides, sides, sides) + .setRGBA(rgba) + .setAllowAlpha(true) + .build(); + } + + /** + * Rendered {@link ITexture} factory + * + * @param iconContainer The {@link IIconContainer} to render + * @param rgba The {@code short[]} RGBA tint for the texture. + * @param allowAlpha Determine if texture will use alpha blending (Not yet implemented) + * @return The instance of an {@link ITexture} implementation + */ + public static ITexture of(final IIconContainer iconContainer, final short[] rgba, final boolean allowAlpha) { + return builder().addIcon(iconContainer) + .setRGBA(rgba) + .setAllowAlpha(allowAlpha) + .build(); + } + + public static ITexture of(final IIconContainer iconContainer, final short[] rgba) { + return builder().addIcon(iconContainer) + .setRGBA(rgba) + .build(); + } + + public static ITexture of(final IIconContainer iconContainer) { + return builder().addIcon(iconContainer) + .build(); + } + + /** + * Copied-Block {@link ITexture} factory that will render a texture copied from the side of a {@link Block}. + * + * @param block The {@link Block} that will provide the texture + * @param meta The meta value for the Block + * @param side The {@link ForgeDirection} side providing the texture + * @param rgba The RGBA tint to apply + * @return The instance of an {@link ITexture} implementation + */ + public static ITexture of(final Block block, final int meta, final ForgeDirection side, final short[] rgba) { + return builder().setFromBlock(block, meta) + .setFromSide(side) + .setRGBA(rgba) + .build(); + } + + public static ITexture of(final Block block, final int meta, final ForgeDirection side) { + return builder().setFromBlock(block, meta) + .setFromSide(side) + .build(); + } + + public static ITexture of(final Block block, final int meta) { + return builder().setFromBlock(block, meta) + .build(); + } + + public static ITexture of(final Block block) { + return of(block, 0); + } + + /** + * {@link ITextureBuilder} factory + * + * @return An instance of the {@link ITextureBuilder} implementation + */ + public static ITextureBuilder builder() { + return new GT_TextureBuilder(); + } +} diff --git a/src/main/java/gregtech/api/task/TaskHost.java b/src/main/java/gregtech/api/task/TaskHost.java new file mode 100644 index 0000000000..d6377949c1 --- /dev/null +++ b/src/main/java/gregtech/api/task/TaskHost.java @@ -0,0 +1,18 @@ +package gregtech.api.task; + +import javax.annotation.Nonnull; + +import org.jetbrains.annotations.ApiStatus; + +/** + * Classes implementing this interface can have {@link TickableTask} to run. Tasks with conflicting name should not be + * allowed, to prevent them from overwriting others' NBT load/save. + */ +public interface TaskHost { + + /** + * This method should be called ONLY by {@link TickableTask} constructor. + */ + @ApiStatus.OverrideOnly + void registerTask(@Nonnull TickableTask<?> task); +} diff --git a/src/main/java/gregtech/api/task/TickableTask.java b/src/main/java/gregtech/api/task/TickableTask.java new file mode 100644 index 0000000000..3bbeb216e7 --- /dev/null +++ b/src/main/java/gregtech/api/task/TickableTask.java @@ -0,0 +1,48 @@ +package gregtech.api.task; + +import javax.annotation.Nonnull; + +import net.minecraft.nbt.NBTTagCompound; + +/** + * This class aims at separating logic run on {@link TaskHost}, rather than using interface layers. + * It has two main functionalities: Run tick and Save/Load. + * + * @param <T> Type of the host + */ +public abstract class TickableTask<T extends TaskHost> { + + @Nonnull + protected final T taskHost; + + public TickableTask(@Nonnull T taskHost) { + this.taskHost = taskHost; + taskHost.registerTask(this); + } + + /** + * @return Name of this task. Tasks with conflicting name cannot be registered to the same machine. + */ + @Nonnull + public abstract String getName(); + + /** + * Called once per world tick. + */ + public abstract void update(long tick, boolean isServerSide); + + /** + * Save info to NBT. + */ + public void writeToNBT(@Nonnull NBTTagCompound nbt) {} + + /** + * Read info from NBT. + */ + public void readFromNBT(@Nonnull NBTTagCompound nbt) {} + + @Override + public String toString() { + return "TickableTask{" + "name=" + getName() + ", taskHost=" + taskHost + "}"; + } +} diff --git a/src/main/java/gregtech/api/task/tasks/PollutionTask.java b/src/main/java/gregtech/api/task/tasks/PollutionTask.java new file mode 100644 index 0000000000..060a91acab --- /dev/null +++ b/src/main/java/gregtech/api/task/tasks/PollutionTask.java @@ -0,0 +1,45 @@ +package gregtech.api.task.tasks; + +import javax.annotation.Nonnull; + +import net.minecraft.tileentity.TileEntity; + +import gregtech.api.enums.TickTime; +import gregtech.api.interfaces.tileentity.IMachineProgress; +import gregtech.api.task.TaskHost; +import gregtech.api.task.TickableTask; +import gregtech.common.GT_Pollution; + +public class PollutionTask<T extends TaskHost & IMachineProgress> extends TickableTask<T> { + + private int pollutionPerSecond; + private static final int POLLUTION_TICK = TickTime.SECOND; + + public PollutionTask(@Nonnull T taskHost) { + super(taskHost); + } + + public PollutionTask<T> setPollutionPerSecond(int pollutionPerSecond) { + this.pollutionPerSecond = pollutionPerSecond; + return this; + } + + public int getPollutionPerSecond() { + return pollutionPerSecond; + } + + @Nonnull + @Override + public String getName() { + return "pollution"; + } + + @Override + public void update(long tick, boolean isServerSide) { + if (isServerSide && tick % POLLUTION_TICK == 0 && taskHost.hasThingsToDo()) { + if (taskHost instanceof final TileEntity entity) { + GT_Pollution.addPollution(entity, getPollutionPerSecond()); + } + } + } +} diff --git a/src/main/java/gregtech/api/task/tasks/PowerOutputTask.java b/src/main/java/gregtech/api/task/tasks/PowerOutputTask.java new file mode 100644 index 0000000000..ef800635fb --- /dev/null +++ b/src/main/java/gregtech/api/task/tasks/PowerOutputTask.java @@ -0,0 +1,32 @@ +package gregtech.api.task.tasks; + +import javax.annotation.Nonnull; + +import gregtech.api.interfaces.tileentity.IMachineProgress; +import gregtech.api.logic.interfaces.PowerLogicHost; +import gregtech.api.task.TaskHost; +import gregtech.api.task.TickableTask; + +public class PowerOutputTask<T extends PowerLogicHost & TaskHost & IMachineProgress> extends TickableTask<T> { + + private static final String NAME = "powerOutput"; + + public PowerOutputTask(@Nonnull T taskHost) { + super(taskHost); + } + + @Override + @Nonnull + public String getName() { + return NAME; + } + + @Override + public void update(long tick, boolean isServerSide) { + if (!isServerSide) return; + if (!taskHost.isActive()) return; + if (!taskHost.isEnergyEmitter()) return; + taskHost.emitEnergyFromLogic(); + } + +} diff --git a/src/main/java/gregtech/api/task/tasks/ProcessingTask.java b/src/main/java/gregtech/api/task/tasks/ProcessingTask.java new file mode 100644 index 0000000000..410c8d7a6f --- /dev/null +++ b/src/main/java/gregtech/api/task/tasks/ProcessingTask.java @@ -0,0 +1,51 @@ +package gregtech.api.task.tasks; + +import javax.annotation.Nonnull; + +import gregtech.api.interfaces.tileentity.IMachineProgress; +import gregtech.api.logic.MuTEProcessingLogic; +import gregtech.api.logic.interfaces.ProcessingLogicHost; +import gregtech.api.task.TaskHost; +import gregtech.api.task.TickableTask; + +public class ProcessingTask<T extends TaskHost & ProcessingLogicHost<P> & IMachineProgress, P extends MuTEProcessingLogic<P>> + extends TickableTask<T> { + + public ProcessingTask(@Nonnull T taskHost) { + super(taskHost); + } + + private static final String NAME = "processing"; + + @Override + @Nonnull + public String getName() { + return NAME; + } + + @Override + public void update(long tick, boolean isServerSide) { + if (!isServerSide) return; + if (!taskHost.isAllowedToWork()) return; + final P logic = taskHost.getProcessingLogic(); + if (taskHost.needsUpdate()) { + taskHost.updateProcessingLogic(logic); + taskHost.setProcessingUpdate(false); + } + if (logic.canWork() && tick % 100 == 0) { + taskHost.setProcessingLogicPower(logic); + logic.startCheck(); + if (logic.getResult() + .wasSuccessful()) { + taskHost.setActive(true); + } + } + + if (taskHost.hasThingsToDo()) { + logic.progress(); + } else { + taskHost.setActive(false); + } + } + +} diff --git a/src/main/java/gregtech/api/threads/GT_Runnable_Cable_Update.java b/src/main/java/gregtech/api/threads/GT_Runnable_Cable_Update.java new file mode 100644 index 0000000000..4375d21e40 --- /dev/null +++ b/src/main/java/gregtech/api/threads/GT_Runnable_Cable_Update.java @@ -0,0 +1,84 @@ +package gregtech.api.threads; + +import net.minecraft.tileentity.TileEntity; +import net.minecraft.world.World; +import net.minecraftforge.common.util.ForgeDirection; + +import gregtech.GT_Mod; +import gregtech.api.interfaces.tileentity.IMachineBlockUpdateable; +import gregtech.api.metatileentity.BaseMetaPipeEntity; +import gregtech.api.metatileentity.implementations.GT_MetaPipeEntity_Cable; +import gregtech.common.GT_Proxy; + +public class GT_Runnable_Cable_Update extends GT_Runnable_MachineBlockUpdate { + + protected GT_Runnable_Cable_Update(World aWorld, int posX, int posY, int posZ) { + super(aWorld, posX, posY, posZ); + } + + public static void setCableUpdateValues(World aWorld, int posX, int posY, int posZ) { + if (isEnabled) { + EXECUTOR_SERVICE.submit(new GT_Runnable_Cable_Update(aWorld, posX, posY, posZ)); + } + } + + @Override + public void run() { + int posX, posY, posZ; + try { + while (!tQueue.isEmpty()) { + final long packedCoords = tQueue.dequeueLong(); + posX = unpackLongX(packedCoords); + posY = unpackLongY(packedCoords); + posZ = unpackLongZ(packedCoords); + + final TileEntity tTileEntity; + + GT_Proxy.TICK_LOCK.lock(); + try { + // we dont want to go over cables that are in unloaded chunks + // keeping the lock just to make sure no CME happens + if (world.blockExists(posX, posY, posZ)) { + tTileEntity = world.getTileEntity(posX, posY, posZ); + } else { + tTileEntity = null; + } + } finally { + GT_Proxy.TICK_LOCK.unlock(); + } + + // See if the block itself needs an update + if (tTileEntity instanceof IMachineBlockUpdateable) + ((IMachineBlockUpdateable) tTileEntity).onMachineBlockUpdate(); + + // Now see if we should add the nearby blocks to the queue: + // only add blocks the cable is connected to + if (tTileEntity instanceof BaseMetaPipeEntity metaPipe + && metaPipe.getMetaTileEntity() instanceof GT_MetaPipeEntity_Cable cable) { + for (int i = 0; i < ForgeDirection.VALID_DIRECTIONS.length; i++) { + final ForgeDirection side = ForgeDirection.VALID_DIRECTIONS[i]; + if (cable.isConnectedAtSide(side)) { + final long tCoords = asLong(posX + side.offsetX, posY + side.offsetY, posZ + side.offsetZ); + if (visited.add(tCoords)) { + tQueue.enqueue(tCoords); + } + } + } + } + } + } catch (Exception e) { + GT_Mod.GT_FML_LOGGER.error( + "Well this update was broken... " + initialX + + ", " + + initialY + + ", " + + initialZ + + ", mWorld={" + + world.getProviderName() + + " @dimId " + + world.provider.dimensionId + + "}", + e); + } + } +} diff --git a/src/main/java/gregtech/api/threads/GT_Runnable_MachineBlockUpdate.java b/src/main/java/gregtech/api/threads/GT_Runnable_MachineBlockUpdate.java new file mode 100644 index 0000000000..3670655eaf --- /dev/null +++ b/src/main/java/gregtech/api/threads/GT_Runnable_MachineBlockUpdate.java @@ -0,0 +1,211 @@ +package gregtech.api.threads; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; + +import net.minecraft.tileentity.TileEntity; +import net.minecraft.world.World; +import net.minecraftforge.common.util.ForgeDirection; + +import gregtech.GT_Mod; +import gregtech.api.GregTech_API; +import gregtech.api.interfaces.tileentity.IMachineBlockUpdateable; +import gregtech.common.GT_Proxy; +import it.unimi.dsi.fastutil.longs.LongArrayFIFOQueue; +import it.unimi.dsi.fastutil.longs.LongOpenHashSet; +import it.unimi.dsi.fastutil.longs.LongSet; + +public class GT_Runnable_MachineBlockUpdate implements Runnable { + + // Borrowed from Angelica until moving to GTNHLib or something + final static int SIZE_BITS_X = 26; // 1 + log2(MathHelper.roundUpToPowerOfTwo(30000000), RoundingMode.UNNECESSARY); + final static int SIZE_BITS_Z = SIZE_BITS_X; + final static int SIZE_BITS_Y = 64 - SIZE_BITS_X - SIZE_BITS_Z; + final static long BITS_X = (1L << SIZE_BITS_X) - 1L; + final static long BITS_Y = (1L << SIZE_BITS_Y) - 1L; + final static long BITS_Z = (1L << SIZE_BITS_Z) - 1L; + final static int BIT_SHIFT_Z = SIZE_BITS_Y; + final static int BIT_SHIFT_X = SIZE_BITS_Y + SIZE_BITS_Z; + + static long asLong(int x, int y, int z) { + long l = 0L; + l |= ((long) x & BITS_X) << BIT_SHIFT_X; + l |= ((long) y & BITS_Y) << 0; + l |= ((long) z & BITS_Z) << BIT_SHIFT_Z; + return l; + } + + public static int unpackLongX(long packedPos) { + return (int) (packedPos << 64 - BIT_SHIFT_X - SIZE_BITS_X >> 64 - SIZE_BITS_X); + } + + public static int unpackLongY(long packedPos) { + return (int) (packedPos << 64 - SIZE_BITS_Y >> 64 - SIZE_BITS_Y); + } + + public static int unpackLongZ(long packedPos) { + return (int) (packedPos << 64 - BIT_SHIFT_Z - SIZE_BITS_Z >> 64 - SIZE_BITS_Z); + } + + // used by runner thread + protected final int initialX, initialY, initialZ; + protected final World world; + protected final LongSet visited = new LongOpenHashSet(); + protected final LongArrayFIFOQueue tQueue = new LongArrayFIFOQueue(); + + // Threading + private static final ThreadFactory THREAD_FACTORY = r -> { + Thread thread = new Thread(r); + thread.setName("GT_MachineBlockUpdate"); + return thread; + }; + protected static ExecutorService EXECUTOR_SERVICE; + + // This class should never be initiated outside of this class! + protected GT_Runnable_MachineBlockUpdate(World aWorld, int posX, int posY, int posZ) { + this.world = aWorld; + this.initialX = posX; + this.initialY = posY; + this.initialZ = posZ; + final long coords = asLong(posX, posY, posZ); + visited.add(coords); + tQueue.enqueue(coords); + } + + public static boolean isEnabled() { + return isEnabled; + } + + public static void setEnabled() { + GT_Runnable_MachineBlockUpdate.isEnabled = true; + } + + public static void setDisabled() { + GT_Runnable_MachineBlockUpdate.isEnabled = false; + } + + public static void setEnabled(boolean isEnabled) { + GT_Runnable_MachineBlockUpdate.isEnabled = isEnabled; + } + + public static boolean isCurrentThreadEnabled() { + return perThreadEnable.get(); + } + + public static void setCurrentThreadEnabled(boolean perThreadEnable) { + GT_Runnable_MachineBlockUpdate.perThreadEnable.set(perThreadEnable); + } + + protected static boolean isEnabled = true; + protected static final ThreadLocal<Boolean> perThreadEnable = ThreadLocal.withInitial(() -> true); + + public static void setMachineUpdateValues(World aWorld, int posX, int posY, int posZ) { + if (isEnabled() && isCurrentThreadEnabled()) { + EXECUTOR_SERVICE.submit(new GT_Runnable_MachineBlockUpdate(aWorld, posX, posY, posZ)); + } + } + + public static void initExecutorService() { + EXECUTOR_SERVICE = Executors.newFixedThreadPool( + Math.max( + 1, + (Runtime.getRuntime() + .availableProcessors() * 2 + / 3)), + THREAD_FACTORY); + } + + public static void shutdownExecutorService() { + try { + GT_Mod.GT_FML_LOGGER.info("Shutting down Machine block update executor service"); + EXECUTOR_SERVICE.shutdown(); // Disable new tasks from being submitted + // Wait a while for existing tasks to terminate + if (!EXECUTOR_SERVICE.awaitTermination(60, TimeUnit.SECONDS)) { + EXECUTOR_SERVICE.shutdownNow(); // Cancel currently executing tasks + // Wait a while for tasks to respond to being cancelled + if (!EXECUTOR_SERVICE.awaitTermination(60, TimeUnit.SECONDS)) { + GT_Mod.GT_FML_LOGGER.error( + "Well this didn't terminated well... GT_Runnable_MachineBlockUpdate.shutdownExecutorService"); + } + } + } catch (InterruptedException ie) { + GT_Mod.GT_FML_LOGGER.error("Well this interruption got interrupted...", ie); + // (Re-)Cancel if current thread also interrupted + EXECUTOR_SERVICE.shutdownNow(); + // Preserve interrupt status + Thread.currentThread() + .interrupt(); + } catch (Exception e) { + GT_Mod.GT_FML_LOGGER.error("Well this didn't terminated well...", e); + // (Re-)Cancel in case + EXECUTOR_SERVICE.shutdownNow(); + } finally { + GT_Mod.GT_FML_LOGGER.info("Leaving... GT_Runnable_MachineBlockUpdate.shutdownExecutorService"); + } + } + + @Override + public void run() { + int posX, posY, posZ; + try { + while (!tQueue.isEmpty()) { + final long packedCoords = tQueue.dequeueLong(); + posX = unpackLongX(packedCoords); + posY = unpackLongY(packedCoords); + posZ = unpackLongZ(packedCoords); + + final TileEntity tTileEntity; + final boolean isMachineBlock; + + // This might load a chunk... which might load a TileEntity... which might get added to + // `loadedTileEntityList`... which might be in the process + // of being iterated over during `UpdateEntities()`... which might cause a + // ConcurrentModificationException. So, lock that shit. + GT_Proxy.TICK_LOCK.lock(); + try { + tTileEntity = world.getTileEntity(posX, posY, posZ); + isMachineBlock = GregTech_API + .isMachineBlock(world.getBlock(posX, posY, posZ), world.getBlockMetadata(posX, posY, posZ)); + } finally { + GT_Proxy.TICK_LOCK.unlock(); + } + + // See if the block itself needs an update + if (tTileEntity instanceof IMachineBlockUpdateable) + ((IMachineBlockUpdateable) tTileEntity).onMachineBlockUpdate(); + + // Now see if we should add the nearby blocks to the queue: + // 1) If we've visited less than 5 blocks, then yes + // 2) If the tile says we should recursively updated (pipes don't, machine blocks do) + // 3) If the block at the coordinates is marked as a machine block + if (visited.size() < 5 + || (tTileEntity instanceof IMachineBlockUpdateable + && ((IMachineBlockUpdateable) tTileEntity).isMachineBlockUpdateRecursive()) + || isMachineBlock) { + for (int i = 0; i < ForgeDirection.VALID_DIRECTIONS.length; i++) { + final ForgeDirection side = ForgeDirection.VALID_DIRECTIONS[i]; + final long tCoords = asLong(posX + side.offsetX, posY + side.offsetY, posZ + side.offsetZ); + if (visited.add(tCoords)) { + tQueue.enqueue(tCoords); + } + } + } + } + } catch (Exception e) { + GT_Mod.GT_FML_LOGGER.error( + "Well this update was broken... " + initialX + + ", " + + initialY + + ", " + + initialZ + + ", mWorld={" + + world.getProviderName() + + " @dimId " + + world.provider.dimensionId + + "}", + e); + } + } +} diff --git a/src/main/java/gregtech/api/threads/GT_Runnable_Sound.java b/src/main/java/gregtech/api/threads/GT_Runnable_Sound.java new file mode 100644 index 0000000000..9021a60e89 --- /dev/null +++ b/src/main/java/gregtech/api/threads/GT_Runnable_Sound.java @@ -0,0 +1,41 @@ +package gregtech.api.threads; + +import net.minecraft.util.ResourceLocation; +import net.minecraft.world.World; + +import gregtech.api.util.GT_PlayedSound; +import gregtech.api.util.GT_Utility; + +public class GT_Runnable_Sound implements Runnable { + + private final double mX, mY, mZ; + private final int mTimeUntilNextSound; + private final World mWorld; + private final ResourceLocation mSoundResourceLocation; + private final float mSoundStrength, mSoundModulation; + + public GT_Runnable_Sound(World aWorld, double aX, double aY, double aZ, int aTimeUntilNextSound, + ResourceLocation aSoundResourceLocation, float aSoundStrength, float aSoundModulation) { + mWorld = aWorld; + mX = aX; + mY = aY; + mZ = aZ; + mTimeUntilNextSound = aTimeUntilNextSound; + mSoundResourceLocation = aSoundResourceLocation; + mSoundStrength = aSoundStrength; + mSoundModulation = aSoundModulation; + } + + @Override + public void run() { + try { + GT_PlayedSound tSound; + if (GT_Utility.sPlayedSoundMap.containsKey(tSound = new GT_PlayedSound(mSoundResourceLocation, mX, mY, mZ))) + return; + mWorld.playSound(mX, mY, mZ, mSoundResourceLocation.toString(), mSoundStrength, mSoundModulation, false); + GT_Utility.sPlayedSoundMap.put(tSound, mTimeUntilNextSound); + } catch (Throwable e) { + /**/ + } + } +} diff --git a/src/main/java/gregtech/api/util/AdvancedFusionOverclockDescriber.java b/src/main/java/gregtech/api/util/AdvancedFusionOverclockDescriber.java new file mode 100644 index 0000000000..7a6e609f93 --- /dev/null +++ b/src/main/java/gregtech/api/util/AdvancedFusionOverclockDescriber.java @@ -0,0 +1,23 @@ +package gregtech.api.util; + +import javax.annotation.ParametersAreNonnullByDefault; + +import gregtech.api.objects.overclockdescriber.FusionOverclockDescriber; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +public class AdvancedFusionOverclockDescriber extends FusionOverclockDescriber { + + public AdvancedFusionOverclockDescriber(byte energyTier, long capableStartup) { + super(energyTier, capableStartup); + } + + @Override + protected int getEUtIncreasePerOC() { + return 2; + } + + protected int getDurationDecreasePerOC() { + return 2; + } +} diff --git a/src/main/java/gregtech/api/util/AveragePerTickCounter.java b/src/main/java/gregtech/api/util/AveragePerTickCounter.java new file mode 100644 index 0000000000..c9b1275deb --- /dev/null +++ b/src/main/java/gregtech/api/util/AveragePerTickCounter.java @@ -0,0 +1,139 @@ +package gregtech.api.util; + +import java.security.InvalidParameterException; +import java.util.ArrayDeque; + +import net.minecraft.server.MinecraftServer; + +public class AveragePerTickCounter { + + /** + * Averages a value over a certain amount of ticks + * + * @param period amount of ticks to average (20 for 1 second) + * + */ + public AveragePerTickCounter(int period) throws InvalidParameterException { + + if (period <= 0) throw new InvalidParameterException("period should be a positive non-zero number"); + + this.period = period; + values = new ArrayDeque<>(period); + } + + public void addValue(long value) { + + if (value == 0) return; + + final int currTick = getWorldTimeInTicks(); + + if (values.isEmpty()) { + values.addLast(new Measurement(currTick, value)); + isCachedAverageValid = false; + return; + } + + Measurement lastMeasurement = values.peekLast(); + final int lastMeasurementTick = lastMeasurement.TimestampInWorldTicks; + + /// sums up values added in the same tick + /// for example a cable had an amp running through it multiple times in the same tick + if (currTick == lastMeasurementTick) { + lastMeasurement.Value = lastMeasurement.Value + value; + isCachedAverageValid = false; + return; + } + + if (currTick > lastMeasurementTick) { + trimIrrelevantData(currTick); + + values.addLast(new Measurement(currTick, value)); + isCachedAverageValid = false; + return; + } + } + + public double getAverage() { + + if (values.isEmpty()) return 0; + + final int currTick = getWorldTimeInTicks(); + + Measurement lastMeasurement = values.peekLast(); + final int lastMeasurementTick = lastMeasurement.TimestampInWorldTicks; + + if (currTick < lastMeasurementTick) return 0; + + if (currTick > lastMeasurementTick) { + trimIrrelevantData(currTick); + } + + if (isCachedAverageValid) return cachedAverage; + + return calculateAverage(); + } + + public long getLast() { + + if (values.isEmpty()) return 0; + + final int currTick = getWorldTimeInTicks(); + + Measurement lastMeasurement = values.peekLast(); + final int lastMeasurementTick = lastMeasurement.TimestampInWorldTicks; + + if (currTick == lastMeasurementTick) return values.getLast().Value; + + return 0; + } + + private double calculateAverage() { + + isCachedAverageValid = true; + long sum = 0; + + for (Measurement measurement : values) { + sum += measurement.Value; + } + + return sum / (double) period; + } + + private void trimIrrelevantData(int currWorldTimeInTicks) { + + if (values.isEmpty()) return; + + int firstMeasurementTick = values.peekFirst().TimestampInWorldTicks; + + while (currWorldTimeInTicks - firstMeasurementTick >= period) { + values.removeFirst(); + isCachedAverageValid = false; + + if (values.isEmpty()) return; + + firstMeasurementTick = values.peekFirst().TimestampInWorldTicks; + } + } + + private int getWorldTimeInTicks() { + return MinecraftServer.getServer() + .getTickCounter(); + } + + private ArrayDeque<Measurement> values; + private int period; + + private double cachedAverage = 0; + private boolean isCachedAverageValid = true; + + private class Measurement { + + public int TimestampInWorldTicks; + public long Value; + + public Measurement(int timestampInWorldTicks, long value) { + this.TimestampInWorldTicks = timestampInWorldTicks; + this.Value = value; + } + } +} diff --git a/src/main/java/gregtech/api/util/ColorsMetadataSection.java b/src/main/java/gregtech/api/util/ColorsMetadataSection.java new file mode 100644 index 0000000000..fb9cc6dd56 --- /dev/null +++ b/src/main/java/gregtech/api/util/ColorsMetadataSection.java @@ -0,0 +1,63 @@ +package gregtech.api.util; + +import java.util.HashMap; +import java.util.Map; + +import net.minecraft.client.resources.data.IMetadataSection; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; + +@SideOnly(Side.CLIENT) +public class ColorsMetadataSection implements IMetadataSection { + + private final Map<String, Integer> textColors; + private final Map<String, String> hexTextColors; + private final Map<String, Integer> guiTints; + private final Map<String, String> hexGuiTints; + private final boolean guiTintEnabled; + + public ColorsMetadataSection(Map<String, String> hexTextColorMap, Map<String, String> hexGuiTintMap, + boolean guiTintEnabled) { + this.hexTextColors = hexTextColorMap; + this.textColors = convertHexMapToIntMap(hexTextColorMap); + + this.hexGuiTints = hexGuiTintMap; + this.guiTints = convertHexMapToIntMap(hexGuiTintMap); + + this.guiTintEnabled = guiTintEnabled; + } + + private Map<String, Integer> convertHexMapToIntMap(Map<String, String> hexMap) { + Map<String, Integer> intMap = new HashMap<>(); + + for (String key : hexMap.keySet()) { + int colorValue = -1; + String hex = hexMap.get(key); + try { + if (!hex.isEmpty()) colorValue = Integer.parseUnsignedInt(hex, 16); + } catch (final NumberFormatException e) { + GT_Log.err.println("Couldn't format color correctly of " + key + " -> " + hex); + } + intMap.put(key, colorValue); + } + return intMap; + } + + public int getTextColorOrDefault(String key, int defaultColor) { + return isColorInMap(key, this.hexTextColors) ? this.textColors.get(key) : defaultColor; + } + + public int getGuiTintOrDefault(String key, int defaultColor) { + return isColorInMap(key, this.hexGuiTints) ? this.guiTints.get(key) : defaultColor; + } + + private boolean isColorInMap(String key, Map<String, String> hexMap) { + return hexMap.containsKey(key) && !hexMap.get(key) + .isEmpty(); + } + + public boolean sGuiTintingEnabled() { + return this.guiTintEnabled; + } +} diff --git a/src/main/java/gregtech/api/util/ColorsMetadataSectionSerializer.java b/src/main/java/gregtech/api/util/ColorsMetadataSectionSerializer.java new file mode 100644 index 0000000000..389662d041 --- /dev/null +++ b/src/main/java/gregtech/api/util/ColorsMetadataSectionSerializer.java @@ -0,0 +1,81 @@ +package gregtech.api.util; + +import java.lang.reflect.Type; +import java.util.HashMap; +import java.util.Map; + +import net.minecraft.client.resources.data.BaseMetadataSectionSerializer; +import net.minecraft.util.JsonUtils; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonSerializationContext; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import gregtech.GT_Mod; +import gregtech.api.GregTech_API; +import gregtech.api.enums.Dyes; + +@SideOnly(Side.CLIENT) +public class ColorsMetadataSectionSerializer extends BaseMetadataSectionSerializer { + + public ColorsMetadataSection deserialize(JsonElement metadataColors, Type type, + JsonDeserializationContext context) { + // Default values + boolean enableGuiTint = GregTech_API.sColoredGUI; + Map<String, String> hexGuiTintMap = new HashMap<>(); + Map<String, String> hexTextColorMap = new HashMap<>(); + + JsonObject jsonObject = JsonUtils.getJsonElementAsJsonObject(metadataColors, "metadata section"); + if (jsonObject.has("textColor")) { + JsonObject textColors = JsonUtils.func_152754_s(jsonObject, "textColor"); + for (Map.Entry<String, JsonElement> entry : textColors.entrySet()) { + if (entry.getValue() + .isJsonPrimitive()) { + hexTextColorMap.put( + entry.getKey(), + entry.getValue() + .getAsString()); + } else { + GT_Mod.GT_FML_LOGGER.warn("ColorOverride expects primitive value for key `textColor`"); + } + } + } + + if (jsonObject.has("guiTint")) { + JsonObject guiTints = JsonUtils.func_152754_s(jsonObject, "guiTint"); + enableGuiTint = JsonUtils + .getJsonObjectBooleanFieldValueOrDefault(guiTints, "enableGuiTintWhenPainted", true); + + for (Dyes dye : Dyes.values()) { + hexGuiTintMap.put(dye.mName, GT_Util.toHexString(dye.getRGBA())); + } + + for (String key : hexGuiTintMap.keySet()) { + if (enableGuiTint) { + hexGuiTintMap.replace( + key, + JsonUtils.getJsonObjectStringFieldValueOrDefault(guiTints, key, hexGuiTintMap.get(key))); + } else { + hexGuiTintMap.replace(key, GT_Util.toHexString(Dyes.dyeWhite.getRGBA())); + } + } + } + + return new ColorsMetadataSection(hexTextColorMap, hexGuiTintMap, enableGuiTint); + } + + public JsonElement serialize(ColorsMetadataSection colorsMetaSection, Type type, JsonSerializationContext context) { + return new JsonObject(); + } + + public String getSectionName() { + return "colors"; + } + + public JsonElement serialize(Object object, Type type, JsonSerializationContext context) { + return this.serialize((ColorsMetadataSection) object, type, context); + } +} diff --git a/src/main/java/gregtech/api/util/ExternalMaterials.java b/src/main/java/gregtech/api/util/ExternalMaterials.java new file mode 100644 index 0000000000..29564db2fd --- /dev/null +++ b/src/main/java/gregtech/api/util/ExternalMaterials.java @@ -0,0 +1,14 @@ +package gregtech.api.util; + +import gregtech.api.enums.Materials; + +public class ExternalMaterials { + + public static Materials getRhodiumPlatedPalladium() { + return Materials.getWithFallback("Rhodium-PlatedPalladium", Materials.Chrome); + } + + public static Materials getRuridit() { + return Materials.getWithFallback("Ruridit", Materials.Osmiridium); + } +} diff --git a/src/main/java/gregtech/api/util/FieldsAreNonnullByDefault.java b/src/main/java/gregtech/api/util/FieldsAreNonnullByDefault.java new file mode 100644 index 0000000000..1f51aa39a7 --- /dev/null +++ b/src/main/java/gregtech/api/util/FieldsAreNonnullByDefault.java @@ -0,0 +1,18 @@ +package gregtech.api.util; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import javax.annotation.Nonnull; +import javax.annotation.meta.TypeQualifierDefault; + +/** + * This annotation can be applied to a package or class to indicate that + * the fields in that element are nonnull by default unless there is an explicit nullness annotation. + * </ul> + */ +@Nonnull +@TypeQualifierDefault({ ElementType.FIELD }) +@Retention(RetentionPolicy.RUNTIME) +public @interface FieldsAreNonnullByDefault {} diff --git a/src/main/java/gregtech/api/util/FishPondFakeRecipe.java b/src/main/java/gregtech/api/util/FishPondFakeRecipe.java new file mode 100644 index 0000000000..dc579ebd9b --- /dev/null +++ b/src/main/java/gregtech/api/util/FishPondFakeRecipe.java @@ -0,0 +1,80 @@ +package gregtech.api.util; + +import java.util.ArrayList; + +import net.minecraft.item.ItemStack; +import net.minecraft.util.WeightedRandomFishable; +import net.minecraftforge.common.FishingHooks; +import net.minecraftforge.fluids.FluidStack; + +import gtPlusPlus.api.objects.Logger; +import gtPlusPlus.api.objects.data.AutoMap; +import gtPlusPlus.api.recipe.GTPPRecipeMaps; +import gtPlusPlus.core.recipe.common.CI; +import gtPlusPlus.core.util.minecraft.ItemUtils; +import gtPlusPlus.core.util.reflect.ReflectionUtils; + +public class FishPondFakeRecipe { + + public static ArrayList<WeightedRandomFishable> fish = new ArrayList<>(); + public static ArrayList<WeightedRandomFishable> junk = new ArrayList<>(); + public static ArrayList<WeightedRandomFishable> treasure = new ArrayList<>(); + + @SuppressWarnings("unchecked") + public static boolean generateFishPondRecipes() { + + try { + fish = (ArrayList<WeightedRandomFishable>) ReflectionUtils.getField(FishingHooks.class, "fish") + .get(null); + junk = (ArrayList<WeightedRandomFishable>) ReflectionUtils.getField(FishingHooks.class, "junk") + .get(null); + treasure = (ArrayList<WeightedRandomFishable>) ReflectionUtils.getField(FishingHooks.class, "treasure") + .get(null); + } catch (IllegalArgumentException | IllegalAccessException e) { + Logger.INFO("Error generating Fish Pond Recipes. [1]"); + e.printStackTrace(); + } + + AutoMap<ArrayList<WeightedRandomFishable>> mega = new AutoMap<>(); + mega.put(fish); + mega.put(junk); + mega.put(treasure); + + int mType = 14; + for (ArrayList<WeightedRandomFishable> f : mega.values()) { + for (WeightedRandomFishable weightedRandomFishable : f) { + if (weightedRandomFishable != null) { + WeightedRandomFishable u = weightedRandomFishable; + try { + ItemStack t = (ItemStack) ReflectionUtils + .getField(WeightedRandomFishable.class, "field_150711_b") + .get(u); + addNewFishPondLoot(mType, new ItemStack[] { t }, new int[] { 10000 }); + } catch (IllegalArgumentException | IllegalAccessException e1) { + Logger.INFO("Error generating Fish Pond Recipes. [2]"); + e1.printStackTrace(); + } + } + } + mType++; + } + + return true; + } + + public static void addNewFishPondLoot(int circuit, ItemStack[] outputItems, int[] chances) { + GT_Recipe x = new GT_Recipe( + true, + new ItemStack[] { CI.getNumberedCircuit(circuit) }, + outputItems, + null, + chances, + new FluidStack[] { null }, + new FluidStack[] { null }, + 100, // 1 Tick + 0, // No Eu produced + 0); + Logger.INFO("Fishing [" + circuit + "]: " + ItemUtils.getArrayStackNames(outputItems)); + GTPPRecipeMaps.fishPondRecipes.addRecipe(x, false, false, false); + } +} diff --git a/src/main/java/gregtech/api/util/GT_ApiaryModifier.java b/src/main/java/gregtech/api/util/GT_ApiaryModifier.java new file mode 100644 index 0000000000..4a89345670 --- /dev/null +++ b/src/main/java/gregtech/api/util/GT_ApiaryModifier.java @@ -0,0 +1,24 @@ +package gregtech.api.util; + +import net.minecraft.world.biome.BiomeGenBase; + +public class GT_ApiaryModifier { + + public float territory = 1f; + public float mutation = 1f; + public float lifespan = 1f; + public float production = 2f; + public float flowering = 1f; + public float geneticDecay = 1f; + public boolean isSealed = false; + public boolean isSelfLighted = false; + public boolean isSelfUnlighted = false; + public boolean isSunlightSimulated = false; + public boolean isAutomated = false; + public boolean isCollectingPollen = false; + public BiomeGenBase biomeOverride = null; + public float energy = 1f; + public float temperature = 0f; + public float humidity = 0f; + public int maxSpeed = 0; +} diff --git a/src/main/java/gregtech/api/util/GT_ApiaryUpgrade.java b/src/main/java/gregtech/api/util/GT_ApiaryUpgrade.java new file mode 100644 index 0000000000..9ed7a56e1e --- /dev/null +++ b/src/main/java/gregtech/api/util/GT_ApiaryUpgrade.java @@ -0,0 +1,225 @@ +package gregtech.api.util; + +import java.util.ArrayList; +import java.util.EnumMap; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.HashSet; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +import net.minecraft.item.ItemStack; +import net.minecraft.world.biome.BiomeGenBase; + +import gregtech.api.enums.OrePrefixes; +import gregtech.common.items.GT_MetaGenerated_Item_03; + +/** + * Actual items are defined in {@link GT_MetaGenerated_Item_03} + */ +public enum GT_ApiaryUpgrade { + + speed1(UNIQUE_INDEX.SPEED_UPGRADE, 32200, 1, (mods, n) -> mods.maxSpeed = 1), + speed2(UNIQUE_INDEX.SPEED_UPGRADE, 32201, 1, (mods, n) -> mods.maxSpeed = 2), + speed3(UNIQUE_INDEX.SPEED_UPGRADE, 32202, 1, (mods, n) -> mods.maxSpeed = 3), + speed4(UNIQUE_INDEX.SPEED_UPGRADE, 32203, 1, (mods, n) -> mods.maxSpeed = 4), + speed5(UNIQUE_INDEX.SPEED_UPGRADE, 32204, 1, (mods, n) -> mods.maxSpeed = 5), + speed6(UNIQUE_INDEX.SPEED_UPGRADE, 32205, 1, (mods, n) -> mods.maxSpeed = 6), + speed7(UNIQUE_INDEX.SPEED_UPGRADE, 32206, 1, (mods, n) -> mods.maxSpeed = 7), + speed8(UNIQUE_INDEX.SPEED_UPGRADE, 32207, 1, (mods, n) -> mods.maxSpeed = 8), + speed8upgraded(UNIQUE_INDEX.SPEED_UPGRADE, 32208, 1, (mods, n) -> { + mods.maxSpeed = 8; + mods.production = 17.19926784f; + mods.energy *= 14.75; + }), + production(UNIQUE_INDEX.PRODUCTION_UPGRADE, 32209, 8, (mods, n) -> { + mods.production = 4.f * (float) Math.pow(1.2d, n); + mods.energy *= Math.pow(1.4f, n); + }), + plains(UNIQUE_INDEX.PLAINS_UPGRADE, 32210, 1, (mods, n) -> { + mods.biomeOverride = BiomeGenBase.plains; + mods.energy *= 1.2f; + }), + light(UNIQUE_INDEX.LIGHT_UPGRADE, 32211, 1, (mods, n) -> { + mods.isSelfLighted = true; + mods.energy *= 1.05f; + }), + flowering(UNIQUE_INDEX.FLOWERING_UPGRADE, 32212, 8, (mods, n) -> { + mods.flowering *= Math.pow(1.2f, n); + mods.energy *= Math.pow(1.1f, n); + }), + winter(UNIQUE_INDEX.WINTER_UPGRADE, 32213, 1, (mods, n) -> { + mods.biomeOverride = BiomeGenBase.taiga; + mods.energy *= 1.5f; + }), + dryer(UNIQUE_INDEX.DRYER_UPGRADE, 32214, 16, (mods, n) -> { + mods.humidity -= 0.125f * n; + mods.energy *= Math.pow(1.025f, n); + }), + automation(UNIQUE_INDEX.AUTOMATION_UPGRADE, 32215, 1, (mods, n) -> { + mods.isAutomated = true; + mods.energy *= 1.1f; + }), + humidifier(UNIQUE_INDEX.HUMIDIFIER_UPGRADE, 32216, 16, (mods, n) -> { + mods.humidity += 0.125f * n; + mods.energy *= Math.pow(1.05f, n); + }), + hell(UNIQUE_INDEX.HELL_UPGRADE, 32217, 1, (mods, n) -> { + mods.biomeOverride = BiomeGenBase.hell; + mods.energy *= 1.5f; + }), + pollen(UNIQUE_INDEX.POLLEN_UPGRADE, 32218, 1, (mods, n) -> { + mods.flowering = 0f; + mods.energy *= 1.3f; + }), + desert(UNIQUE_INDEX.DESERT_UPGRADE, 32219, 1, (mods, n) -> { + mods.biomeOverride = BiomeGenBase.desert; + mods.energy *= 1.2f; + }), + cooler(UNIQUE_INDEX.COOLER_UPGRADE, 32220, 16, (mods, n) -> { + mods.temperature -= 0.125f * n; + mods.energy *= Math.pow(1.025f, n); + }), + lifespan(UNIQUE_INDEX.LIFESPAN_UPGRADE, 32221, 4, (mods, n) -> { + mods.lifespan /= Math.pow(1.5f, n); + mods.energy *= Math.pow(1.05f, n); + }), + seal(UNIQUE_INDEX.SEAL_UPGRADE, 32222, 1, (mods, n) -> { + mods.isSealed = true; + mods.energy *= 1.05f; + }), + stabilizer(UNIQUE_INDEX.STABILIZER_UPGRADE, 32223, 1, (mods, n) -> { + mods.geneticDecay = 0f; + mods.energy *= 2.50f; + }), + jungle(UNIQUE_INDEX.JUNGLE_UPGRADE, 32224, 1, (mods, n) -> { + mods.biomeOverride = BiomeGenBase.jungle; + mods.energy *= 1.20f; + }), + territory(UNIQUE_INDEX.TERRITORY_UPGRADE, 32225, 4, (mods, n) -> { + mods.territory *= Math.pow(1.5f, n); + mods.energy *= Math.pow(1.05f, n); + }), + ocean(UNIQUE_INDEX.OCEAN_UPGRADE, 32226, 1, (mods, n) -> { + mods.biomeOverride = BiomeGenBase.ocean; + mods.energy *= 1.20f; + }), + sky(UNIQUE_INDEX.SKY_UPGRADE, 32227, 1, (mods, n) -> { + mods.isSunlightSimulated = true; + mods.energy *= 1.05f; + }), + heater(UNIQUE_INDEX.HEATER_UPGRADE, 32228, 16, (mods, n) -> { + mods.temperature += 0.125f * n; + mods.energy *= Math.pow(1.025f, n); + }), + sieve(UNIQUE_INDEX.SIEVE_UPGRADE, 32229, 1, (mods, n) -> { + mods.isCollectingPollen = true; + mods.energy *= 1.05f; + }), + unlight(UNIQUE_INDEX.LIGHT_UPGRADE, 32231, 1, (mods, n) -> { + mods.isSelfUnlighted = true; + mods.energy *= 1.05f; + }),; + + private enum UNIQUE_INDEX { + + SPEED_UPGRADE, + PRODUCTION_UPGRADE, + PLAINS_UPGRADE, + LIGHT_UPGRADE, // also unlight + FLOWERING_UPGRADE, + WINTER_UPGRADE, + DRYER_UPGRADE, + AUTOMATION_UPGRADE, + HUMIDIFIER_UPGRADE, + HELL_UPGRADE, + POLLEN_UPGRADE, + DESERT_UPGRADE, + COOLER_UPGRADE, + LIFESPAN_UPGRADE, + SEAL_UPGRADE, + STABILIZER_UPGRADE, + JUNGLE_UPGRADE, + TERRITORY_UPGRADE, + OCEAN_UPGRADE, + SKY_UPGRADE, + HEATER_UPGRADE, + SIEVE_UPGRADE,; + + void apply(Consumer<GT_ApiaryUpgrade> fn) { + UNIQUE_UPGRADE_LIST.get(this) + .forEach(fn); + } + } + + private static final EnumMap<UNIQUE_INDEX, ArrayList<GT_ApiaryUpgrade>> UNIQUE_UPGRADE_LIST = new EnumMap<>( + UNIQUE_INDEX.class); + + private int meta = 0; + private int maxnumber = 1; + + private final GT_Utility.ItemId id; + private final UNIQUE_INDEX unique_index; + private final BiConsumer<GT_ApiaryModifier, Integer> applier; + + private final HashSet<GT_Utility.ItemId> blacklistedUpgrades = new HashSet<>(); + + GT_ApiaryUpgrade(UNIQUE_INDEX unique_index, int meta, int maxnumber, + BiConsumer<GT_ApiaryModifier, Integer> applier) { + this.unique_index = unique_index; + this.meta = meta; + this.maxnumber = maxnumber; + this.applier = applier; + this.id = GT_Utility.ItemId.createNoCopy(get(1)); + } + + private void setup_static_variables() { + quickLookup.put(this.meta, this); + ArrayList<GT_ApiaryUpgrade> un = UNIQUE_UPGRADE_LIST.get(this.unique_index); + if (un != null) un.forEach((u) -> { + u.blacklistedUpgrades.add(this.id); + this.blacklistedUpgrades.add(u.id); + }); + else { + un = new ArrayList<>(1); + UNIQUE_UPGRADE_LIST.put(this.unique_index, un); + } + un.add(this); + } + + public static GT_ApiaryUpgrade getUpgrade(ItemStack s) { + if (s == null) return null; + if (!isUpgrade(s)) return null; + return quickLookup.get(s.getItemDamage()); + } + + public boolean isAllowedToWorkWith(ItemStack s) { + GT_Utility.ItemId id = GT_Utility.ItemId.createNoCopy(s); + return !blacklistedUpgrades.contains(id); + } + + public int getMaxNumber() { + return maxnumber; + } + + public void applyModifiers(GT_ApiaryModifier mods, ItemStack stack) { + if (applier != null) applier.accept(mods, stack.stackSize); + } + + public ItemStack get(int count) { + return new ItemStack(GT_MetaGenerated_Item_03.INSTANCE, count, meta); + } + + public static boolean isUpgrade(ItemStack s) { + return OrePrefixes.apiaryUpgrade.contains(s); + } + + private static final HashMap<Integer, GT_ApiaryUpgrade> quickLookup = new HashMap<>(); + + static { + EnumSet.allOf(GT_ApiaryUpgrade.class) + .forEach(GT_ApiaryUpgrade::setup_static_variables); + speed8upgraded.blacklistedUpgrades.add(production.id); + production.blacklistedUpgrades.add(speed8upgraded.id); + } +} diff --git a/src/main/java/gregtech/api/util/GT_AssemblyLineUtils.java b/src/main/java/gregtech/api/util/GT_AssemblyLineUtils.java new file mode 100644 index 0000000000..1ada9c78d5 --- /dev/null +++ b/src/main/java/gregtech/api/util/GT_AssemblyLineUtils.java @@ -0,0 +1,557 @@ +package gregtech.api.util; + +import static gregtech.GT_Mod.GT_FML_LOGGER; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Objects; + +import javax.annotation.Nonnull; + +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraft.nbt.NBTTagString; +import net.minecraftforge.common.util.Constants.NBT; +import net.minecraftforge.fluids.FluidStack; + +import cpw.mods.fml.common.FMLCommonHandler; +import gregtech.api.enums.GT_Values; +import gregtech.api.enums.ItemList; +import gregtech.api.objects.GT_ItemStack; +import gregtech.api.util.GT_Recipe.GT_Recipe_AssemblyLine; + +public class GT_AssemblyLineUtils { + + /** + * A cache of Recipes using the Output as Key. + */ + private static final HashMap<GT_ItemStack, GT_Recipe_AssemblyLine> sRecipeCacheByOutput = new HashMap<>(); + /** + * A cache of Recipes using the Recipe Hash String as Key. + */ + private static final HashMap<String, GT_Recipe_AssemblyLine> sRecipeCacheByRecipeHash = new HashMap<>(); + + /** + * Checks the DataStick for deprecated/invalid recipes, updating them as required. + * + * @param aDataStick - The DataStick to process + * @return Is this DataStick now valid with a current recipe? + */ + public static GT_Recipe_AssemblyLine processDataStick(ItemStack aDataStick) { + if (!isItemDataStick(aDataStick)) { + return null; + } + if (doesDataStickNeedUpdate(aDataStick)) { + ItemStack aStickOutput = getDataStickOutput(aDataStick); + if (aStickOutput != null) { + GT_Recipe_AssemblyLine aIntendedRecipe = findAssemblyLineRecipeByOutput(aStickOutput); + if (aIntendedRecipe != null && setAssemblyLineRecipeOnDataStick(aDataStick, aIntendedRecipe)) + return aIntendedRecipe; + } + } + return null; + } + + /** + * Finds an Assembly Line recipe from a DataStick. + * + * @param aDataStick - The DataStick to check. + * @return The GT_Recipe_AssemblyLine recipe contained on the DataStick, if any. + */ + public static GT_Recipe_AssemblyLine findAssemblyLineRecipeFromDataStick(ItemStack aDataStick) { + return findAssemblyLineRecipeFromDataStick(aDataStick, false).getRecipe(); + } + + /** + * Finds an Assembly Line recipe from a DataStick. + * + * @param aDataStick - The DataStick to check. + * @param aReturnBuiltRecipe - Do we return a GT_Recipe_AssemblyLine built from the data on the Data Stick instead + * of searching the Recipe Map? + * @return The GT_Recipe_AssemblyLine recipe contained on the DataStick, if any. + */ + @Nonnull + public static LookupResult findAssemblyLineRecipeFromDataStick(ItemStack aDataStick, boolean aReturnBuiltRecipe) { + if (!isItemDataStick(aDataStick) || !doesDataStickHaveOutput(aDataStick)) { + return LookupResultType.INVALID_STICK.getResult(); + } + List<ItemStack> aInputs = new ArrayList<>(16); + ItemStack aOutput = getDataStickOutput(aDataStick); + List<List<ItemStack>> mOreDictAlt = new ArrayList<>(16); + List<FluidStack> aFluidInputs = new ArrayList<>(4); + + NBTTagCompound aTag = aDataStick.getTagCompound(); + if (aTag == null) { + return LookupResultType.INVALID_STICK.getResult(); + } + + // Get From Cache + if (doesDataStickHaveRecipeHash(aDataStick)) { + GT_Recipe_AssemblyLine aRecipeFromCache = sRecipeCacheByRecipeHash.get(getHashFromDataStack(aDataStick)); + if (aRecipeFromCache != null && GT_Utility.areStacksEqual(aOutput, aRecipeFromCache.mOutput)) { + return LookupResultType.VALID_STACK_AND_VALID_HASH.getResult(aRecipeFromCache); + } // else: no cache, or the old recipe run into a hash collision with a different new recipe + } + + for (int i = 0; i < 16; i++) { + int count = aTag.getInteger("a" + i); + if (!aTag.hasKey("" + i) && count <= 0) { + continue; + } + + List<ItemStack> tAltCurrent = new ArrayList<>(); + for (int j = 0; j < count; j++) { + ItemStack tLoaded = GT_Utility.loadItem(aTag, "a" + i + ":" + j); + if (tLoaded == null) { + continue; + } + tAltCurrent.add(tLoaded); + if (GT_Values.D1) { + GT_FML_LOGGER.info("Item Alt " + i + " : " + tLoaded.getUnlocalizedName()); + } + } + mOreDictAlt.add(tAltCurrent); + ItemStack tLoaded = GT_Utility.loadItem(aTag, "" + i); + if (tLoaded == null) { + continue; + } + aInputs.add(tLoaded); + if (GT_Values.D1) { + GT_FML_LOGGER.info("Item " + i + " : " + tLoaded.getUnlocalizedName()); + } + } + + if (GT_Values.D1) { + GT_FML_LOGGER.info("All Items done, start fluid check"); + } + for (int i = 0; i < 4; i++) { + if (!aTag.hasKey("f" + i)) continue; + FluidStack tLoaded = GT_Utility.loadFluid(aTag, "f" + i); + if (tLoaded == null) continue; + aFluidInputs.add(tLoaded); + if (GT_Values.D1) { + GT_FML_LOGGER.info("Fluid " + i + " " + tLoaded.getUnlocalizedName()); + } + } + if (!aTag.hasKey("output") || !aTag.hasKey("time") + || aTag.getInteger("time") <= 0 + || !aTag.hasKey("eu") + || !GT_Utility.isStackValid(aOutput)) { + return LookupResultType.INVALID_STICK.getResult(); + } + if (GT_Values.D1) { + GT_FML_LOGGER.info("Found Data Stick recipe"); + } + + int aTime = aTag.getInteger("time"); + int aEU = aTag.getInteger("eu"); + + // Try build a recipe instance + if (aReturnBuiltRecipe) { + return LookupResultType.VALID_STACK_AND_VALID_HASH.getResult( + new GT_Recipe_AssemblyLine( + null, + 0, + aInputs.toArray(new ItemStack[0]), + aFluidInputs.toArray(new FluidStack[0]), + aOutput, + aTime, + aEU)); + } + + for (GT_Recipe_AssemblyLine aRecipe : GT_Recipe.GT_Recipe_AssemblyLine.sAssemblylineRecipes) { + if (aRecipe.mEUt != aEU || aRecipe.mDuration != aTime) continue; + if (!GT_Utility.areStacksEqual(aOutput, aRecipe.mOutput, true)) continue; + if (!GT_Utility.areStackListsEqual(Arrays.asList(aRecipe.mInputs), aInputs, false, true)) continue; + if (!Objects.equals(Arrays.asList(aRecipe.mFluidInputs), aFluidInputs)) continue; + if (!areStacksEqual(aRecipe.mOreDictAlt, mOreDictAlt)) continue; + + // Cache it + String aRecipeHash = generateRecipeHash(aRecipe); + sRecipeCacheByRecipeHash.put(aRecipeHash, aRecipe); + sRecipeCacheByOutput.put(new GT_ItemStack(aRecipe.mOutput), aRecipe); + if (doesDataStickHaveRecipeHash(aDataStick)) { + String aStickHash = getHashFromDataStack(aDataStick); + if (aRecipeHash.equals(aStickHash)) + return LookupResultType.VALID_STACK_AND_VALID_HASH.getResult(aRecipe); + } + return LookupResultType.VALID_STACK_AND_VALID_RECIPE.getResult(aRecipe); + } + return LookupResultType.VALID_STACK_BUT_INVALID_RECIPE.getResult(); + } + + private static boolean areStacksEqual(ItemStack[][] lhs, List<List<ItemStack>> rhs) { + for (int i = 0; i < lhs.length; i++) { + if (!areStacksEqual(lhs[i], rhs.get(i))) return false; + } + return true; + } + + private static boolean areStacksEqual(ItemStack[] lhs, List<ItemStack> rhs) { + return lhs == null ? rhs.isEmpty() + : !rhs.isEmpty() && GT_Utility.areStackListsEqual(Arrays.asList(lhs), rhs, false, true); + } + + /** + * Finds a GT_Recipe_AssemblyLine based on the expected output ItemStack. + * + * @param aOutput - The Output of a GT_Recipe_AssemblyLine. + * @return First found GT_Recipe_AssemblyLine with matching output. + */ + public static GT_Recipe_AssemblyLine findAssemblyLineRecipeByOutput(ItemStack aOutput) { + if (aOutput == null) { + return null; + } + + // Check the cache + GT_ItemStack aCacheStack = new GT_ItemStack(aOutput); + GT_Recipe_AssemblyLine aRecipeFromCache = sRecipeCacheByOutput.get(aCacheStack); + if (aRecipeFromCache != null) { + return aRecipeFromCache; + } + + // Iterate all recipes and return the first matching based on Output. + for (GT_Recipe_AssemblyLine aRecipe : GT_Recipe.GT_Recipe_AssemblyLine.sAssemblylineRecipes) { + ItemStack aRecipeOutput = aRecipe.mOutput; + if (GT_Utility.areStacksEqual(aRecipeOutput, aOutput)) { + // Cache it to prevent future iterations of all recipes + sRecipeCacheByOutput.put(aCacheStack, aRecipe); + sRecipeCacheByRecipeHash.put(generateRecipeHash(aRecipe), aRecipe); + return aRecipe; + } + } + return null; + } + + /** + * @param aRecipe - The recipe to generate a Recipe Hash String from. + * @return The Recipe Hash String. + */ + public static String generateRecipeHash(GT_Recipe_AssemblyLine aRecipe) { + String aHash = "Invalid.Recipe.Hash"; + if (aRecipe != null) { + aHash = "Hash." + aRecipe.getPersistentHash(); + } + return aHash; + } + + /** + * @param aRecipe - The recipe to add to internal caches + * @throws IllegalArgumentException if given recipe collide with any existing recipe in the cache + */ + public static void addRecipeToCache(GT_Recipe_AssemblyLine aRecipe) { + if (aRecipe != null) { + String aHash = "Hash." + aRecipe.getPersistentHash(); + GT_Recipe_AssemblyLine existing = sRecipeCacheByOutput.put(new GT_ItemStack(aRecipe.mOutput), aRecipe); + if (existing != null) throw new IllegalArgumentException("Duplicate assline recipe for " + aRecipe.mOutput); + existing = sRecipeCacheByRecipeHash.put(aHash, aRecipe); + if (existing != null && !existing.equals(aRecipe)) + throw new IllegalArgumentException("Recipe hash collision for " + aRecipe + " and " + existing); + } + } + + /** + * @param aHash - Recipe hash String, may be null but will just be treated as invalid. + * @return Is this Recipe Hash String valid? + */ + public static boolean isValidHash(String aHash) { + if (aHash != null && aHash.length() > 0) { + // persistent hash can never be 0 + return !aHash.equals("Invalid.Recipe.Hash") && !aHash.equals("Hash.0"); + } + return false; + } + + /** + * @param aStack - The ItemStack to check. + * @return Is this ItemStack a Data Stick? + */ + public static boolean isItemDataStick(ItemStack aStack) { + return GT_Utility.isStackValid(aStack) && ItemList.Tool_DataStick.isStackEqual(aStack, false, true); + } + + /** + * @param aDataStick - The Data Stick to check. + * @return Does this Data Stick have a valid output ItemStack? + */ + public static boolean doesDataStickHaveOutput(ItemStack aDataStick) { + return isItemDataStick(aDataStick) && aDataStick.hasTagCompound() + && aDataStick.getTagCompound() + .hasKey("output"); + } + + /** + * @param aDataStick - The Data Stick to check. + * @return Does this Data Stick need recipe data updated. + */ + public static boolean doesDataStickNeedUpdate(ItemStack aDataStick) { + if (isItemDataStick(aDataStick) && doesDataStickHaveRecipeHash(aDataStick)) { + String aStickHash = getHashFromDataStack(aDataStick); + if (isValidHash(aStickHash) && doesDataStickHaveOutput(aDataStick)) { + ItemStack aStickOutput = getDataStickOutput(aDataStick); + GT_Recipe_AssemblyLine aIntendedRecipe = findAssemblyLineRecipeByOutput(aStickOutput); + return !aStickHash.equals(generateRecipeHash(aIntendedRecipe)); + } + } + return true; + } + + /** + * @param aDataStick - The Data Stick to check. + * @return Does this have a Recipe Hash String at all? + */ + public static boolean doesDataStickHaveRecipeHash(ItemStack aDataStick) { + if (isItemDataStick(aDataStick) && aDataStick.hasTagCompound()) { + NBTTagCompound aNBT = aDataStick.getTagCompound(); + return aNBT.hasKey("Data.Recipe.Hash") && !aNBT.getString("Data.Recipe.Hash") + .equals("Hash.0"); + } + return false; + } + + /** + * Get the Output ItemStack from a Data Stick. + * + * @param aDataStick - The Data Stick to check. + * @return Output ItemStack contained on the Data Stick. + */ + public static ItemStack getDataStickOutput(ItemStack aDataStick) { + if (doesDataStickHaveOutput(aDataStick)) { + return GT_Utility.loadItem(aDataStick.getTagCompound(), "output"); + } + return null; + } + + /** + * @param aDataStick - The Data Stick to process. + * @return The stored Recipe Hash String on the Data Stick, will return an invalid Hash if one is not found. + * <p> + * The hash will be guaranteed to pass isValidHash(). + * <p> + * Will not return Null. + */ + public static String getHashFromDataStack(ItemStack aDataStick) { + if (isItemDataStick(aDataStick) && aDataStick.hasTagCompound()) { + NBTTagCompound aNBT = aDataStick.getTagCompound(); + if (aNBT.hasKey("Data.Recipe.Hash", NBT.TAG_STRING)) { + String hash = aNBT.getString("Data.Recipe.Hash"); + if (isValidHash(hash)) return hash; + } + } + return "Invalid.Recipe.Hash"; + } + + /** + * + * @param aDataStick - The Data Stick to update. + * @param aRecipeHash - The Recipe Hash String to update with. + * @return Did we update the Recipe Hash String on the Data Stick? + */ + public static boolean setRecipeHashOnDataStick(ItemStack aDataStick, String aRecipeHash) { + if (isItemDataStick(aDataStick) && aDataStick.hasTagCompound()) { + NBTTagCompound aNBT = aDataStick.getTagCompound(); + aNBT.setString("Data.Recipe.Hash", aRecipeHash); + aDataStick.setTagCompound(aNBT); + return true; + } + return false; + } + + /** + * + * @param aDataStick - The Data Stick to update. + * @param aNewRecipe - The New GT_Recipe_AssemblyLine recipe to update it with. + * @return Did we set the new recipe data & Recipe Hash String on the Data Stick? + */ + public static boolean setAssemblyLineRecipeOnDataStick(ItemStack aDataStick, GT_Recipe_AssemblyLine aNewRecipe) { + if (isItemDataStick(aDataStick)) { + String s = aNewRecipe.mOutput.getDisplayName(); + if (FMLCommonHandler.instance() + .getEffectiveSide() + .isServer()) { + s = GT_Assemblyline_Server.lServerNames.get(aNewRecipe.mOutput.getDisplayName()); + if (s == null) { + s = aNewRecipe.mOutput.getDisplayName(); + } + } + + String aHash = generateRecipeHash(aNewRecipe); + if (GT_Values.D1) { + GT_Recipe_AssemblyLine aOldRecipe = findAssemblyLineRecipeFromDataStick(aDataStick, true).recipe; + GT_FML_LOGGER.info( + "Updating data stick: " + aDataStick.getDisplayName() + + " | Old Recipe Hash: " + + generateRecipeHash(aOldRecipe) + + ", New Recipe Hash: " + + aHash); + } + + String author = "Assembling Line Recipe Generator"; + String displayName = null; + if (aDataStick.hasTagCompound()) { + NBTTagCompound tag = aDataStick.getTagCompound(); + if (tag.hasKey("author", NBT.TAG_STRING)) { + author = tag.getString("author"); + } + if (tag.hasKey("display", NBT.TAG_COMPOUND)) { + NBTTagCompound displayTag = tag.getCompoundTag("display"); + if (displayTag.hasKey("Name", NBT.TAG_STRING)) displayName = displayTag.getString("Name"); + } + } + + // remove possible old NBTTagCompound + aDataStick.setTagCompound(new NBTTagCompound()); + if (displayName != null) aDataStick.setStackDisplayName(displayName); + if (GT_Values.D1) { + GT_Utility.ItemNBT.setBookTitle(aDataStick, s + " Construction Data (" + aHash + ")"); + } else { + GT_Utility.ItemNBT.setBookTitle(aDataStick, s + " Construction Data"); + } + + NBTTagCompound tNBT = aDataStick.getTagCompound(); + if (tNBT == null) { + tNBT = new NBTTagCompound(); + } + + tNBT.setTag("output", aNewRecipe.mOutput.writeToNBT(new NBTTagCompound())); + tNBT.setInteger("time", aNewRecipe.mDuration); + tNBT.setInteger("eu", aNewRecipe.mEUt); + for (int i = 0; i < aNewRecipe.mInputs.length; i++) { + tNBT.setTag("" + i, aNewRecipe.mInputs[i].writeToNBT(new NBTTagCompound())); + } + for (int i = 0; i < aNewRecipe.mOreDictAlt.length; i++) { + if (aNewRecipe.mOreDictAlt[i] != null && aNewRecipe.mOreDictAlt[i].length > 0) { + tNBT.setInteger("a" + i, aNewRecipe.mOreDictAlt[i].length); + for (int j = 0; j < aNewRecipe.mOreDictAlt[i].length; j++) { + tNBT.setTag("a" + i + ":" + j, aNewRecipe.mOreDictAlt[i][j].writeToNBT(new NBTTagCompound())); + } + } + } + for (int i = 0; i < aNewRecipe.mFluidInputs.length; i++) { + tNBT.setTag("f" + i, aNewRecipe.mFluidInputs[i].writeToNBT(new NBTTagCompound())); + } + tNBT.setString("author", author); + NBTTagList tNBTList = new NBTTagList(); + s = aNewRecipe.mOutput.getDisplayName(); + if (FMLCommonHandler.instance() + .getEffectiveSide() + .isServer()) { + s = GT_Assemblyline_Server.lServerNames.get(aNewRecipe.mOutput.getDisplayName()); + if (s == null) s = aNewRecipe.mOutput.getDisplayName(); + } + tNBTList.appendTag( + new NBTTagString( + "Construction plan for " + aNewRecipe.mOutput.stackSize + + " " + + s + + ". Needed EU/t: " + + aNewRecipe.mEUt + + " Production time: " + + (aNewRecipe.mDuration / 20))); + for (int i = 0; i < aNewRecipe.mInputs.length; i++) { + if (aNewRecipe.mOreDictAlt[i] != null) { + int count = 0; + StringBuilder tBuilder = new StringBuilder("Input Bus " + (i + 1) + ": "); + for (ItemStack tStack : aNewRecipe.mOreDictAlt[i]) { + if (tStack != null) { + s = tStack.getDisplayName(); + if (FMLCommonHandler.instance() + .getEffectiveSide() + .isServer()) { + s = GT_Assemblyline_Server.lServerNames.get(tStack.getDisplayName()); + if (s == null) s = tStack.getDisplayName(); + } + + tBuilder.append(count == 0 ? "" : "\nOr ") + .append(tStack.stackSize) + .append(" ") + .append(s); + count++; + } + } + if (count > 0) tNBTList.appendTag(new NBTTagString(tBuilder.toString())); + } else if (aNewRecipe.mInputs[i] != null) { + s = aNewRecipe.mInputs[i].getDisplayName(); + if (FMLCommonHandler.instance() + .getEffectiveSide() + .isServer()) { + s = GT_Assemblyline_Server.lServerNames.get(aNewRecipe.mInputs[i].getDisplayName()); + if (s == null) s = aNewRecipe.mInputs[i].getDisplayName(); + } + tNBTList.appendTag( + new NBTTagString("Input Bus " + (i + 1) + ": " + aNewRecipe.mInputs[i].stackSize + " " + s)); + } + } + for (int i = 0; i < aNewRecipe.mFluidInputs.length; i++) { + if (aNewRecipe.mFluidInputs[i] != null) { + s = aNewRecipe.mFluidInputs[i].getLocalizedName(); + if (FMLCommonHandler.instance() + .getEffectiveSide() + .isServer()) { + s = GT_Assemblyline_Server.lServerNames.get(aNewRecipe.mFluidInputs[i].getLocalizedName()); + if (s == null) s = aNewRecipe.mFluidInputs[i].getLocalizedName(); + } + tNBTList.appendTag( + new NBTTagString( + "Input Hatch " + (i + 1) + ": " + aNewRecipe.mFluidInputs[i].amount + "L " + s)); + } + } + tNBT.setTag("pages", tNBTList); + tNBT.setLong("lastUpdate", System.currentTimeMillis()); + aDataStick.setTagCompound(tNBT); + // Set recipe hash + setRecipeHashOnDataStick(aDataStick, aHash); + return true; + } + return false; + } + + public enum LookupResultType { + + INVALID_STICK(true), + VALID_STACK_BUT_INVALID_RECIPE(true), + VALID_STACK_AND_VALID_RECIPE(false), + VALID_STACK_AND_VALID_HASH(false); + + private final boolean recipeNull; + private LookupResult singletonResult; + + LookupResultType(boolean recipeNull) { + this.recipeNull = recipeNull; + } + + public LookupResult getResult() { + if (!recipeNull) throw new IllegalArgumentException("This result type require a nonnull recipe"); + if (singletonResult == null) singletonResult = new LookupResult(null, this); + return singletonResult; + } + + public LookupResult getResult(GT_Recipe_AssemblyLine recipe) { + if ((recipe == null) != recipeNull) + throw new IllegalArgumentException("This result type does not allow given input"); + return new LookupResult(recipe, this); + } + } + + public static class LookupResult { + + private final GT_Recipe_AssemblyLine recipe; + private final LookupResultType type; + + LookupResult(GT_Recipe_AssemblyLine recipe, LookupResultType type) { + this.recipe = recipe; + this.type = type; + } + + public GT_Recipe_AssemblyLine getRecipe() { + return recipe; + } + + public LookupResultType getType() { + return type; + } + } +} diff --git a/src/main/java/gregtech/api/util/GT_Assemblyline_Server.java b/src/main/java/gregtech/api/util/GT_Assemblyline_Server.java new file mode 100644 index 0000000000..4c0348683a --- /dev/null +++ b/src/main/java/gregtech/api/util/GT_Assemblyline_Server.java @@ -0,0 +1,297 @@ +package gregtech.api.util; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; + +import net.minecraftforge.common.config.ConfigCategory; +import net.minecraftforge.common.config.Configuration; +import net.minecraftforge.common.config.Property; + +import cpw.mods.fml.common.event.FMLPreInitializationEvent; +import gregtech.api.GregTech_API; +import gregtech.api.enums.Materials; +import gregtech.api.enums.MaterialsBotania; + +public class GT_Assemblyline_Server { + + public static LinkedHashMap<String, String> lServerNames = new LinkedHashMap<>(); + private static LinkedHashMap<String, String> internal2 = new LinkedHashMap<>(), internal3 = new LinkedHashMap<>(), + internal4 = new LinkedHashMap<>(); + private static HashMap<String, Property> internal = new HashMap<>(); + + public static void fillMap(FMLPreInitializationEvent aEvent) { + Configuration conf = GT_LanguageManager.sEnglishFile; + + ConfigCategory cat = conf.getCategory("languagefile"); + internal.putAll(cat.getValues()); + for (Map.Entry<String, Property> entry : internal.entrySet()) { + try { + String s = entry.getValue() + .getString() + .replaceAll("%", ""); + + if (entry.getKey() + .contains("metaitem") && s.contains("material")) internal2.put(entry.getKey(), s); + else if (entry.getKey() + .contains("blockmachines") && s.contains("material")) internal3.put(entry.getKey(), s); + else if ((entry.getKey() + .contains("blockores") + || (entry.getKey() + .contains("blockmetal") + || entry.getKey() + .contains("blockgem"))) + && s.contains("material")) internal4.put(entry.getKey(), s); + else lServerNames.put(entry.getKey(), s); + } catch (Exception ignored) {} + } + for (Map.Entry<String, String> entry : internal2.entrySet()) { + try { + if (entry.getKey() + .contains("name")) { + int i = Integer.parseInt( + entry.getKey() + .substring( + "gt.metaitem.01.".length(), + entry.getKey() + .length() - ".name".length())); + i = i % 1000; + if (GregTech_API.sGeneratedMaterials[i] != null) lServerNames.put( + entry.getKey(), + entry.getValue() + .replace("material", GregTech_API.sGeneratedMaterials[i].toString())); + else lServerNames.put(entry.getKey(), null); + } + } catch (Exception ignored) {} + } + for (Map.Entry<String, String> entry : internal3.entrySet()) { + try { + if (entry.getKey() + .contains("cable")) + lServerNames.put( + entry.getKey(), + entry.getValue() + .replace( + "material", + entry.getKey() + .substring( + "gt.blockmachines.cable.".length(), + entry.getKey() + .length() - ".01.name".length()))); + else if (entry.getKey() + .contains("gt_frame_")) + lServerNames.put( + entry.getKey(), + entry.getValue() + .replace( + "material", + entry.getKey() + .substring( + "gt.blockmachines.gt_frame_".length(), + entry.getKey() + .length() - ".name".length()))); + else if (entry.getKey() + .contains("gt_pipe_")) { + if (!entry.getKey() + .contains("_huge") + && !entry.getKey() + .contains("_large") + && !entry.getKey() + .contains("_nonuple") + && !entry.getKey() + .contains("_quadruple") + && !entry.getKey() + .contains("_small") + && !entry.getKey() + .contains("_tiny")) + lServerNames.put( + entry.getKey(), + entry.getValue() + .replace( + "material", + entry.getKey() + .substring( + "gt.blockmachines.gt_pipe_".length(), + entry.getKey() + .length() - ".name".length()))); + else if (entry.getKey() + .contains("_huge") + || entry.getKey() + .contains("_tiny")) + lServerNames.put( + entry.getKey(), + entry.getValue() + .replace( + "material", + entry.getKey() + .substring( + "gt.blockmachines.gt_pipe_".length(), + entry.getKey() + .length() - "_tiny.name".length()))); + else if (entry.getKey() + .contains("_large") + || entry.getKey() + .contains("_small")) + lServerNames.put( + entry.getKey(), + entry.getValue() + .replace( + "material", + entry.getKey() + .substring( + "gt.blockmachines.gt_pipe_".length(), + entry.getKey() + .length() - "_large.name".length()))); + else if (entry.getKey() + .contains("_nonuple")) + lServerNames.put( + entry.getKey(), + entry.getValue() + .replace( + "material", + entry.getKey() + .substring( + "gt.blockmachines.gt_pipe_".length(), + entry.getKey() + .length() - "_nonuple.name".length()))); + else if (entry.getKey() + .contains("_quadruple")) + lServerNames.put( + entry.getKey(), + entry.getValue() + .replace( + "material", + entry.getKey() + .substring( + "gt.blockmachines.gt_pipe_".length(), + entry.getKey() + .length() - "_quadruple.name".length()))); + } else if (entry.getKey() + .contains("wire")) + lServerNames.put( + entry.getKey(), + entry.getValue() + .replace( + "material", + entry.getKey() + .substring( + "gt.blockmachines.wire.".length(), + entry.getKey() + .length() - ".01.name".length()))); + else lServerNames.put(entry.getKey(), entry.getValue()); + } catch (Exception ignored) {} + } + for (Map.Entry<String, String> entry : internal4.entrySet()) { + try { + if (entry.getKey() + .contains("blockores")) { + int i = Integer.parseInt( + entry.getKey() + .substring( + "gt.blockores.".length(), + entry.getKey() + .length() - ".name".length())); + i = i % 1000; + if (GregTech_API.sGeneratedMaterials[i] != null) lServerNames.put( + entry.getKey(), + entry.getValue() + .replace("material", GregTech_API.sGeneratedMaterials[i].toString())); + else lServerNames.put(entry.getKey(), null); + } else if (entry.getKey() + .contains("blockmetal")) { + Materials[] mMats = null; + String t = entry.getKey() + .substring("gt.blockmetal".length()); + t = t.substring(0, 1); + int i = Integer.parseInt(t); + switch (i) { + case 1 -> mMats = new Materials[] { Materials.Adamantium, Materials.Aluminium, + Materials.Americium, Materials.AnnealedCopper, Materials.Antimony, Materials.Arsenic, + Materials.AstralSilver, Materials.BatteryAlloy, Materials.Beryllium, Materials.Bismuth, + Materials.BismuthBronze, Materials.BlackBronze, Materials.BlackSteel, + Materials.BlueAlloy, Materials.BlueSteel, Materials.Brass }; + case 2 -> mMats = new Materials[] { Materials.Bronze, Materials.Caesium, Materials.Cerium, + Materials.Chrome, Materials.ChromiumDioxide, Materials.Cobalt, Materials.CobaltBrass, + Materials.Copper, Materials.Cupronickel, Materials.DamascusSteel, Materials.DarkIron, + Materials.DeepIron, Materials.Desh, Materials.Duranium, Materials.Dysprosium, + Materials.Electrum }; + case 3 -> mMats = new Materials[] { Materials.ElectrumFlux, Materials.Enderium, + Materials.Erbium, Materials.Europium, Materials.FierySteel, Materials.Gadolinium, + Materials.Gallium, Materials.Holmium, Materials.HSLA, Materials.Indium, + Materials.InfusedGold, Materials.Invar, Materials.Iridium, Materials.IronMagnetic, + Materials.IronWood, Materials.Kanthal }; + case 4 -> mMats = new Materials[] { Materials.Knightmetal, Materials.Lanthanum, + Materials.Lead, Materials.Lutetium, Materials.Magnalium, Materials.Magnesium, + Materials.Manganese, Materials.MeteoricIron, Materials.MeteoricSteel, Materials.Trinium, + Materials.Mithril, Materials.Molybdenum, Materials.Naquadah, Materials.NaquadahAlloy, + Materials.NaquadahEnriched, Materials.Naquadria }; + case 5 -> mMats = new Materials[] { Materials.Neodymium, Materials.NeodymiumMagnetic, + Materials.Neutronium, Materials.Nichrome, Materials.Nickel, Materials.Niobium, + Materials.NiobiumNitride, Materials.NiobiumTitanium, Materials.Osmiridium, + Materials.Osmium, Materials.Palladium, Materials.PigIron, Materials.Platinum, + Materials.Plutonium, Materials.Plutonium241, Materials.Praseodymium }; + case 6 -> mMats = new Materials[] { Materials.Promethium, Materials.RedAlloy, + Materials.RedSteel, Materials.RoseGold, Materials.Rubidium, Materials.Samarium, + Materials.Scandium, Materials.ShadowIron, Materials.ShadowSteel, Materials.Silicon, + Materials.Silver, Materials.SolderingAlloy, Materials.StainlessSteel, Materials.Steel, + Materials.SteelMagnetic, Materials.SterlingSilver }; + case 7 -> mMats = new Materials[] { Materials.Sunnarium, Materials.Tantalum, + Materials.Tellurium, Materials.Terbium, Materials.Thaumium, Materials.Thorium, + Materials.Thulium, Materials.Tin, Materials.TinAlloy, Materials.Titanium, + Materials.Tritanium, Materials.Tungsten, Materials.TungstenSteel, Materials.Ultimet, + Materials.Uranium, Materials.Uranium235 }; + case 8 -> mMats = new Materials[] { Materials.Vanadium, Materials.VanadiumGallium, + Materials.WroughtIron, Materials.Ytterbium, Materials.Yttrium, + Materials.YttriumBariumCuprate, Materials.Zinc, Materials.TungstenCarbide, + Materials.VanadiumSteel, Materials.HSSG, Materials.HSSE, Materials.HSSS, + Materials.Steeleaf, Materials.Ichorium, Materials.Firestone }; + } + t = entry.getKey() + .substring( + "gt.blockmetal1.".length(), + entry.getKey() + .length() - ".name".length()); + i = Integer.parseInt(t); + lServerNames.put(entry.getKey(), "Block of " + mMats[i].toString()); + mMats = null; + } else if (entry.getKey() + .contains("blockgem")) { + Materials[] mMats = null; + String t = entry.getKey() + .substring("gt.blockgem".length()); + t = t.substring(0, 1); + int i = Integer.parseInt(t); + switch (i) { + case 1 -> mMats = new Materials[] { Materials.InfusedAir, Materials.Amber, + Materials.Amethyst, Materials.InfusedWater, Materials.BlueTopaz, + Materials.CertusQuartz, Materials.Dilithium, Materials.EnderEye, + Materials.EnderPearl, Materials.FoolsRuby, Materials.Force, Materials.Forcicium, + Materials.Forcillium, Materials.GreenSapphire, Materials.InfusedFire, + Materials.Jasper, MaterialsBotania.ManaDiamond, + MaterialsBotania.BotaniaDragonstone }; + case 2 -> mMats = new Materials[] { Materials.Lazurite, Materials.Lignite, + Materials.Monazite, Materials.Niter, Materials.Olivine, Materials.Opal, + Materials.InfusedOrder, Materials.InfusedEntropy, Materials.Phosphorus, + Materials.Quartzite, Materials.GarnetRed, Materials.Ruby, Materials.Sapphire, + Materials.Sodalite, Materials.Tanzanite, Materials.InfusedEarth }; + case 3 -> mMats = new Materials[] { Materials.Topaz, Materials.Vinteum, + Materials.GarnetYellow, Materials.NetherStar, Materials.Charcoal, Materials.Blaze }; + } + t = entry.getKey() + .substring( + "gt.blockgem1.".length(), + entry.getKey() + .length() - ".name".length()); + i = Integer.parseInt(t); + lServerNames.put(entry.getKey(), "Block of " + mMats[i].toString()); + mMats = null; + } + } catch (Exception ignored) {} + } + + internal = null; + internal2 = null; + internal3 = null; + internal4 = null; + } +} diff --git a/src/main/java/gregtech/api/util/GT_BaseCrop.java b/src/main/java/gregtech/api/util/GT_BaseCrop.java new file mode 100644 index 0000000000..d8608c85a0 --- /dev/null +++ b/src/main/java/gregtech/api/util/GT_BaseCrop.java @@ -0,0 +1,311 @@ +package gregtech.api.util; + +import static gregtech.api.enums.GT_Values.E; +import static gregtech.api.enums.Mods.IC2CropPlugin; + +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.List; + +import net.minecraft.block.Block; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.item.ItemStack; +import net.minecraft.tileentity.TileEntity; + +import gregtech.GT_Mod; +import gregtech.api.GregTech_API; +import gregtech.api.enums.ConfigCategories; +import gregtech.api.enums.Materials; +import gregtech.api.enums.OrePrefixes; +import gregtech.api.objects.ItemData; +import gregtech.common.blocks.GT_Block_Ores_Abstract; +import gregtech.common.blocks.GT_TileEntity_Ores; +import ic2.api.crops.CropCard; +import ic2.api.crops.Crops; +import ic2.api.crops.ICropTile; +import speiger.src.crops.api.ICropCardInfo; + +public class GT_BaseCrop extends CropCard implements ICropCardInfo { + + public static ArrayList<GT_BaseCrop> sCropList = new ArrayList<>(); + private String mName = E; + private String mDiscoveredBy = "Gregorius Techneticies"; + private String[] mAttributes; + private int mTier = 0; + private int mMaxSize = 0; + private int mAfterHarvestSize = 0; + private int mHarvestSize = 0; + private final int[] mStats = new int[5]; + private final int mGrowthSpeed = 0; + private ItemStack mDrop = null; + private ItemStack[] mSpecialDrops = null; + private Materials mBlock = null; + private static boolean bIc2NeiLoaded = IC2CropPlugin.isModLoaded(); + + /** + * To create new Crops + * + * @param aID Default ID + * @param aCropName Name of the Crop + * @param aDiscoveredBy The one who discovered the Crop + * @param aDrop The Item which is dropped by the Crop. must be != null + * @param aBaseSeed Baseseed to plant this Crop. null == crossbreed only + * @param aTier tier of the Crop. forced to be >= 1 + * @param aMaxSize maximum Size of the Crop. forced to be >= 3 + * @param aGrowthSpeed how fast the Crop grows. if < 0 then its set to Tier*300 + * @param aHarvestSize the size the Crop needs to be harvested. forced to be between 2 and max size + */ + public GT_BaseCrop(int aID, String aCropName, String aDiscoveredBy, ItemStack aBaseSeed, int aTier, int aMaxSize, + int aGrowthSpeed, int aAfterHarvestSize, int aHarvestSize, int aStatChemical, int aStatFood, int aStatDefensive, + int aStatColor, int aStatWeed, String[] aAttributes, ItemStack aDrop, ItemStack[] aSpecialDrops) { + new GT_BaseCrop( + aID, + aCropName, + aDiscoveredBy, + aBaseSeed, + aTier, + aMaxSize, + aGrowthSpeed, + aAfterHarvestSize, + aHarvestSize, + aStatChemical, + aStatFood, + aStatDefensive, + aStatColor, + aStatWeed, + aAttributes, + null, + aDrop, + aSpecialDrops); + } + + /** + * To create new Crops + * + * @param aID Default ID + * @param aCropName Name of the Crop + * @param aDiscoveredBy The one who discovered the Crop + * @param aDrop The Item which is dropped by the Crop. must be != null + * @param aBaseSeed Baseseed to plant this Crop. null == crossbreed only + * @param aTier tier of the Crop. forced to be >= 1 + * @param aMaxSize maximum Size of the Crop. forced to be >= 3 + * @param aGrowthSpeed how fast the Crop grows. if < 0 then its set to Tier*300 + * @param aHarvestSize the size the Crop needs to be harvested. forced to be between 2 and max size + * @param aBlock the block below needed for crop to grow. If null no block needed + */ + public GT_BaseCrop(int aID, String aCropName, String aDiscoveredBy, ItemStack aBaseSeed, int aTier, int aMaxSize, + int aGrowthSpeed, int aAfterHarvestSize, int aHarvestSize, int aStatChemical, int aStatFood, int aStatDefensive, + int aStatColor, int aStatWeed, String[] aAttributes, Materials aBlock, ItemStack aDrop, + ItemStack[] aSpecialDrops) { + mName = aCropName; + aID = GT_Config.addIDConfig(ConfigCategories.IDs.crops, mName.replaceAll(" ", "_"), aID); + if (aDiscoveredBy != null && !aDiscoveredBy.equals(E)) mDiscoveredBy = aDiscoveredBy; + if (aDrop != null && aID > 0 && aID < 256) { + mDrop = GT_Utility.copyOrNull(aDrop); + mSpecialDrops = aSpecialDrops; + mTier = Math.max(1, aTier); + mMaxSize = Math.max(3, aMaxSize); + mHarvestSize = Math.min(Math.max(aHarvestSize, 2), mMaxSize); + mAfterHarvestSize = Math.min(Math.max(aAfterHarvestSize, 1), mMaxSize - 1); + mStats[0] = aStatChemical; + mStats[1] = aStatFood; + mStats[2] = aStatDefensive; + mStats[3] = aStatColor; + mStats[4] = aStatWeed; + mAttributes = aAttributes; + mBlock = aBlock; + if (!Crops.instance.registerCrop(this, aID)) + throw new GT_ItsNotMyFaultException("Make sure the Crop ID is valid!"); + if (aBaseSeed != null) Crops.instance.registerBaseSeed(aBaseSeed, this, 1, 1, 1, 1); + sCropList.add(this); + } + if (bIc2NeiLoaded) { + try { + Class.forName("speiger.src.crops.api.CropPluginAPI") + .getMethod("registerCropInfo", Class.forName("speiger.src.crops.api.ICropCardInfo")) + .invoke( + Class.forName("speiger.src.crops.api.CropPluginAPI") + .getField("instance"), + this); + } catch (IllegalAccessException | ClassNotFoundException | SecurityException | NoSuchMethodException + | NoSuchFieldException | InvocationTargetException | IllegalArgumentException ex) { + bIc2NeiLoaded = false; + } + } + } + + @Override + public byte getSizeAfterHarvest(ICropTile crop) { + return (byte) mAfterHarvestSize; + } + + @Override + public int growthDuration(ICropTile aCrop) { + if (mGrowthSpeed < 200) return super.growthDuration(aCrop); + return tier() * mGrowthSpeed; + } + + @Override + public int getrootslength(ICropTile crop) { + return 5; + } + + @Override + public String[] attributes() { + return mAttributes; + } + + @Override + public String discoveredBy() { + return mDiscoveredBy; + } + + @Override + public final boolean canGrow(ICropTile aCrop) { + // block check is only performed at the last stage of growth + if (this.needsBlockBelow() && aCrop.getSize() == mMaxSize - 1) { + return isBlockBelow(aCrop); + } + return aCrop.getSize() < maxSize(); + } + + @Override + public final boolean canBeHarvested(ICropTile aCrop) { + return aCrop.getSize() >= mHarvestSize; + } + + @Override + public boolean canCross(ICropTile aCrop) { + return aCrop.getSize() + 2 > maxSize(); + } + + @Override + public int stat(int n) { + if (n < 0 || n >= mStats.length) return 0; + return mStats[n]; + } + + @Override + public String name() { + return mName; + } + + @Override + public int tier() { + return mTier; + } + + @Override + public int maxSize() { + return mMaxSize; + } + + @Override + public ItemStack getGain(ICropTile aCrop) { + int tDrop = 0; + if (mSpecialDrops != null && (tDrop = java.util.concurrent.ThreadLocalRandom.current() + .nextInt(0, (mSpecialDrops.length * 2) + 2)) < mSpecialDrops.length && mSpecialDrops[tDrop] != null) { + return GT_Utility.copyOrNull(mSpecialDrops[tDrop]); + } + return GT_Utility.copyOrNull(mDrop); + } + + @Override + public boolean rightclick(ICropTile aCrop, EntityPlayer aPlayer) { + if (!canBeHarvested(aCrop)) return false; + return aCrop.harvest(aPlayer instanceof EntityPlayerMP); + } + + @Override + public int getOptimalHavestSize(ICropTile crop) { + return maxSize(); + } + + /** + * Checks if the crop needs a block below it + * + * @return True if the crop needs a block below it to grow to its max size + */ + public boolean needsBlockBelow() { + return GT_Mod.gregtechproxy.mCropNeedBlock && this.mBlock != null; + } + + public boolean isBlockBelow(ICropTile aCrop) { + if (aCrop == null) { + return false; + } + for (int i = 1; i < this.getrootslength(aCrop); i++) { + Block tBlock = aCrop.getWorld() + .getBlock(aCrop.getLocation().posX, aCrop.getLocation().posY - i, aCrop.getLocation().posZ); + if ((tBlock instanceof GT_Block_Ores_Abstract)) { + TileEntity tTileEntity = aCrop.getWorld() + .getTileEntity(aCrop.getLocation().posX, aCrop.getLocation().posY - i, aCrop.getLocation().posZ); + if ((tTileEntity instanceof GT_TileEntity_Ores)) { + Materials tMaterial = GregTech_API.sGeneratedMaterials[(((GT_TileEntity_Ores) tTileEntity).mMetaData + % 1000)]; + if ((tMaterial != null) && (tMaterial != Materials._NULL)) { + return tMaterial == mBlock; + } + } + } else { + int tMetaID = aCrop.getWorld() + .getBlockMetadata(aCrop.getLocation().posX, aCrop.getLocation().posY - i, aCrop.getLocation().posZ); + if (isBlockBelow(new ItemStack(tBlock, 1, tMetaID))) { + return true; + } + } + } + return false; + } + + /** + * An isolated function to check if an item stack is a block that should be below this crop + * + * @param aItem a stack of the block placed under the crop + * @return The result of the check + */ + public boolean isBlockBelow(ItemStack aItem) { + // safety in case someone calls this without checking if we have a block + if (!this.needsBlockBelow()) return true; + + // get material from stack + ItemData tAssociation = GT_OreDictUnificator.getAssociation(aItem); + if (tAssociation == null) return false; + + // return true if it's an ore of the material + // note: some ores don't appear to have associations in testing, naq ore is an example of that + if (tAssociation.mPrefix.toString() + .startsWith("ore") && tAssociation.mMaterial.mMaterial == mBlock) { + return true; + } + + // return true if it's a block of the material + if (tAssociation.mPrefix == OrePrefixes.block && tAssociation.mMaterial.mMaterial == mBlock) { + return true; + } + return false; + } + + @Override + public List<String> getCropInformation() { + if (mBlock != null) { + ArrayList<String> result = new ArrayList<>(1); + result.add( + String.format( + "Requires %s Ore or Block of %s as soil block to reach full growth.", + mBlock.mName, + mBlock.mName)); + return result; + } + return null; + } + + @Override + public ItemStack getDisplayItem() { + if (mSpecialDrops != null && mSpecialDrops[mSpecialDrops.length - 1] != null) { + return GT_Utility.copyOrNull(mSpecialDrops[mSpecialDrops.length - 1]); + } + return GT_Utility.copyOrNull(mDrop); + } +} diff --git a/src/main/java/gregtech/api/util/GT_BlockMap.java b/src/main/java/gregtech/api/util/GT_BlockMap.java new file mode 100644 index 0000000000..9ffe273cac --- /dev/null +++ b/src/main/java/gregtech/api/util/GT_BlockMap.java @@ -0,0 +1,134 @@ +package gregtech.api.util; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.BiFunction; + +import net.minecraft.block.Block; + +import gnu.trove.map.TByteObjectMap; +import gnu.trove.map.hash.TByteObjectHashMap; + +public class GT_BlockMap<V> { + + public static final byte WILDCARD = -1; + private final ConcurrentHashMap<Block, TByteObjectMap<V>> backing = new ConcurrentHashMap<>(); + private int size = 0; + + private TByteObjectMap<V> getSubmap(Block block) { + return backing.computeIfAbsent(block, b -> new TByteObjectHashMap<>()); + } + + /** + * Associate a value with that union key + * + * @param block block + * @param meta meta + * @return old mapping, or null if that doesn't exist + */ + public V put(Block block, byte meta, V value) { + V v = getSubmap(block).put(meta, value); + if (v == null) size++; + return v; + } + + /** + * Associate a value with that union key ONLY IF there isn't a prior EXACT mapping + * + * @param block block + * @param meta meta + * @return old mapping, or null if that doesn't exist + */ + public V putIfAbsent(Block block, byte meta, V value) { + V v = getSubmap(block).putIfAbsent(meta, value); + if (v == null) size++; + return v; + } + + /** + * Associate a value with that union key ONLY IF there isn't a prior EXACT mapping + * + * @param block block + * @param meta meta + * @return old mapping, or null if that doesn't exist + */ + public V computeIfAbsent(Block block, byte meta, BiFunction<Block, Byte, V> function) { + TByteObjectMap<V> submap = getSubmap(block); + V v = submap.get(meta); + if (v == null) { + v = function.apply(block, meta); + submap.put(meta, v); + size++; + } + return v; + } + + /** + * Contains an associated value + * + * @param block block + * @param meta meta + * @return current mapping OR wildcard of that mapping exists + */ + public boolean containsKey(Block block, byte meta) { + TByteObjectMap<V> submap = backing.get(block); + if (submap == null) return false; + return submap.containsKey(meta) || submap.containsKey(WILDCARD); + } + + /** + * Get the associated value + * + * @param block block + * @param meta meta + * @return current mapping OR wildcard of that block. null if neither exists + */ + public V get(Block block, byte meta) { + TByteObjectMap<V> submap = backing.get(block); + if (submap == null) return null; + V v = submap.get(meta); + if (v != null) return v; + return submap.get(WILDCARD); + } + + /** + * Remove a mapping + * + * @param block block + * @param meta meta + * @return old value, or null if none + */ + public V remove(Block block, byte meta) { + TByteObjectMap<V> submap = backing.get(block); + if (submap == null) return null; + V v = submap.remove(meta); + if (v != null) { + size--; + if (submap.isEmpty()) backing.remove(block); + } + return v; + } + + /** + * Size of all mappings + * + * @return size + */ + public int size() { + return size; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + GT_BlockMap<?> that = (GT_BlockMap<?>) o; + + return backing.equals(that.backing); + } + + @Override + public int hashCode() { + return backing.hashCode(); + } +} diff --git a/src/main/java/gregtech/api/util/GT_BlockSet.java b/src/main/java/gregtech/api/util/GT_BlockSet.java new file mode 100644 index 0000000000..1d13afd056 --- /dev/null +++ b/src/main/java/gregtech/api/util/GT_BlockSet.java @@ -0,0 +1,39 @@ +package gregtech.api.util; + +import net.minecraft.block.Block; + +public class GT_BlockSet { + + private final GT_BlockMap<Object> backing = new GT_BlockMap<>(); + + public boolean add(Block block, byte meta) { + return backing.put(block, meta, this) != this; + } + + public boolean contains(Block block, byte meta) { + return backing.get(block, meta) == this; + } + + public boolean remove(Block block, byte meta) { + return backing.remove(block, meta) == this; + } + + public int size() { + return backing.size(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + GT_BlockSet that = (GT_BlockSet) o; + + return backing.equals(that.backing); + } + + @Override + public int hashCode() { + return backing.hashCode(); + } +} diff --git a/src/main/java/gregtech/api/util/GT_CLS_Compat.java b/src/main/java/gregtech/api/util/GT_CLS_Compat.java new file mode 100644 index 0000000000..c560435e30 --- /dev/null +++ b/src/main/java/gregtech/api/util/GT_CLS_Compat.java @@ -0,0 +1,157 @@ +package gregtech.api.util; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.Optional; +import java.util.Set; +import java.util.function.Consumer; +import java.util.function.Function; + +import cpw.mods.fml.common.ProgressManager; +import gregtech.GT_Mod; +import gregtech.api.enums.Materials; +import gregtech.common.GT_Proxy; +import gregtech.loaders.postload.GT_PostLoad; + +@SuppressWarnings("rawtypes, unchecked, deprecation") +public class GT_CLS_Compat { + + private static Class alexiilMinecraftDisplayer; + private static Class alexiilProgressDisplayer; + private static Class cpwProgressBar; + + private static Method getLastPercent; + private static Method displayProgress; + + private static Field isReplacingVanillaMaterials; + private static Field isRegisteringGTmaterials; + private static Field progressBarStep; + + static { + // CLS + try { + alexiilMinecraftDisplayer = Class.forName("alexiil.mods.load.MinecraftDisplayer"); + alexiilProgressDisplayer = Class.forName("alexiil.mods.load.ProgressDisplayer"); + } catch (ClassNotFoundException ex) { + GT_Mod.GT_FML_LOGGER.catching(ex); + } + + try { + cpwProgressBar = Class.forName("cpw.mods.fml.common.ProgressManager$ProgressBar"); + } catch (ClassNotFoundException ex) { + GT_Mod.GT_FML_LOGGER.catching(ex); + } + + Optional.ofNullable(alexiilMinecraftDisplayer) + .ifPresent(e -> { + try { + getLastPercent = e.getMethod("getLastPercent"); + isReplacingVanillaMaterials = e.getField("isReplacingVanillaMaterials"); + isRegisteringGTmaterials = e.getField("isRegisteringGTmaterials"); + } catch (NoSuchMethodException | NoSuchFieldException ex) { + GT_Mod.GT_FML_LOGGER.catching(ex); + } + }); + + Optional.ofNullable(alexiilProgressDisplayer) + .ifPresent(e -> { + try { + displayProgress = e.getMethod("displayProgress", String.class, float.class); + } catch (NoSuchMethodException ex) { + GT_Mod.GT_FML_LOGGER.catching(ex); + } + }); + + try { + progressBarStep = cpwProgressBar.getDeclaredField("step"); + progressBarStep.setAccessible(true); + } catch (NoSuchFieldException ex) { + GT_Mod.GT_FML_LOGGER.catching(ex); + } + } + + private GT_CLS_Compat() {} + + private static <T> void registerAndReportProgression(String materialsType, Collection<T> materials, + ProgressManager.ProgressBar progressBar, Function<T, Object> getName, Consumer<T> action) { + int sizeStep = materials.size(); + final long progressionReportsEvery = 100; + final long bakingMsgEvery = 1000; + long nextProgressionReportAt = 0; + long nextBakingMsgAt = 0; + int currentStep = 0; + + for (T m : materials) { + long now = System.currentTimeMillis(); + + if (nextProgressionReportAt < now) { + nextProgressionReportAt = now + progressionReportsEvery; + String materialName = getName.apply(m) + .toString(); + try { + displayProgress.invoke(null, materialName, (float) currentStep / sizeStep); + } catch (IllegalAccessException | InvocationTargetException iae) { + GT_Mod.GT_FML_LOGGER.error("While updating progression", iae); + } + try { + progressBarStep.set(progressBar, currentStep); + } catch (IllegalAccessException iae) { + GT_Mod.GT_FML_LOGGER.error("While updating intermediate progression steps number", iae); + } + progressBar.step(materialName); + } + if (nextBakingMsgAt < now) { + nextBakingMsgAt = now + bakingMsgEvery; + GT_Mod.GT_FML_LOGGER + .info(String.format("%s - Baking: %d%%", materialsType, currentStep * 100 / sizeStep)); + } + action.accept(m); + currentStep += 1; + } + GT_Mod.GT_FML_LOGGER.info(String.format("%s - Baking: Done", materialsType)); + try { + progressBarStep.set(progressBar, currentStep); + } catch (IllegalAccessException iae) { + GT_Mod.GT_FML_LOGGER.error("While updating final progression steps number", iae); + } + } + + public static void stepMaterialsCLS(Collection<GT_Proxy.OreDictEventContainer> mEvents, + ProgressManager.ProgressBar progressBar) throws IllegalAccessException { + try { + isRegisteringGTmaterials.set(null, true); + } catch (IllegalArgumentException | IllegalAccessException e) { + GT_Mod.GT_FML_LOGGER.catching(e); + } + registerAndReportProgression( + "GregTech materials", + mEvents, + progressBar, + m -> m.mMaterial, + GT_Proxy::registerRecipes); + ProgressManager.pop(progressBar); + isRegisteringGTmaterials.set(null, false); + } + + public static void doActualRegistrationCLS(ProgressManager.ProgressBar progressBar, + Set<Materials> replacedVanillaItemsSet) { + try { + isReplacingVanillaMaterials.set(null, true); + } catch (IllegalArgumentException | IllegalAccessException e) { + GT_Mod.GT_FML_LOGGER.catching(e); + } + registerAndReportProgression( + "Vanilla materials", + replacedVanillaItemsSet, + progressBar, + m -> m.mDefaultLocalName, + GT_PostLoad::doActualRegistration); + } + + public static void pushToDisplayProgress() throws InvocationTargetException, IllegalAccessException { + isReplacingVanillaMaterials.set(null, false); + displayProgress.invoke(null, "Post Initialization: loading GregTech", getLastPercent.invoke(null)); + } +} diff --git a/src/main/java/gregtech/api/util/GT_ChunkAssociatedData.java b/src/main/java/gregtech/api/util/GT_ChunkAssociatedData.java new file mode 100644 index 0000000000..bb73cf77ed --- /dev/null +++ b/src/main/java/gregtech/api/util/GT_ChunkAssociatedData.java @@ -0,0 +1,494 @@ +package gregtech.api.util; + +import static gregtech.api.enums.Mods.GregTech; + +import java.io.DataInput; +import java.io.DataInputStream; +import java.io.DataOutput; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.lang.ref.WeakReference; +import java.lang.reflect.Array; +import java.nio.file.AtomicMoveNotSupportedException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.util.Arrays; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.function.Function; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import javax.annotation.ParametersAreNonnullByDefault; + +import net.minecraft.world.ChunkCoordIntPair; +import net.minecraft.world.World; +import net.minecraft.world.chunk.Chunk; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.world.WorldEvent; + +import org.apache.commons.io.FileUtils; + +import cpw.mods.fml.common.eventhandler.SubscribeEvent; +import gregtech.api.enums.GT_Values; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; + +/** + * A utility to save all kinds of data that is a function of any chunk. + * <p> + * GregTech takes care of saving and loading the data from disk, and an efficient mechanism to locate it. Subclass only + * need to define the exact scheme of each element data (by overriding the three protected abstract method) + * <p> + * Oh, there is no limit on how large your data is, though you'd not have the familiar NBT interface, but DataOutput + * should be reasonably common anyway. + * <p> + * It should be noted this class is NOT thread safe. + * <p> + * Element cannot be null. + * <p> + * TODO: Implement automatic region unloading. + * + * @param <T> data element type + * @author glease + */ +@ParametersAreNonnullByDefault +public abstract class GT_ChunkAssociatedData<T extends GT_ChunkAssociatedData.IData> { + + private static final Map<String, GT_ChunkAssociatedData<?>> instances = new ConcurrentHashMap<>(); + private static final int IO_PARALLELISM = Math.min( + 8, + Math.max( + 1, + Runtime.getRuntime() + .availableProcessors() * 2 + / 3)); + private static final ExecutorService IO_WORKERS = Executors.newWorkStealingPool(IO_PARALLELISM); + private static final Pattern FILE_PATTERN = Pattern.compile("(.+)\\.(-?\\d+)\\.(-?\\d+)\\.dat"); + + static { + // register event handler + new EventHandler(); + } + + protected final String mId; + protected final Class<T> elementtype; + private final int regionLength; + private final int version; + private final boolean saveDefaults; + /** + * Data is stored as a `(world id -> (super region id -> super region data))` hash map. where super region's size is + * determined by regionSize. Here it is called super region, to not confuse with vanilla's regions. + */ + private final Map<Integer, Map<ChunkCoordIntPair, SuperRegion>> masterMap = new ConcurrentHashMap<>(); + + /** + * Initialize this instance. + * + * @param aId An arbitrary, but globally unique identifier for what this data is + * @param elementType The class of this element type. Used to create arrays. + * @param regionLength The length of one super region. Each super region will contain + * {@code regionLength * regionLength} chunks + * @param version An integer marking the version of this data. Useful later when the data's serialized form + * changed. + */ + protected GT_ChunkAssociatedData(String aId, Class<T> elementType, int regionLength, byte version, + boolean saveDefaults) { + if (regionLength * regionLength > Short.MAX_VALUE || regionLength <= 0) + throw new IllegalArgumentException("Region invalid: " + regionLength); + if (!IData.class.isAssignableFrom(elementType)) throw new IllegalArgumentException("Data type invalid"); + if (aId.contains(".")) throw new IllegalArgumentException("ID cannot contains dot"); + this.mId = aId; + this.elementtype = elementType; + this.regionLength = regionLength; + this.version = version; + this.saveDefaults = saveDefaults; + if (instances.putIfAbsent(aId, this) != null) + throw new IllegalArgumentException("Duplicate GT_ChunkAssociatedData: " + aId); + } + + private ChunkCoordIntPair getRegionID(int aChunkX, int aChunkZ) { + return new ChunkCoordIntPair(Math.floorDiv(aChunkX, regionLength), Math.floorDiv(aChunkZ, regionLength)); + } + + /** + * Get a reference to data of the chunk that tile entity is in. The returned reference should be mutable. + */ + public final T get(IGregTechTileEntity tileEntity) { + return get(tileEntity.getWorld(), tileEntity.getXCoord() >> 4, tileEntity.getZCoord() >> 4); + } + + public final T get(Chunk chunk) { + return get(chunk.worldObj, chunk.xPosition, chunk.zPosition); + } + + public final T get(World world, ChunkCoordIntPair coord) { + return get(world, coord.chunkXPos, coord.chunkZPos); + } + + public final T get(World world, int chunkX, int chunkZ) { + SuperRegion region = masterMap.computeIfAbsent(world.provider.dimensionId, ignored -> new ConcurrentHashMap<>()) + .computeIfAbsent(getRegionID(chunkX, chunkZ), c -> new SuperRegion(world, c)); + return region.get(Math.floorMod(chunkX, regionLength), Math.floorMod(chunkZ, regionLength)); + } + + protected final void set(World world, int chunkX, int chunkZ, T data) { + SuperRegion region = masterMap.computeIfAbsent(world.provider.dimensionId, ignored -> new ConcurrentHashMap<>()) + .computeIfAbsent(getRegionID(chunkX, chunkZ), c -> new SuperRegion(world, c)); + region.set(Math.floorMod(chunkX, regionLength), Math.floorMod(chunkZ, regionLength), data); + } + + protected final boolean isCreated(int dimId, int chunkX, int chunkZ) { + Map<ChunkCoordIntPair, SuperRegion> dimData = masterMap.getOrDefault(dimId, null); + if (dimData == null) return false; + + SuperRegion region = dimData.getOrDefault(getRegionID(chunkX, chunkZ), null); + if (region == null) return false; + + return region.isCreated(Math.floorMod(chunkX, regionLength), Math.floorMod(chunkZ, regionLength)); + } + + public void clear() { + if (GT_Values.debugWorldData) { + long dirtyRegionCount = masterMap.values() + .stream() + .flatMap( + m -> m.values() + .stream()) + .filter(SuperRegion::isDirty) + .count(); + if (dirtyRegionCount > 0) GT_Log.out.println( + "Clearing ChunkAssociatedData with " + dirtyRegionCount + " regions dirty. Data might have been lost!"); + } + masterMap.clear(); + } + + public void save() { + saveRegions( + masterMap.values() + .stream() + .flatMap( + m -> m.values() + .stream())); + } + + public void save(World world) { + Map<ChunkCoordIntPair, SuperRegion> map = masterMap.get(world.provider.dimensionId); + if (map != null) saveRegions( + map.values() + .stream()); + } + + private void saveRegions(Stream<SuperRegion> stream) { + stream.filter(SuperRegion::isDirty) + .map(c -> (Runnable) c::save) + .map(r -> CompletableFuture.runAsync(r, IO_WORKERS)) + .reduce(CompletableFuture::allOf) + .ifPresent(f -> { + try { + f.get(); + } catch (Exception e) { + GT_Log.err.println("Data save error: " + mId); + e.printStackTrace(GT_Log.err); + } + }); + } + + protected abstract void writeElement(DataOutput output, T element, World world, int chunkX, int chunkZ) + throws IOException; + + protected abstract T readElement(DataInput input, int version, World world, int chunkX, int chunkZ) + throws IOException; + + protected abstract T createElement(World world, int chunkX, int chunkZ); + + /** + * Clear all mappings, regardless of whether they are dirty + */ + public static void clearAll() { + for (GT_ChunkAssociatedData<?> d : instances.values()) d.clear(); + } + + /** + * Save all mappings + */ + public static void saveAll() { + for (GT_ChunkAssociatedData<?> d : instances.values()) d.save(); + } + + /** + * Load data for all chunks for a given world. Current data for that world will be discarded. If this is what you + * intended, call {@link #save(World)} beforehand. + * <p> + * Be aware of the memory consumption though. + */ + protected void loadAll(World w) { + if (GT_Values.debugWorldData && masterMap.containsKey(w.provider.dimensionId)) GT_Log.err.println( + "Reloading ChunkAssociatedData " + mId + " for world " + w.provider.dimensionId + " discards old data!"); + if (!getSaveDirectory(w).isDirectory()) + // nothing to load... + return; + try (Stream<Path> stream = Files.list(getSaveDirectory(w).toPath())) { + Map<ChunkCoordIntPair, SuperRegion> worldData = stream.map(f -> { + Matcher matcher = FILE_PATTERN.matcher( + f.getFileName() + .toString()); + return matcher.matches() ? matcher : null; + }) + .filter(Objects::nonNull) + .filter(m -> mId.equals(m.group(1))) + .map( + m -> CompletableFuture.supplyAsync( + () -> new SuperRegion(w, Integer.parseInt(m.group(2)), Integer.parseInt(m.group(3))), + IO_WORKERS)) + .map(f -> { + try { + return f.get(); + } catch (Exception e) { + GT_Log.err.println("Error loading region"); + e.printStackTrace(GT_Log.err); + return null; + } + }) + .filter(Objects::nonNull) + .collect(Collectors.toMap(SuperRegion::getCoord, Function.identity())); + masterMap.put(w.provider.dimensionId, worldData); + } catch (IOException | UncheckedIOException e) { + GT_Log.err.println("Error loading all region"); + e.printStackTrace(GT_Log.err); + } + } + + protected File getSaveDirectory(World w) { + File base; + if (w.provider.getSaveFolder() == null) base = w.getSaveHandler() + .getWorldDirectory(); + else base = new File( + w.getSaveHandler() + .getWorldDirectory(), + w.provider.getSaveFolder()); + return new File(base, GregTech.ID); + } + + public interface IData { + + /** + * @return Whether the data is different from chunk default + */ + boolean isSameAsDefault(); + } + + protected final class SuperRegion { + + private final T[] data = createData(); + private final File backingStorage; + private final WeakReference<World> world; + /** + * Be aware, this means region coord, not bottom-left chunk coord + */ + private final ChunkCoordIntPair coord; + + private SuperRegion(World world, int regionX, int regionZ) { + this.world = new WeakReference<>(world); + this.coord = new ChunkCoordIntPair(regionX, regionZ); + backingStorage = new File(getSaveDirectory(world), String.format("%s.%d.%d.dat", mId, regionX, regionZ)); + if (backingStorage.isFile()) load(); + } + + private SuperRegion(World world, ChunkCoordIntPair regionCoord) { + this.world = new WeakReference<>(world); + this.coord = regionCoord; + backingStorage = new File( + getSaveDirectory(world), + String.format("%s.%d.%d.dat", mId, regionCoord.chunkXPos, regionCoord.chunkZPos)); + if (backingStorage.isFile()) load(); + } + + @SuppressWarnings("unchecked") + private T[] createData() { + return (T[]) Array.newInstance(elementtype, regionLength * regionLength); + } + + public T get(int subRegionX, int subRegionZ) { + int index = getIndex(subRegionX, subRegionZ); + T datum = data[index]; + if (datum == null) { + World world = Objects.requireNonNull(this.world.get()); + T newElem = createElement( + world, + coord.chunkXPos * regionLength + subRegionX, + coord.chunkZPos * regionLength + subRegionZ); + data[index] = newElem; + return newElem; + } + return datum; + } + + public void set(int subRegionX, int subRegionZ, T data) { + this.data[getIndex(subRegionX, subRegionZ)] = data; + } + + public boolean isCreated(int subRegionX, int subRegionZ) { + return this.data[getIndex(subRegionX, subRegionZ)] != null; + } + + public ChunkCoordIntPair getCoord() { + return coord; + } + + private int getIndex(int subRegionX, int subRegionY) { + return subRegionX * regionLength + subRegionY; + } + + private int getChunkX(int index) { + return index / regionLength + coord.chunkXPos * regionLength; + } + + private int getChunkZ(int index) { + return index % regionLength + coord.chunkZPos * regionLength; + } + + public boolean isDirty() { + for (T datum : data) { + if (datum != null && !datum.isSameAsDefault()) return true; + } + return false; + } + + public void save() { + try { + save0(); + } catch (IOException e) { + GT_Log.err.println("Error saving data " + backingStorage.getPath()); + e.printStackTrace(GT_Log.err); + } + } + + private void save0() throws IOException { + // noinspection ResultOfMethodCallIgnored + backingStorage.getParentFile() + .mkdirs(); + File tmpFile = getTmpFile(); + World world = Objects.requireNonNull(this.world.get(), "Attempting to save region of another world!"); + try (DataOutputStream output = new DataOutputStream(new FileOutputStream(tmpFile))) { + int ptr = 0; + boolean nullRange = data[0] == null; + // write a magic byte as storage format version + output.writeByte(0); + // write a magic byte as data format version + output.writeByte(version); + output.writeBoolean(nullRange); + while (ptr < data.length) { + // work out how long is this range + int rangeStart = ptr; + while (ptr < data.length + && (data[ptr] == null || (!saveDefaults && data[ptr].isSameAsDefault())) == nullRange) ptr++; + // write range length + output.writeShort(ptr - rangeStart); + if (!nullRange) + // write element data + for (int i = rangeStart; i < ptr; i++) + writeElement(output, data[i], world, getChunkX(ptr), getChunkZ(ptr)); + // or not + nullRange = !nullRange; + } + } + // first try to replace the destination file + // since atomic operation, no need to keep the backup in place + try { + Files.move( + tmpFile.toPath(), + backingStorage.toPath(), + StandardCopyOption.REPLACE_EXISTING, + StandardCopyOption.ATOMIC_MOVE); + } catch (AtomicMoveNotSupportedException ignored) { + // in case some dumb system/jre combination would cause this + // or if **somehow** two file inside the same directory belongs two separate filesystem + FileUtils.copyFile(tmpFile, backingStorage); + } + } + + public void load() { + try { + loadFromFile(backingStorage); + } catch (IOException | RuntimeException e) { + GT_Log.err.println("Primary storage file broken in " + backingStorage.getPath()); + e.printStackTrace(GT_Log.err); + // in case the primary storage is broken + try { + loadFromFile(getTmpFile()); + } catch (IOException | RuntimeException e2) { + GT_Log.err.println("Backup storage file broken in " + backingStorage.getPath()); + e2.printStackTrace(GT_Log.err); + } + } + } + + private void loadFromFile(File file) throws IOException { + World world = Objects.requireNonNull(this.world.get(), "Attempting to load region of another world!"); + try (DataInputStream input = new DataInputStream(new FileInputStream(file))) { + byte b = input.readByte(); + if (b == 0) { + loadV0(input, world); + } else { + GT_Log.err.printf("Unknown ChunkAssociatedData version %d\n", b); + } + } + } + + private void loadV0(DataInput input, World world) throws IOException { + int version = input.readByte(); + boolean nullRange = input.readBoolean(); + int ptr = 0; + while (ptr != data.length) { + int rangeEnd = ptr + input.readUnsignedShort(); + if (!nullRange) { + for (; ptr < rangeEnd; ptr++) { + data[ptr] = readElement(input, version, world, getChunkX(ptr), getChunkZ(ptr)); + } + } else { + Arrays.fill(data, ptr, rangeEnd, null); + ptr = rangeEnd; + } + nullRange = !nullRange; + } + } + + private File getTmpFile() { + return new File(backingStorage.getParentFile(), backingStorage.getName() + ".tmp"); + } + } + + public static class EventHandler { + + private EventHandler() { + MinecraftForge.EVENT_BUS.register(this); + } + + @SubscribeEvent + public void onWorldSave(WorldEvent.Save e) { + for (GT_ChunkAssociatedData<?> d : instances.values()) { + d.save(e.world); + } + } + + @SubscribeEvent + public void onWorldUnload(WorldEvent.Unload e) { + for (GT_ChunkAssociatedData<?> d : instances.values()) { + // there is no need to explicitly do a save here + // forge will send a WorldEvent.Save on server thread before this event is distributed + d.masterMap.remove(e.world.provider.dimensionId); + } + } + } +} diff --git a/src/main/java/gregtech/api/util/GT_CircuitryBehavior.java b/src/main/java/gregtech/api/util/GT_CircuitryBehavior.java new file mode 100644 index 0000000000..63fb7d1e70 --- /dev/null +++ b/src/main/java/gregtech/api/util/GT_CircuitryBehavior.java @@ -0,0 +1,212 @@ +package gregtech.api.util; + +import net.minecraftforge.common.util.ForgeDirection; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import gregtech.api.GregTech_API; +import gregtech.api.interfaces.IRedstoneCircuitBlock; + +/** + * Redstone Circuit Control Code + * <p/> + * This should make everything possible what a Redstone Computer or BuildCraft Gate could do. It is intended to use this + * similar to BC-Gates (for acquiring Data) and RP Logic Gates. You could write an extremely specified and complex Logic + * Gate, which works only for you Setup, like with ComputerCraft, but you would have to write an extra Mod to add that, + * as it doesn't work Ingame. + * <p/> + * One can make use of the fact, that ItemStacks can be stored as Integer, so that you can scan Inventories for specific + * Items using that. Luckily the Buttons in the GUI enable Copy/Paste of ItemID+MetaData to Integer, including the + * WildCard Damage Value when you use rightclick to place it. You just need to use @GT_Utility.stackToInt(ItemStack + * aStack) to get it. + * <p/> + * All Functions run usually in a seperate try/catch Block, so that failed Logic won't crash the TileEntity. + */ +public abstract class GT_CircuitryBehavior { + + /** + * @param aIndex 0 - 1023 are my own Indices, so use other Numbers! + */ + public GT_CircuitryBehavior(int aIndex) { + GregTech_API.sCircuitryBehaviors.put(aIndex, this); + } + + /** + * returns if there is Redstone applied to any of the valid Inputs (OR) + */ + public static boolean getAnyRedstone(IRedstoneCircuitBlock aRedstoneCircuitBlock) { + for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) { + if (side != aRedstoneCircuitBlock.getOutputFacing() && aRedstoneCircuitBlock.getCover(side) + .letsRedstoneGoIn( + side, + aRedstoneCircuitBlock.getCoverID(side), + aRedstoneCircuitBlock.getCoverVariable(side), + aRedstoneCircuitBlock.getOwnTileEntity())) { + if (aRedstoneCircuitBlock.getInputRedstone(side) > 0) { + return true; + } + } + } + return false; + } + + /** + * returns if there is Redstone applied to all the valid Inputs (AND) + */ + public static boolean getAllRedstone(IRedstoneCircuitBlock aRedstoneCircuitBlock) { + for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) { + if (side != aRedstoneCircuitBlock.getOutputFacing() && aRedstoneCircuitBlock.getCover(side) + .letsRedstoneGoIn( + side, + aRedstoneCircuitBlock.getCoverID(side), + aRedstoneCircuitBlock.getCoverVariable(side), + aRedstoneCircuitBlock.getOwnTileEntity())) { + if (aRedstoneCircuitBlock.getInputRedstone(side) == 0) { + return false; + } + } + } + return true; + } + + /** + * returns if there is Redstone applied to exactly one of the valid Inputs (XOR) + */ + public static boolean getOneRedstone(IRedstoneCircuitBlock aRedstoneCircuitBlock) { + int tRedstoneAmount = 0; + for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) { + if (side != aRedstoneCircuitBlock.getOutputFacing() && aRedstoneCircuitBlock.getCover(side) + .letsRedstoneGoIn( + side, + aRedstoneCircuitBlock.getCoverID(side), + aRedstoneCircuitBlock.getCoverVariable(side), + aRedstoneCircuitBlock.getOwnTileEntity())) { + if (aRedstoneCircuitBlock.getInputRedstone(side) > 0) { + tRedstoneAmount++; + } + } + } + return tRedstoneAmount == 1; + } + + /** + * returns the strongest incoming RS-Power + */ + public static byte getStrongestRedstone(IRedstoneCircuitBlock aRedstoneCircuitBlock) { + byte tRedstoneAmount = 0; + for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) { + if (side != aRedstoneCircuitBlock.getOutputFacing() && aRedstoneCircuitBlock.getCover(side) + .letsRedstoneGoIn( + side, + aRedstoneCircuitBlock.getCoverID(side), + aRedstoneCircuitBlock.getCoverVariable(side), + aRedstoneCircuitBlock.getOwnTileEntity())) { + tRedstoneAmount = (byte) Math.max(tRedstoneAmount, aRedstoneCircuitBlock.getInputRedstone(side)); + } + } + return tRedstoneAmount; + } + + // region GUI Functions + + /** + * returns the weakest incoming non-zero RS-Power + */ + public static byte getWeakestNonZeroRedstone(IRedstoneCircuitBlock aRedstoneCircuitBlock) { + if (!getAnyRedstone(aRedstoneCircuitBlock)) return 0; + byte tRedstoneAmount = 15; + for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) { + if (side != aRedstoneCircuitBlock.getOutputFacing() && aRedstoneCircuitBlock.getCover(side) + .letsRedstoneGoIn( + side, + aRedstoneCircuitBlock.getCoverID(side), + aRedstoneCircuitBlock.getCoverVariable(side), + aRedstoneCircuitBlock.getOwnTileEntity())) { + if (aRedstoneCircuitBlock.getInputRedstone(side) > 0) + tRedstoneAmount = (byte) Math.min(tRedstoneAmount, aRedstoneCircuitBlock.getInputRedstone(side)); + } + } + return tRedstoneAmount; + } + + /** + * returns the weakest incoming RS-Power + */ + public static byte getWeakestRedstone(IRedstoneCircuitBlock aRedstoneCircuitBlock) { + if (!getAnyRedstone(aRedstoneCircuitBlock)) return 0; + byte tRedstoneAmount = 15; + for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) { + if (side != aRedstoneCircuitBlock.getOutputFacing() && aRedstoneCircuitBlock.getCover(side) + .letsRedstoneGoIn( + side, + aRedstoneCircuitBlock.getCoverID(side), + aRedstoneCircuitBlock.getCoverVariable(side), + aRedstoneCircuitBlock.getOwnTileEntity())) { + tRedstoneAmount = (byte) Math.min(tRedstoneAmount, aRedstoneCircuitBlock.getInputRedstone(side)); + } + } + return tRedstoneAmount; + } + + /** + * Initializes the Parameters of this Circuit, all Parameters have been set to 0 right before calling this + * + * @param aCircuitData, The Data Storage you can use (8 Slots) + * @param aRedstoneCircuitBlock, The Circuit Block MetaTileEntity itself + */ + public abstract void initParameters(int[] aCircuitData, IRedstoneCircuitBlock aRedstoneCircuitBlock); + + /** + * Validates the Parameters of this Circuit when a value has been changed by the GUI Also called right + * after @initParameters and when the Chunk reloads + * + * @param aCircuitData, The Data Storage you can use (8 Slots and only the first 4 are User definable) + * @param aRedstoneCircuitBlock, The Circuit Block MetaTileEntity itself + */ + public abstract void validateParameters(int[] aCircuitData, IRedstoneCircuitBlock aRedstoneCircuitBlock); + + // endregion + + // region Utility Functions + + /** + * Called every tick if the Block has enough Energy and if the Block is Active + * + * @param aCircuitData, The Data Storage you can use (8 Slots) + * @param aRedstoneCircuitBlock, The Circuit Block MetaTileEntity itself + */ + public abstract void onTick(int[] aCircuitData, IRedstoneCircuitBlock aRedstoneCircuitBlock); + + /** + * If the ItemStack should be displayed. Parameters are between 0 and 3. + */ + public abstract boolean displayItemStack(int[] aCircuitData, IRedstoneCircuitBlock aRedstoneCircuitBlock, + int aIndex); + + /** + * The Name of the Gate for the GUI + */ + @SideOnly(Side.CLIENT) + public abstract String getName(); + + /** + * The Description of the Gate for the GUI + */ + @SideOnly(Side.CLIENT) + public abstract String getDescription(); + + /** + * The Description of the Data Field for the GUI + */ + @SideOnly(Side.CLIENT) + public abstract String getDataDescription(int[] aCircuitData, int aCircuitDataIndex); + + /** + * How the Integer should be displayed in the GUI. null means, that it just displays as regular Number. + */ + @SideOnly(Side.CLIENT) + public String getDataDisplay(int[] aCircuitData, int aCircuitDataIndex) { + return null; + } + // endregion +} diff --git a/src/main/java/gregtech/api/util/GT_ClientPreference.java b/src/main/java/gregtech/api/util/GT_ClientPreference.java new file mode 100644 index 0000000000..8df4ef8b05 --- /dev/null +++ b/src/main/java/gregtech/api/util/GT_ClientPreference.java @@ -0,0 +1,41 @@ +package gregtech.api.util; + +public class GT_ClientPreference { + + private final boolean mSingleBlockInitialFilter; + private final boolean mSingleBlockInitialMultiStack; + private final boolean mInputBusInitialFilter; + private final boolean wailaAverageNS; + + public GT_ClientPreference(boolean mSingleBlockInitialFilter, boolean mSingleBlockInitialMultiStack, + boolean mInputBusInitialFilter, boolean wailaAverageNS) { + this.mSingleBlockInitialFilter = mSingleBlockInitialFilter; + this.mSingleBlockInitialMultiStack = mSingleBlockInitialMultiStack; + this.mInputBusInitialFilter = mInputBusInitialFilter; + this.wailaAverageNS = wailaAverageNS; + } + + public GT_ClientPreference(GT_Config aClientDataFile) { + this.mSingleBlockInitialFilter = aClientDataFile.get("preference", "mSingleBlockInitialFilter", false); + this.mSingleBlockInitialMultiStack = aClientDataFile + .get("preference", "mSingleBlockInitialAllowMultiStack", false); + this.mInputBusInitialFilter = aClientDataFile.get("preference", "mInputBusInitialFilter", true); + this.wailaAverageNS = aClientDataFile.get("waila", "WailaAverageNS", false); + } + + public boolean isSingleBlockInitialFilterEnabled() { + return mSingleBlockInitialFilter; + } + + public boolean isSingleBlockInitialMultiStackEnabled() { + return mSingleBlockInitialMultiStack; + } + + public boolean isInputBusInitialFilterEnabled() { + return mInputBusInitialFilter; + } + + public boolean isWailaAverageNSEnabled() { + return wailaAverageNS; + } +} diff --git a/src/main/java/gregtech/api/util/GT_Config.java b/src/main/java/gregtech/api/util/GT_Config.java new file mode 100644 index 0000000000..dc56def68f --- /dev/null +++ b/src/main/java/gregtech/api/util/GT_Config.java @@ -0,0 +1,160 @@ +package gregtech.api.util; + +import static gregtech.api.enums.GT_Values.E; + +import net.minecraft.item.ItemStack; +import net.minecraftforge.common.config.Configuration; +import net.minecraftforge.common.config.Property; + +import gregtech.api.GregTech_API; +import gregtech.api.enums.GT_Values; + +public class GT_Config implements Runnable { + + public static boolean troll = false; + + public static Configuration sConfigFileIDs; + public final Configuration mConfig; + + public GT_Config(Configuration aConfig) { + mConfig = aConfig; + mConfig.load(); + mConfig.save(); + GregTech_API.sAfterGTPreload.add(this); // in case of crash on startup + GregTech_API.sAfterGTLoad.add(this); // in case of crash on startup + GregTech_API.sAfterGTPostload.add(this); + if (GT_Values.lateConfigSave) GregTech_API.sFirstWorldTick.add(this); + } + + private static boolean shouldSave() { + return GT_Values.lateConfigSave ? GT_Values.worldTickHappened : GregTech_API.sPostloadFinished; + } + + public static int addIDConfig(Object aCategory, String aName, int aDefault) { + if (GT_Utility.isStringInvalid(aName)) return aDefault; + Property tProperty = sConfigFileIDs.get( + aCategory.toString() + .replaceAll("\\|", "."), + aName.replaceAll("\\|", "."), + aDefault); + int rResult = tProperty.getInt(aDefault); + if (!tProperty.wasRead() && shouldSave()) sConfigFileIDs.save(); + return rResult; + } + + public static String getStackConfigName(ItemStack aStack) { + if (GT_Utility.isStackInvalid(aStack)) return E; + Object rName = GT_OreDictUnificator.getAssociation(aStack); + if (rName != null) return rName.toString(); + try { + if (GT_Utility.isStringValid(rName = aStack.getUnlocalizedName())) return rName.toString(); + } catch (Throwable e) { + /* Do nothing */ + } + String sName = aStack.getItem() + .toString(); + String[] tmp = sName.split("@"); + if (tmp.length > 0) sName = tmp[0]; + return sName + "." + aStack.getItemDamage(); + } + + public boolean get(Object aCategory, ItemStack aStack, boolean aDefault) { + String aName = getStackConfigName(aStack); + return get(aCategory, aName, aDefault); + } + + public boolean get(Object aCategory, String aName, boolean aDefault) { + if (GT_Utility.isStringInvalid(aName)) return aDefault; + Property tProperty = mConfig.get( + aCategory.toString() + .replaceAll("\\|", "_"), + (aName + "_" + aDefault).replaceAll("\\|", "_"), + aDefault); + boolean rResult = tProperty.getBoolean(aDefault); + if (!tProperty.wasRead() && shouldSave()) mConfig.save(); + return rResult; + } + + public int get(Object aCategory, ItemStack aStack, int aDefault) { + return get(aCategory, getStackConfigName(aStack), aDefault); + } + + public int get(Object aCategory, String aName, int aDefault) { + if (GT_Utility.isStringInvalid(aName)) return aDefault; + Property tProperty = mConfig.get( + aCategory.toString() + .replaceAll("\\|", "_"), + (aName + "_" + aDefault).replaceAll("\\|", "_"), + aDefault); + int rResult = tProperty.getInt(aDefault); + if (!tProperty.wasRead() && shouldSave()) mConfig.save(); + return rResult; + } + + public double get(Object aCategory, ItemStack aStack, double aDefault) { + return get(aCategory, getStackConfigName(aStack), aDefault); + } + + public double get(Object aCategory, String aName, double aDefault) { + if (GT_Utility.isStringInvalid(aName)) return aDefault; + Property tProperty = mConfig.get( + aCategory.toString() + .replaceAll("\\|", "_"), + (aName + "_" + aDefault).replaceAll("\\|", "_"), + aDefault); + double rResult = tProperty.getDouble(aDefault); + if (!tProperty.wasRead() && shouldSave()) mConfig.save(); + return rResult; + } + + public String get(Object aCategory, ItemStack aStack, String aDefault) { + return get(aCategory, getStackConfigName(aStack), aDefault); + } + + public String get(Object aCategory, String aName, String aDefault) { + if (GT_Utility.isStringInvalid(aName)) return aDefault; + Property tProperty = mConfig.get( + aCategory.toString() + .replaceAll("\\|", "_"), + (aName + "_" + aDefault).replaceAll("\\|", "_"), + aDefault); + String rResult = tProperty.getString(); + if (!tProperty.wasRead() && shouldSave()) mConfig.save(); + return rResult; + } + + public String[] get(Object aCategory, ItemStack aStack, String... aDefault) { + return get(aCategory, getStackConfigName(aStack), aDefault); + } + + public String[] get(Object aCategory, String aName, String... aDefault) { + if (GT_Utility.isStringInvalid(aName)) return aDefault; + Property tProperty = mConfig.get( + aCategory.toString() + .replaceAll("\\|", "_"), + aName.replaceAll("\\|", "_"), + aDefault); + String[] rResult = tProperty.getStringList(); + if (!tProperty.wasRead() && GregTech_API.sPostloadFinished) mConfig.save(); + return rResult; + } + + public String getWithValidValues(Object aCategory, String aName, String[] validValues, String aDefault) { + if (GT_Utility.isStringInvalid(aName)) return aDefault; + Property tProperty = mConfig.get( + aCategory.toString() + .replaceAll("\\|", "_"), + aName.replaceAll("\\|", "_"), + aDefault, + null, + validValues); + String rResult = tProperty.getString(); + if (!tProperty.wasRead() && GregTech_API.sPostloadFinished) mConfig.save(); + return rResult; + } + + @Override + public void run() { + mConfig.save(); + } +} diff --git a/src/main/java/gregtech/api/util/GT_CoverBehavior.java b/src/main/java/gregtech/api/util/GT_CoverBehavior.java new file mode 100644 index 0000000000..34fc151b9a --- /dev/null +++ b/src/main/java/gregtech/api/util/GT_CoverBehavior.java @@ -0,0 +1,411 @@ +package gregtech.api.util; + +import static gregtech.api.enums.GT_Values.E; + +import java.lang.ref.WeakReference; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.item.ItemStack; +import net.minecraft.world.World; +import net.minecraftforge.common.util.ForgeDirection; +import net.minecraftforge.fluids.Fluid; + +import gregtech.api.enums.GT_Values; +import gregtech.api.gui.modularui.GT_UIInfos; +import gregtech.api.interfaces.ITexture; +import gregtech.api.interfaces.tileentity.ICoverable; +import gregtech.api.net.GT_Packet_TileEntityCoverGUI; + +/** + * For Covers with a special behavior. Has fixed storage format of 4 byte. Not very convenient... + */ +public abstract class GT_CoverBehavior extends GT_CoverBehaviorBase<ISerializableObject.LegacyCoverData> { + + public boolean mPlayerNotified = false; + + public GT_CoverBehavior() { + this(null); + } + + public GT_CoverBehavior(ITexture coverTexture) { + super(ISerializableObject.LegacyCoverData.class, coverTexture); + } + + protected static int convert(ISerializableObject.LegacyCoverData data) { + return data == null ? 0 : data.get(); + } + + // region bridge the parent call to legacy calls + + @Override + public final ISerializableObject.LegacyCoverData createDataObject() { + return new ISerializableObject.LegacyCoverData(); + } + + @Override + public ISerializableObject.LegacyCoverData createDataObject(int aLegacyData) { + return new ISerializableObject.LegacyCoverData(aLegacyData); + } + + @Override + protected boolean isRedstoneSensitiveImpl(ForgeDirection side, int aCoverID, + ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity, long aTimer) { + return isRedstoneSensitive(side, aCoverID, aCoverVariable.get(), aTileEntity, aTimer); + } + + @Override + protected ISerializableObject.LegacyCoverData doCoverThingsImpl(ForgeDirection side, byte aInputRedstone, + int aCoverID, ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity, long aTimer) { + if (aCoverVariable == null) aCoverVariable = new ISerializableObject.LegacyCoverData(); + aCoverVariable.set(doCoverThings(side, aInputRedstone, aCoverID, aCoverVariable.get(), aTileEntity, aTimer)); + return aCoverVariable; + } + + @Override + protected boolean onCoverRightClickImpl(ForgeDirection side, int aCoverID, + ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity, EntityPlayer aPlayer, float aX, + float aY, float aZ) { + return onCoverRightclick(side, aCoverID, convert(aCoverVariable), aTileEntity, aPlayer, aX, aY, aZ); + } + + @Override + protected ISerializableObject.LegacyCoverData onCoverScrewdriverClickImpl(ForgeDirection side, int aCoverID, + ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity, EntityPlayer aPlayer, float aX, + float aY, float aZ) { + if (aCoverVariable == null) aCoverVariable = new ISerializableObject.LegacyCoverData(); + aCoverVariable + .set(onCoverScrewdriverclick(side, aCoverID, convert(aCoverVariable), aTileEntity, aPlayer, aX, aY, aZ)); + return aCoverVariable; + } + + @Override + protected boolean onCoverShiftRightClickImpl(ForgeDirection side, int aCoverID, + ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity, EntityPlayer aPlayer) { + return onCoverShiftRightclick(side, aCoverID, convert(aCoverVariable), aTileEntity, aPlayer); + } + + @Deprecated + @Override + protected Object getClientGUIImpl(ForgeDirection side, int aCoverID, + ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity, EntityPlayer aPlayer, + World aWorld) { + return getClientGUI(side, aCoverID, convert(aCoverVariable), aTileEntity); + } + + @Override + protected boolean onCoverRemovalImpl(ForgeDirection side, int aCoverID, + ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity, boolean aForced) { + return onCoverRemoval(side, aCoverID, convert(aCoverVariable), aTileEntity, aForced); + } + + @Override + protected String getDescriptionImpl(ForgeDirection side, int aCoverID, + ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity) { + return getDescription(side, aCoverID, convert(aCoverVariable), aTileEntity); + } + + @Override + protected float getBlastProofLevelImpl(ForgeDirection side, int aCoverID, + ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity) { + return getBlastProofLevel(side, aCoverID, convert(aCoverVariable), aTileEntity); + } + + @Override + protected boolean letsRedstoneGoInImpl(ForgeDirection side, int aCoverID, + ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity) { + return letsRedstoneGoIn(side, aCoverID, convert(aCoverVariable), aTileEntity); + } + + @Override + protected boolean letsRedstoneGoOutImpl(ForgeDirection side, int aCoverID, + ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity) { + return letsRedstoneGoOut(side, aCoverID, convert(aCoverVariable), aTileEntity); + } + + @Override + protected boolean letsEnergyInImpl(ForgeDirection side, int aCoverID, + ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity) { + return letsEnergyIn(side, aCoverID, convert(aCoverVariable), aTileEntity); + } + + @Override + protected boolean letsEnergyOutImpl(ForgeDirection side, int aCoverID, + ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity) { + return letsEnergyOut(side, aCoverID, convert(aCoverVariable), aTileEntity); + } + + @Override + protected boolean letsFluidInImpl(ForgeDirection side, int aCoverID, + ISerializableObject.LegacyCoverData aCoverVariable, Fluid aFluid, ICoverable aTileEntity) { + return letsFluidIn(side, aCoverID, convert(aCoverVariable), aFluid, aTileEntity); + } + + @Override + protected boolean letsFluidOutImpl(ForgeDirection side, int aCoverID, + ISerializableObject.LegacyCoverData aCoverVariable, Fluid aFluid, ICoverable aTileEntity) { + return letsFluidOut(side, aCoverID, convert(aCoverVariable), aFluid, aTileEntity); + } + + @Override + protected boolean letsItemsInImpl(ForgeDirection side, int aCoverID, + ISerializableObject.LegacyCoverData aCoverVariable, int aSlot, ICoverable aTileEntity) { + return letsItemsIn(side, aCoverID, convert(aCoverVariable), aSlot, aTileEntity); + } + + @Override + protected boolean letsItemsOutImpl(ForgeDirection side, int aCoverID, + ISerializableObject.LegacyCoverData aCoverVariable, int aSlot, ICoverable aTileEntity) { + return letsItemsOut(side, aCoverID, convert(aCoverVariable), aSlot, aTileEntity); + } + + @Override + protected boolean isGUIClickableImpl(ForgeDirection side, int aCoverID, + ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity) { + return isGUIClickable(side, aCoverID, convert(aCoverVariable), aTileEntity); + } + + @Override + protected boolean manipulatesSidedRedstoneOutputImpl(ForgeDirection side, int aCoverID, + ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity) { + return manipulatesSidedRedstoneOutput(side, aCoverID, convert(aCoverVariable), aTileEntity); + } + + @Override + protected boolean alwaysLookConnectedImpl(ForgeDirection side, int aCoverID, + ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity) { + return alwaysLookConnected(side, aCoverID, convert(aCoverVariable), aTileEntity); + } + + @Override + protected byte getRedstoneInputImpl(ForgeDirection side, byte aInputRedstone, int aCoverID, + ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity) { + return getRedstoneInput(side, aInputRedstone, aCoverID, convert(aCoverVariable), aTileEntity); + } + + @Override + protected int getTickRateImpl(ForgeDirection side, int aCoverID, ISerializableObject.LegacyCoverData aCoverVariable, + ICoverable aTileEntity) { + return getTickRate(side, aCoverID, convert(aCoverVariable), aTileEntity); + } + + @Override + protected byte getLensColorImpl(ForgeDirection side, int aCoverID, + ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity) { + return getLensColor(side, aCoverID, convert(aCoverVariable), aTileEntity); + } + + @Override + protected ItemStack getDropImpl(ForgeDirection side, int aCoverID, + ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity) { + return getDrop(side, aCoverID, convert(aCoverVariable), aTileEntity); + } + + // endregion + + public boolean isRedstoneSensitive(ForgeDirection side, int aCoverID, int aCoverVariable, ICoverable aTileEntity, + long aTimer) { + return true; + } + + /** + * Called by updateEntity inside the covered TileEntity. aCoverVariable is the Value you returned last time. + */ + public int doCoverThings(ForgeDirection side, byte aInputRedstone, int aCoverID, int aCoverVariable, + ICoverable aTileEntity, long aTimer) { + return aCoverVariable; + } + + /** + * Called when someone rightclicks this Cover. + * <p/> + * return true, if something actually happens. + */ + public boolean onCoverRightclick(ForgeDirection side, int aCoverID, int aCoverVariable, ICoverable aTileEntity, + EntityPlayer aPlayer, float aX, float aY, float aZ) { + return false; + } + + /** + * Called when someone rightclicks this Cover with a Screwdriver. Doesn't call @onCoverRightclick in this Case. + * <p/> + * return the new Value of the Cover Variable + */ + public int onCoverScrewdriverclick(ForgeDirection side, int aCoverID, int aCoverVariable, ICoverable aTileEntity, + EntityPlayer aPlayer, float aX, float aY, float aZ) { + return aCoverVariable; + } + + /** + * Called when someone shift-rightclicks this Cover with no tool. Doesn't call @onCoverRightclick in this Case. + */ + public boolean onCoverShiftRightclick(ForgeDirection side, int aCoverID, int aCoverVariable, ICoverable aTileEntity, + EntityPlayer aPlayer) { + if (hasCoverGUI() && aPlayer instanceof EntityPlayerMP) { + lastPlayer = new WeakReference<>(aPlayer); + mPlayerNotified = false; + if (useModularUI()) { + GT_UIInfos.openCoverUI(aTileEntity, aPlayer, side); + } else { + GT_Values.NW.sendToPlayer( + new GT_Packet_TileEntityCoverGUI( + side, + aCoverID, + aCoverVariable, + aTileEntity, + (EntityPlayerMP) aPlayer), + (EntityPlayerMP) aPlayer); + } + return true; + } + return false; + } + + @Deprecated + public Object getClientGUI(ForgeDirection side, int aCoverID, int coverData, ICoverable aTileEntity) { + return null; + } + + /** + * Removes the Cover if this returns true, or if aForced is true. Doesn't get called when the Machine Block is + * getting broken, only if you break the Cover away from the Machine. + */ + public boolean onCoverRemoval(ForgeDirection side, int aCoverID, int aCoverVariable, ICoverable aTileEntity, + boolean aForced) { + return true; + } + + /** + * Gives a small Text for the status of the Cover. + */ + public String getDescription(ForgeDirection side, int aCoverID, int aCoverVariable, ICoverable aTileEntity) { + return E; + } + + /** + * How Blast Proof the Cover is. 30 is normal. + */ + public float getBlastProofLevel(ForgeDirection side, int aCoverID, int aCoverVariable, ICoverable aTileEntity) { + return 10.0F; + } + + /** + * If it lets RS-Signals into the Block + * <p/> + * This is just Informative so that Machines know if their Redstone Input is blocked or not + */ + public boolean letsRedstoneGoIn(ForgeDirection side, int aCoverID, int aCoverVariable, ICoverable aTileEntity) { + return false; + } + + /** + * If it lets RS-Signals out of the Block + */ + public boolean letsRedstoneGoOut(ForgeDirection side, int aCoverID, int aCoverVariable, ICoverable aTileEntity) { + return false; + } + + /** + * If it lets Energy into the Block + */ + public boolean letsEnergyIn(ForgeDirection side, int aCoverID, int aCoverVariable, ICoverable aTileEntity) { + return false; + } + + /** + * If it lets Energy out of the Block + */ + public boolean letsEnergyOut(ForgeDirection side, int aCoverID, int aCoverVariable, ICoverable aTileEntity) { + return false; + } + + /** + * If it lets Liquids into the Block, aFluid can be null meaning if this is generally allowing Fluids or not. + */ + public boolean letsFluidIn(ForgeDirection side, int aCoverID, int aCoverVariable, Fluid aFluid, + ICoverable aTileEntity) { + return false; + } + + /** + * If it lets Liquids out of the Block, aFluid can be null meaning if this is generally allowing Fluids or not. + */ + public boolean letsFluidOut(ForgeDirection side, int aCoverID, int aCoverVariable, Fluid aFluid, + ICoverable aTileEntity) { + return false; + } + + /** + * If it lets Items into the Block, aSlot = -1 means if it is generally accepting Items (return false for no + * Interaction at all), aSlot = -2 means if it would accept for all Slots (return true to skip the Checks for each + * Slot). + */ + public boolean letsItemsIn(ForgeDirection side, int aCoverID, int aCoverVariable, int aSlot, + ICoverable aTileEntity) { + return false; + } + + /** + * If it lets Items out of the Block, aSlot = -1 means if it is generally accepting Items (return false for no + * Interaction at all), aSlot = -2 means if it would accept for all Slots (return true to skip the Checks for each + * Slot). + */ + public boolean letsItemsOut(ForgeDirection side, int aCoverID, int aCoverVariable, int aSlot, + ICoverable aTileEntity) { + return false; + } + + /** + * If it lets you rightclick the Machine normally + */ + public boolean isGUIClickable(ForgeDirection side, int aCoverID, int aCoverVariable, ICoverable aTileEntity) { + return true; + } + + /** + * Needs to return true for Covers, which have a Redstone Output on their Facing. + */ + public boolean manipulatesSidedRedstoneOutput(ForgeDirection side, int aCoverID, int aCoverVariable, + ICoverable aTileEntity) { + return false; + } + + /** + * if this Cover should let Pipe Connections look connected even if it is not the case. + */ + public boolean alwaysLookConnected(ForgeDirection side, int aCoverID, int aCoverVariable, ICoverable aTileEntity) { + return false; + } + + /** + * Called to determine the incoming Redstone Signal of a Machine. Returns the original Redstone per default. The + * Cover should @letsRedstoneGoIn or the aInputRedstone Parameter is always 0. + */ + public byte getRedstoneInput(ForgeDirection side, byte aInputRedstone, int aCoverID, int aCoverVariable, + ICoverable aTileEntity) { + return letsRedstoneGoIn(side, aCoverID, aCoverVariable, aTileEntity) ? aInputRedstone : 0; + } + + /** + * Gets the Tick Rate for doCoverThings of the Cover + * <p/> + * 0 = No Ticks! Yes, 0 is Default, you have to override this + */ + public int getTickRate(ForgeDirection side, int aCoverID, int aCoverVariable, ICoverable aTileEntity) { + return 0; + } + + /** + * The MC Color of this Lens. -1 for no Color (meaning this isn't a Lens then). + */ + public byte getLensColor(ForgeDirection side, int aCoverID, int aCoverVariable, ICoverable aTileEntity) { + return -1; + } + + /** + * @return the ItemStack dropped by this Cover + */ + public ItemStack getDrop(ForgeDirection side, int aCoverID, int aCoverVariable, ICoverable aTileEntity) { + return GT_OreDictUnificator.get(true, aTileEntity.getCoverItemAtSide(side)); + } +} diff --git a/src/main/java/gregtech/api/util/GT_CoverBehaviorBase.java b/src/main/java/gregtech/api/util/GT_CoverBehaviorBase.java new file mode 100644 index 0000000000..be9492ebba --- /dev/null +++ b/src/main/java/gregtech/api/util/GT_CoverBehaviorBase.java @@ -0,0 +1,856 @@ +package gregtech.api.util; + +import static gregtech.api.enums.GT_Values.E; + +import java.lang.ref.WeakReference; +import java.util.List; +import java.util.function.Supplier; + +import javax.annotation.Nullable; + +import net.minecraft.block.Block; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTBase; +import net.minecraft.nbt.NBTTagInt; +import net.minecraft.world.World; +import net.minecraftforge.common.util.ForgeDirection; +import net.minecraftforge.fluids.Fluid; + +import org.jetbrains.annotations.NotNull; + +import com.google.common.collect.ImmutableList; +import com.gtnewhorizons.modularui.api.ModularUITextures; +import com.gtnewhorizons.modularui.api.drawable.ItemDrawable; +import com.gtnewhorizons.modularui.api.screen.ModularWindow; +import com.gtnewhorizons.modularui.common.widget.ButtonWidget; +import com.gtnewhorizons.modularui.common.widget.TextWidget; + +import gregtech.api.GregTech_API; +import gregtech.api.enums.GT_Values; +import gregtech.api.gui.GT_GUIColorOverride; +import gregtech.api.gui.modularui.GT_CoverUIBuildContext; +import gregtech.api.gui.modularui.GT_UIInfos; +import gregtech.api.gui.widgets.GT_CoverTickRateButton; +import gregtech.api.interfaces.ITexture; +import gregtech.api.interfaces.tileentity.ICoverable; +import gregtech.api.net.GT_Packet_TileEntityCoverGUI; +import gregtech.common.covers.CoverInfo; + +/** + * For Covers with a special behavior. + * + * @author glease + */ +public abstract class GT_CoverBehaviorBase<T extends ISerializableObject> { + + public WeakReference<EntityPlayer> lastPlayer = null; + private final Class<T> typeToken; + private final ITexture coverFGTexture; + + protected GT_CoverBehaviorBase(Class<T> typeToken) { + this(typeToken, null); + } + + protected GT_CoverBehaviorBase(Class<T> typeToken, ITexture coverTexture) { + this.typeToken = typeToken; + this.coverFGTexture = coverTexture; + reloadColorOverride(); + } + + public void reloadColorOverride() { + this.colorOverride = GT_GUIColorOverride.get(guiTexturePath); + } + + public abstract T createDataObject(int aLegacyData); + + public abstract T createDataObject(); + + public final T createDataObject(NBTBase aNBT) { + // Handle legacy data (migrating from GT_CoverBehavior to GT_CoverBehaviorBase) + if (aNBT instanceof NBTTagInt) { + return createDataObject(((NBTTagInt) aNBT).func_150287_d()); + } + + final T ret = createDataObject(); + ret.loadDataFromNBT(aNBT); + return ret; + } + + public final T cast(ISerializableObject aData) { + if (typeToken.isInstance(aData)) return forceCast(aData); + return null; + } + + private T forceCast(ISerializableObject aData) { + try { + return typeToken.cast(aData); + } catch (Exception e) { + throw new RuntimeException("Casting data in " + this.getClass() + ", data " + aData, e); + } + } + + // region facade + + /** + * Get target facade block. Does not affect rendering of **this** block. It is only used as a hint for other block + * in case of CTM + * + * @return null if none, otherwise return facade target block + */ + public final Block getFacadeBlock(ForgeDirection side, int aCoverID, ISerializableObject aCoverVariable, + ICoverable aTileEntity) { + return getFacadeBlockImpl(side, aCoverID, forceCast(aCoverVariable), aTileEntity); + } + + /** + * Get target facade block. Does not affect rendering of **this** block. It is only used as a hint for other block + * in case of CTM + * + * @return 0 if none, otherwise return facade target meta + */ + public final int getFacadeMeta(ForgeDirection side, int aCoverID, ISerializableObject aCoverVariable, + ICoverable aTileEntity) { + return getFacadeMetaImpl(side, aCoverID, forceCast(aCoverVariable), aTileEntity); + } + + /** + * Get the display stack. Default to {@code int2Stack(aCoverID)} + */ + public final ItemStack getDisplayStack(int aCoverID, ISerializableObject aCoverVariable) { + return getDisplayStackImpl(aCoverID, forceCast(aCoverVariable)); + } + + /** + * Get the special foreground cover texture associated with this cover. Return null if one should use the texture + * passed to {@link gregtech.api.GregTech_API#registerCover(ItemStack, ITexture, GT_CoverBehaviorBase)} or its + * overloads. + */ + public final ITexture getSpecialCoverFGTexture(ForgeDirection side, int aCoverID, + ISerializableObject aCoverVariable, ICoverable aTileEntity) { + return getSpecialCoverFGTextureImpl(side, aCoverID, forceCast(aCoverVariable), aTileEntity); + } + + /** + * Get the special cover texture associated with this cover. Return null if one should use the texture passed to + * {@link gregtech.api.GregTech_API#registerCover(ItemStack, ITexture, GT_CoverBehaviorBase)} or its overloads. + */ + public final ITexture getSpecialCoverTexture(ForgeDirection side, int aCoverID, ISerializableObject aCoverVariable, + ICoverable aTileEntity) { + return getSpecialCoverTextureImpl(side, aCoverID, forceCast(aCoverVariable), aTileEntity); + } + + /** + * Return whether cover data needs to be synced to client upon tile entity creation or cover placement. + * <p> + * Note if you want to sync the data afterwards you will have to manually do it by calling + * {@link ICoverable#issueCoverUpdate(ForgeDirection)} This option only affects the initial sync. + */ + public final boolean isDataNeededOnClient(ForgeDirection side, int aCoverID, ISerializableObject aCoverVariable, + ICoverable aTileEntity) { + return isDataNeededOnClientImpl(side, aCoverID, forceCast(aCoverVariable), aTileEntity); + } + + /** + * Called upon receiving data from network. Use {@link ICoverable#isClientSide()} to determine the side. + */ + public final void onDataChanged(ForgeDirection side, int aCoverID, ISerializableObject aCoverVariable, + ICoverable aTileEntity) { + onDataChangedImpl(side, aCoverID, forceCast(aCoverVariable), aTileEntity); + } + + /** + * Called before receiving data from network. Use {@link ICoverable#isClientSide()} to determine the side. + */ + public final void preDataChanged(ForgeDirection side, int aCoverID, int aNewCoverId, + ISerializableObject aCoverVariable, ISerializableObject aNewCoverVariable, ICoverable aTileEntity) { + preDataChangedImpl( + side, + aCoverID, + aNewCoverId, + forceCast(aCoverVariable), + forceCast(aNewCoverVariable), + aTileEntity); + } + + /** + * Called upon cover being removed. Called on both server and client. + */ + public final void onDropped(ForgeDirection side, int aCoverID, ISerializableObject aCoverVariable, + ICoverable aTileEntity) { + onDroppedImpl(side, aCoverID, forceCast(aCoverVariable), aTileEntity); + } + + public final boolean isRedstoneSensitive(ForgeDirection side, int aCoverID, ISerializableObject aCoverVariable, + ICoverable aTileEntity, long aTimer) { + return isRedstoneSensitiveImpl(side, aCoverID, forceCast(aCoverVariable), aTileEntity, aTimer); + } + + /** + * Called by updateEntity inside the covered TileEntity. aCoverVariable is the Value you returned last time. + */ + public final T doCoverThings(ForgeDirection side, byte aInputRedstone, int aCoverID, + ISerializableObject aCoverVariable, ICoverable aTileEntity, long aTimer) { + return doCoverThingsImpl(side, aInputRedstone, aCoverID, forceCast(aCoverVariable), aTileEntity, aTimer); + } + + /** + * Called when someone rightclicks this Cover. + * <p/> + * return true, if something actually happens. + */ + public final boolean onCoverRightClick(ForgeDirection side, int aCoverID, ISerializableObject aCoverVariable, + ICoverable aTileEntity, EntityPlayer aPlayer, float aX, float aY, float aZ) { + return onCoverRightClickImpl(side, aCoverID, forceCast(aCoverVariable), aTileEntity, aPlayer, aX, aY, aZ); + } + + /** + * Called when someone rightclicks this Cover with a Screwdriver. Doesn't call @onCoverRightclick in this Case. + * <p/> + * return the new Value of the Cover Variable + */ + public final T onCoverScrewdriverClick(ForgeDirection side, int aCoverID, ISerializableObject aCoverVariable, + ICoverable aTileEntity, EntityPlayer aPlayer, float aX, float aY, float aZ) { + return onCoverScrewdriverClickImpl(side, aCoverID, forceCast(aCoverVariable), aTileEntity, aPlayer, aX, aY, aZ); + } + + /** + * Called when someone shift-rightclicks this Cover with no tool. Doesn't call @onCoverRightclick in this Case. + */ + public final boolean onCoverShiftRightClick(ForgeDirection side, int aCoverID, ISerializableObject aCoverVariable, + ICoverable aTileEntity, EntityPlayer aPlayer) { + return onCoverShiftRightClickImpl(side, aCoverID, forceCast(aCoverVariable), aTileEntity, aPlayer); + } + + @Deprecated + public final Object getClientGUI(ForgeDirection side, int aCoverID, ISerializableObject aCoverVariable, + ICoverable aTileEntity, EntityPlayer aPlayer, World aWorld) { + return getClientGUIImpl(side, aCoverID, forceCast(aCoverVariable), aTileEntity, aPlayer, aWorld); + } + + /** + * Removes the Cover if this returns true, or if aForced is true. Doesn't get called when the Machine Block is + * getting broken, only if you break the Cover away from the Machine. + */ + public final boolean onCoverRemoval(ForgeDirection side, int aCoverID, ISerializableObject aCoverVariable, + ICoverable aTileEntity, boolean aForced) { + return onCoverRemovalImpl(side, aCoverID, forceCast(aCoverVariable), aTileEntity, aForced); + } + + /** + * Called upon Base TE being destroyed (once getDrops is called), thus getting called only when destroyed in + * survival. + */ + public final void onBaseTEDestroyed(ForgeDirection side, int aCoverID, ISerializableObject aCoverVariable, + ICoverable aTileEntity) { + onBaseTEDestroyedImpl(side, aCoverID, forceCast(aCoverVariable), aTileEntity); + } + + /** + * Gives a small Text for the status of the Cover. + */ + public final String getDescription(ForgeDirection side, int aCoverID, ISerializableObject aCoverVariable, + ICoverable aTileEntity) { + return getDescriptionImpl(side, aCoverID, forceCast(aCoverVariable), aTileEntity); + } + + /** + * How Blast Proof the Cover is. 30 is normal. + */ + public final float getBlastProofLevel(ForgeDirection side, int aCoverID, ISerializableObject aCoverVariable, + ICoverable aTileEntity) { + return getBlastProofLevelImpl(side, aCoverID, forceCast(aCoverVariable), aTileEntity); + } + + /** + * If it lets RS-Signals into the Block + * <p/> + * This is just Informative so that Machines know if their Redstone Input is blocked or not + */ + public final boolean letsRedstoneGoIn(ForgeDirection side, int aCoverID, ISerializableObject aCoverVariable, + ICoverable aTileEntity) { + return letsRedstoneGoInImpl(side, aCoverID, forceCast(aCoverVariable), aTileEntity); + } + + /** + * If it lets RS-Signals out of the Block + */ + public final boolean letsRedstoneGoOut(ForgeDirection side, int aCoverID, ISerializableObject aCoverVariable, + ICoverable aTileEntity) { + return letsRedstoneGoOutImpl(side, aCoverID, forceCast(aCoverVariable), aTileEntity); + } + + /** + * If it lets Energy into the Block + */ + public final boolean letsEnergyIn(ForgeDirection side, int aCoverID, ISerializableObject aCoverVariable, + ICoverable aTileEntity) { + return letsEnergyInImpl(side, aCoverID, forceCast(aCoverVariable), aTileEntity); + } + + /** + * If it lets Energy out of the Block + */ + public final boolean letsEnergyOut(ForgeDirection side, int aCoverID, ISerializableObject aCoverVariable, + ICoverable aTileEntity) { + return letsEnergyOutImpl(side, aCoverID, forceCast(aCoverVariable), aTileEntity); + } + + /** + * If it lets Liquids into the Block, aFluid can be null meaning if this is generally allowing Fluids or not. + */ + public final boolean letsFluidIn(ForgeDirection side, int aCoverID, ISerializableObject aCoverVariable, + Fluid aFluid, ICoverable aTileEntity) { + return letsFluidInImpl(side, aCoverID, forceCast(aCoverVariable), aFluid, aTileEntity); + } + + /** + * If it lets Liquids out of the Block, aFluid can be null meaning if this is generally allowing Fluids or not. + */ + public final boolean letsFluidOut(ForgeDirection side, int aCoverID, ISerializableObject aCoverVariable, + Fluid aFluid, ICoverable aTileEntity) { + return letsFluidOutImpl(side, aCoverID, forceCast(aCoverVariable), aFluid, aTileEntity); + } + + /** + * If it lets Items into the Block, aSlot = -1 means if it is generally accepting Items (return false for no + * reaction at all), aSlot = -2 means if it would accept for all Slots Impl(return true to skip the Checks for each + * Slot). + */ + public final boolean letsItemsIn(ForgeDirection side, int aCoverID, ISerializableObject aCoverVariable, int aSlot, + ICoverable aTileEntity) { + return letsItemsInImpl(side, aCoverID, forceCast(aCoverVariable), aSlot, aTileEntity); + } + + /** + * If it lets Items out of the Block, aSlot = -1 means if it is generally accepting Items (return false for no + * reaction at all), aSlot = -2 means if it would accept for all Slots Impl(return true to skip the Checks for each + * Slot). + */ + public final boolean letsItemsOut(ForgeDirection side, int aCoverID, ISerializableObject aCoverVariable, int aSlot, + ICoverable aTileEntity) { + return letsItemsOutImpl(side, aCoverID, forceCast(aCoverVariable), aSlot, aTileEntity); + } + + /** + * If it lets you rightclick the Machine normally + */ + public final boolean isGUIClickable(ForgeDirection side, int aCoverID, ISerializableObject aCoverVariable, + ICoverable aTileEntity) { + return isGUIClickableImpl(side, aCoverID, forceCast(aCoverVariable), aTileEntity); + } + + /** + * Needs to return true for Covers, which have a Redstone Output on their Facing. + */ + public final boolean manipulatesSidedRedstoneOutput(ForgeDirection side, int aCoverID, + ISerializableObject aCoverVariable, ICoverable aTileEntity) { + return manipulatesSidedRedstoneOutputImpl(side, aCoverID, forceCast(aCoverVariable), aTileEntity); + } + + /** + * if this Cover should let Pipe Connections look connected even if it is not the case. + */ + public final boolean alwaysLookConnected(ForgeDirection side, int aCoverID, ISerializableObject aCoverVariable, + ICoverable aTileEntity) { + return alwaysLookConnectedImpl(side, aCoverID, forceCast(aCoverVariable), aTileEntity); + } + + /** + * Called to determine the incoming Redstone Signal of a Machine. Returns the original Redstone per default. The + * Cover should @letsRedstoneGoIn or the aInputRedstone Parameter is always 0. + */ + public final byte getRedstoneInput(ForgeDirection side, byte aInputRedstone, int aCoverID, + ISerializableObject aCoverVariable, ICoverable aTileEntity) { + return getRedstoneInputImpl(side, aInputRedstone, aCoverID, forceCast(aCoverVariable), aTileEntity); + } + + /** + * Gets the Tick Rate for doCoverThings of the Cover + * <p/> + * 0 = No Ticks! Yes, 0 is Default, you have to override this + */ + public final int getTickRate(ForgeDirection side, int aCoverID, ISerializableObject aCoverVariable, + ICoverable aTileEntity) { + return getTickRateImpl(side, aCoverID, forceCast(aCoverVariable), aTileEntity); + } + + /** + * The MC Color of this Lens. -1 for no Color (meaning this isn't a Lens then). + */ + public final byte getLensColor(ForgeDirection side, int aCoverID, ISerializableObject aCoverVariable, + ICoverable aTileEntity) { + return getLensColorImpl(side, aCoverID, forceCast(aCoverVariable), aTileEntity); + } + + /** + * @return the ItemStack dropped by this Cover + */ + public final ItemStack getDrop(ForgeDirection side, int aCoverID, ISerializableObject aCoverVariable, + ICoverable aTileEntity) { + return getDropImpl(side, aCoverID, forceCast(aCoverVariable), aTileEntity); + } + + /** + * Called when the cover is initially attached to a machine. + * + * @param player The attaching player + * @param aCover An {@link ItemStack} containing the cover + * @param aTileEntity The machine receiving the cover + * @param side Which side the cover is attached to + */ + public void onPlayerAttach(EntityPlayer player, ItemStack aCover, ICoverable aTileEntity, ForgeDirection side) { + // Do nothing by default. + } + + // endregion + + // region UI stuff + + protected GT_TooltipDataCache mTooltipCache = new GT_TooltipDataCache(); + protected GT_GUIColorOverride colorOverride; + private static final String guiTexturePath = "gregtech:textures/gui/GuiCover.png"; + + /** + * For back compatibility, you need to override this if this cover uses ModularUI. + */ + public boolean useModularUI() { + return false; + } + + public ModularWindow createWindow(GT_CoverUIBuildContext buildContext) { + return new UIFactory(buildContext).createWindow(); + } + + /** + * Creates {@link ModularWindow} for this cover. This is separated from base class, as attaching the same covers in + * different sides of the same tile needs different UI with different context. + */ + protected class UIFactory { + + private final GT_CoverUIBuildContext uiBuildContext; + + public UIFactory(GT_CoverUIBuildContext buildContext) { + this.uiBuildContext = buildContext; + } + + public ModularWindow createWindow() { + ModularWindow.Builder builder = ModularWindow.builder(getGUIWidth(), getGUIHeight()); + builder.setBackground(ModularUITextures.VANILLA_BACKGROUND); + builder.setGuiTint(getUIBuildContext().getGuiColorization()); + if (doesBindPlayerInventory() && !getUIBuildContext().isAnotherWindow()) { + builder.bindPlayerInventory(getUIBuildContext().getPlayer()); + } + addTitleToUI(builder); + addUIWidgets(builder); + if (getUIBuildContext().isAnotherWindow()) { + builder.widget( + ButtonWidget.closeWindowButton(true) + .setPos(getGUIWidth() - 15, 3)); + } + + final CoverInfo coverInfo = uiBuildContext.getTile() + .getCoverInfoAtSide(uiBuildContext.getCoverSide()); + final GT_CoverBehaviorBase<?> behavior = coverInfo.getCoverBehavior(); + if (coverInfo.getMinimumTickRate() > 0 && behavior.allowsTickRateAddition()) { + builder.widget( + new GT_CoverTickRateButton(coverInfo, builder).setPos(getGUIWidth() - 24, getGUIHeight() - 24)); + } + + return builder.build(); + } + + /** + * Override this to add widgets for your UI. + */ + protected void addUIWidgets(ModularWindow.Builder builder) {} + + public GT_CoverUIBuildContext getUIBuildContext() { + return uiBuildContext; + } + + /** + * Can return null when cover data is invalid e.g. tile is broken or cover is removed + */ + @Nullable + public T getCoverData() { + if (isCoverValid()) { + return forceCast( + getUIBuildContext().getTile() + .getComplexCoverDataAtSide(getUIBuildContext().getCoverSide())); + } else { + return null; + } + } + + public boolean setCoverData(T data) { + if (isCoverValid()) { + getUIBuildContext().getTile() + .receiveCoverData( + getUIBuildContext().getCoverSide(), + getUIBuildContext().getCoverID(), + data, + getUIBuildContext().getPlayer() instanceof EntityPlayerMP + ? (EntityPlayerMP) getUIBuildContext().getPlayer() + : null); + return true; + } else { + return false; + } + } + + public boolean isCoverValid() { + return !getUIBuildContext().getTile() + .isDead() + && getUIBuildContext().getTile() + .getCoverBehaviorAtSideNew(getUIBuildContext().getCoverSide()) != GregTech_API.sNoBehavior; + } + + protected void addTitleToUI(ModularWindow.Builder builder) { + ItemStack coverItem = GT_Utility.intToStack(getUIBuildContext().getCoverID()); + if (coverItem != null) { + builder.widget( + new ItemDrawable(coverItem).asWidget() + .setPos(5, 5) + .setSize(16, 16)) + .widget( + new TextWidget(coverItem.getDisplayName()).setDefaultColor(COLOR_TITLE.get()) + .setPos(25, 9)); + } + } + + protected int getGUIWidth() { + return 176; + } + + protected int getGUIHeight() { + return 107; + } + + protected boolean doesBindPlayerInventory() { + return false; + } + + protected int getTextColorOrDefault(String textType, int defaultColor) { + return colorOverride.getTextColorOrDefault(textType, defaultColor); + } + + protected final Supplier<Integer> COLOR_TITLE = () -> getTextColorOrDefault("title", 0x222222); + protected final Supplier<Integer> COLOR_TEXT_GRAY = () -> getTextColorOrDefault("text_gray", 0x555555); + protected final Supplier<Integer> COLOR_TEXT_WARN = () -> getTextColorOrDefault("text_warn", 0xff0000); + } + + // endregion + + // region impl + + protected Block getFacadeBlockImpl(ForgeDirection side, int aCoverID, T aCoverVariable, ICoverable aTileEntity) { + return null; + } + + protected int getFacadeMetaImpl(ForgeDirection side, int aCoverID, T aCoverVariable, ICoverable aTileEntity) { + return 0; + } + + protected ItemStack getDisplayStackImpl(int aCoverID, T aCoverVariable) { + return GT_Utility.intToStack(aCoverID); + } + + protected ITexture getSpecialCoverFGTextureImpl(ForgeDirection side, int aCoverID, T aCoverVariable, + ICoverable aTileEntity) { + return coverFGTexture; + } + + protected ITexture getSpecialCoverTextureImpl(ForgeDirection side, int aCoverID, T aCoverVariable, + ICoverable aTileEntity) { + return null; + } + + protected boolean isDataNeededOnClientImpl(ForgeDirection side, int aCoverID, T aCoverVariable, + ICoverable aTileEntity) { + return false; + } + + protected void onDataChangedImpl(ForgeDirection side, int aCoverID, T aCoverVariable, ICoverable aTileEntity) {} + + protected void preDataChangedImpl(ForgeDirection side, int aCoverID, int aNewCoverId, T aCoverVariable, + T aNewCoverVariable, ICoverable aTileEntity) {} + + protected void onDroppedImpl(ForgeDirection side, int aCoverID, T aCoverVariable, ICoverable aTileEntity) {} + + protected void onBaseTEDestroyedImpl(ForgeDirection side, int aCoverID, T aCoverVariable, ICoverable aTileEntity) {} + + protected boolean isRedstoneSensitiveImpl(ForgeDirection side, int aCoverID, T aCoverVariable, + ICoverable aTileEntity, long aTimer) { + return false; + } + + /** + * Called by updateEntity inside the covered TileEntity. aCoverVariable is the Value you returned last time. + */ + protected T doCoverThingsImpl(ForgeDirection side, byte aInputRedstone, int aCoverID, T aCoverVariable, + ICoverable aTileEntity, long aTimer) { + return aCoverVariable; + } + + /** + * Called when someone rightclicks this Cover. + * <p/> + * return true, if something actually happens. + */ + protected boolean onCoverRightClickImpl(ForgeDirection side, int aCoverID, T aCoverVariable, ICoverable aTileEntity, + EntityPlayer aPlayer, float aX, float aY, float aZ) { + return false; + } + + /** + * Called when someone rightclicks this Cover with a Screwdriver. Doesn't call @onCoverRightclick in this Case. + * <p/> + * return the new Value of the Cover Variable + */ + protected T onCoverScrewdriverClickImpl(ForgeDirection side, int aCoverID, T aCoverVariable, ICoverable aTileEntity, + EntityPlayer aPlayer, float aX, float aY, float aZ) { + return aCoverVariable; + } + + /** + * Called when someone shift-rightclicks this Cover with no tool. Doesn't call @onCoverRightclick in this Case. + */ + protected boolean onCoverShiftRightClickImpl(ForgeDirection side, int aCoverID, T aCoverVariable, + ICoverable aTileEntity, EntityPlayer aPlayer) { + if (hasCoverGUI() && aPlayer instanceof EntityPlayerMP) { + lastPlayer = new WeakReference<>(aPlayer); + if (useModularUI()) { + GT_UIInfos.openCoverUI(aTileEntity, aPlayer, side); + } else { + GT_Values.NW.sendToPlayer( + new GT_Packet_TileEntityCoverGUI( + side, + aCoverID, + aCoverVariable, + aTileEntity, + (EntityPlayerMP) aPlayer), + (EntityPlayerMP) aPlayer); + } + return true; + } + return false; + } + + @Deprecated + protected Object getClientGUIImpl(ForgeDirection side, int aCoverID, T aCoverVariable, ICoverable aTileEntity, + EntityPlayer aPlayer, World aWorld) { + return null; + } + + /** + * Removes the Cover if this returns true, or if aForced is true. Doesn't get called when the Machine Block is + * getting broken, only if you break the Cover away from the Machine. + */ + protected boolean onCoverRemovalImpl(ForgeDirection side, int aCoverID, T aCoverVariable, ICoverable aTileEntity, + boolean aForced) { + return true; + } + + /** + * Gives a small Text for the status of the Cover. + */ + protected String getDescriptionImpl(ForgeDirection side, int aCoverID, T aCoverVariable, ICoverable aTileEntity) { + return E; + } + + /** + * How Blast Proof the Cover is. 30 is normal. + */ + protected float getBlastProofLevelImpl(ForgeDirection side, int aCoverID, T aCoverVariable, + ICoverable aTileEntity) { + return 10.0F; + } + + /** + * If it lets RS-Signals into the Block + * <p/> + * This is just Informative so that Machines know if their Redstone Input is blocked or not + */ + protected boolean letsRedstoneGoInImpl(ForgeDirection side, int aCoverID, T aCoverVariable, + ICoverable aTileEntity) { + return false; + } + + /** + * If it lets RS-Signals out of the Block + */ + protected boolean letsRedstoneGoOutImpl(ForgeDirection side, int aCoverID, T aCoverVariable, + ICoverable aTileEntity) { + return false; + } + + /** + * If it lets Energy into the Block + */ + protected boolean letsEnergyInImpl(ForgeDirection side, int aCoverID, T aCoverVariable, ICoverable aTileEntity) { + return false; + } + + /** + * If it lets Energy out of the Block + */ + protected boolean letsEnergyOutImpl(ForgeDirection side, int aCoverID, T aCoverVariable, ICoverable aTileEntity) { + return false; + } + + /** + * If it lets Liquids into the Block, aFluid can be null meaning if this is generally allowing Fluids or not. + */ + protected boolean letsFluidInImpl(ForgeDirection side, int aCoverID, T aCoverVariable, Fluid aFluid, + ICoverable aTileEntity) { + return false; + } + + /** + * If it lets Liquids out of the Block, aFluid can be null meaning if this is generally allowing Fluids or not. + */ + protected boolean letsFluidOutImpl(ForgeDirection side, int aCoverID, T aCoverVariable, Fluid aFluid, + ICoverable aTileEntity) { + return false; + } + + /** + * If it lets Items into the Block, aSlot = -1 means if it is generally accepting Items (return false for no + * Interaction at all), aSlot = -2 means if it would accept for all Slots (return true to skip the Checks for each + * Slot). + */ + protected boolean letsItemsInImpl(ForgeDirection side, int aCoverID, T aCoverVariable, int aSlot, + ICoverable aTileEntity) { + return false; + } + + /** + * If it lets Items out of the Block, aSlot = -1 means if it is generally accepting Items (return false for no + * Interaction at all), aSlot = -2 means if it would accept for all Slots (return true to skip the Checks for each + * Slot). + */ + protected boolean letsItemsOutImpl(ForgeDirection side, int aCoverID, T aCoverVariable, int aSlot, + ICoverable aTileEntity) { + return false; + } + + /** + * If it lets you rightclick the Machine normally + */ + protected boolean isGUIClickableImpl(ForgeDirection side, int aCoverID, T aCoverVariable, ICoverable aTileEntity) { + return true; + } + + /** + * Needs to return true for Covers, which have a Redstone Output on their Facing. + */ + protected boolean manipulatesSidedRedstoneOutputImpl(ForgeDirection side, int aCoverID, T aCoverVariable, + ICoverable aTileEntity) { + return false; + } + + /** + * if this Cover should let Pipe Connections look connected even if it is not the case. + */ + protected boolean alwaysLookConnectedImpl(ForgeDirection side, int aCoverID, T aCoverVariable, + ICoverable aTileEntity) { + return false; + } + + /** + * Called to determine the incoming Redstone Signal of a Machine. Returns the original Redstone per default. The + * Cover should @letsRedstoneGoIn or the aInputRedstone Parameter is always 0. + */ + protected byte getRedstoneInputImpl(ForgeDirection side, byte aInputRedstone, int aCoverID, T aCoverVariable, + ICoverable aTileEntity) { + return letsRedstoneGoIn(side, aCoverID, aCoverVariable, aTileEntity) ? aInputRedstone : 0; + } + + /** + * Gets the Tick Rate for doCoverThings of the Cover + * <p/> + * 0 = No Ticks! Yes, 0 is Default, you have to override this + */ + protected int getTickRateImpl(ForgeDirection side, int aCoverID, T aCoverVariable, ICoverable aTileEntity) { + return 0; + } + + /** + * The MC Color of this Lens. -1 for no Color (meaning this isn't a Lens then). + */ + protected byte getLensColorImpl(ForgeDirection side, int aCoverID, T aCoverVariable, ICoverable aTileEntity) { + return -1; + } + + /** + * @return the ItemStack dropped by this Cover + */ + protected ItemStack getDropImpl(ForgeDirection side, int aCoverID, T aCoverVariable, ICoverable aTileEntity) { + return GT_OreDictUnificator.get(true, aTileEntity.getCoverItemAtSide(side)); + } + + // endregion + + // region no data + + /** + * Checks if the Cover can be placed on this. + */ + public boolean isCoverPlaceable(ForgeDirection side, ItemStack aStack, ICoverable aTileEntity) { + return true; + } + + public boolean hasCoverGUI() { + return false; + } + + /** + * Called when someone rightclicks this Cover Client Side + * <p/> + * return true, if something actually happens. + */ + public boolean onCoverRightclickClient(ForgeDirection side, ICoverable aTileEntity, EntityPlayer aPlayer, float aX, + float aY, float aZ) { + return false; + } + + /** + * If this is a simple Cover, which can also be used on Bronze Machines and similar. + */ + public boolean isSimpleCover() { + return false; + } + + /** + * sets the Cover upon placement. + */ + public void placeCover(ForgeDirection side, ItemStack aCover, ICoverable aTileEntity) { + aTileEntity.setCoverIDAtSide(side, GT_Utility.stackToInt(aCover)); + } + + public boolean allowsCopyPasteTool() { + return true; + } + + public boolean allowsTickRateAddition() { + return true; + } + + @NotNull + public final List<String> getAdditionalTooltip(ISerializableObject coverData) { + return getAdditionalTooltipImpl(forceCast(coverData)); + } + + /** + * Override to add to the tooltip generated when a user hovers over the cover on the left side of a machine's UI. + * + * @param coverData The cover data associated with the cover on a particular side. + * @return A list of new tooltip entries. Entries are inserted at the top, just after the name and direction line. + */ + protected List<String> getAdditionalTooltipImpl(T coverData) { + return ImmutableList.of(); + } + // endregion +} diff --git a/src/main/java/gregtech/api/util/GT_CreativeTab.java b/src/main/java/gregtech/api/util/GT_CreativeTab.java new file mode 100644 index 0000000000..1049f40278 --- /dev/null +++ b/src/main/java/gregtech/api/util/GT_CreativeTab.java @@ -0,0 +1,26 @@ +package gregtech.api.util; + +import net.minecraft.creativetab.CreativeTabs; +import net.minecraft.init.Blocks; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; + +import gregtech.api.enums.ItemList; + +public class GT_CreativeTab extends CreativeTabs { + + public GT_CreativeTab(String aName, String aLocalName) { + super("GregTech." + aName); + GT_LanguageManager.addStringLocalization("itemGroup.GregTech." + aName, aLocalName); + } + + @Override + public ItemStack getIconItemStack() { + return ItemList.Tool_Cheat.get(1, new ItemStack(Blocks.iron_block, 1)); + } + + @Override + public Item getTabIconItem() { + return ItemList.Circuit_Integrated.getItem(); + } +} diff --git a/src/main/java/gregtech/api/util/GT_ExoticEnergyInputHelper.java b/src/main/java/gregtech/api/util/GT_ExoticEnergyInputHelper.java new file mode 100644 index 0000000000..d59796b251 --- /dev/null +++ b/src/main/java/gregtech/api/util/GT_ExoticEnergyInputHelper.java @@ -0,0 +1,114 @@ +package gregtech.api.util; + +import static gregtech.api.util.GT_Utility.filterValidMTEs; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import gregtech.api.interfaces.metatileentity.IMetaTileEntity; +import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch; + +public class GT_ExoticEnergyInputHelper { + + /** + * The Valid Types of TecTech Hatch List. + */ + private static final List<Class<? extends GT_MetaTileEntity_Hatch>> sExoticEnergyHatchType = new ArrayList<>(); + + static { + tryRegister("com.github.technus.tectech.thing.metaTileEntity.hatch.GT_MetaTileEntity_Hatch_EnergyMulti"); + tryRegister("com.github.technus.tectech.thing.metaTileEntity.hatch.GT_MetaTileEntity_Hatch_EnergyTunnel"); + } + + public static void register(Class<? extends GT_MetaTileEntity_Hatch> clazz) { + if (!GT_MetaTileEntity_Hatch.class.isAssignableFrom(clazz)) throw new IllegalArgumentException( + clazz.getName() + " is not a subclass of " + GT_MetaTileEntity_Hatch.class.getName()); + sExoticEnergyHatchType.add(clazz); + } + + @SuppressWarnings("unchecked") + public static void tryRegister(String className) { + Class<?> clazz; + try { + clazz = Class.forName(className); + } catch (ClassNotFoundException e) { + return; + } + if (!GT_MetaTileEntity_Hatch.class.isAssignableFrom(clazz)) throw new IllegalArgumentException( + clazz.getName() + " is not a subclass of " + GT_MetaTileEntity_Hatch.class.getName()); + sExoticEnergyHatchType.add((Class<? extends GT_MetaTileEntity_Hatch>) clazz); + } + + public static boolean drainEnergy(long aEU, Collection<? extends GT_MetaTileEntity_Hatch> hatches) { + for (GT_MetaTileEntity_Hatch tHatch : hatches) { + long tDrain = Math.min( + tHatch.getBaseMetaTileEntity() + .getStoredEU(), + aEU); + tHatch.getBaseMetaTileEntity() + .decreaseStoredEnergyUnits(tDrain, false); + aEU -= tDrain; + } + return aEU <= 0; + } + + public static boolean isExoticEnergyInput(IMetaTileEntity aHatch) { + for (Class<?> clazz : sExoticEnergyHatchType) { + if (clazz.isInstance(aHatch)) return true; + } + return false; + } + + public static long getTotalEuMulti(Collection<? extends GT_MetaTileEntity_Hatch> hatches) { + long rEU = 0L; + for (GT_MetaTileEntity_Hatch tHatch : filterValidMTEs(hatches)) { + rEU += tHatch.getBaseMetaTileEntity() + .getInputVoltage() * tHatch.maxWorkingAmperesIn(); + } + return rEU; + } + + public static long getMaxInputVoltageMulti(Collection<? extends GT_MetaTileEntity_Hatch> hatches) { + long rVoltage = 0; + for (GT_MetaTileEntity_Hatch tHatch : filterValidMTEs(hatches)) { + rVoltage += tHatch.getBaseMetaTileEntity() + .getInputVoltage(); + } + return rVoltage; + } + + public static long getAverageInputVoltageMulti(Collection<? extends GT_MetaTileEntity_Hatch> hatches) { + long rVoltage = 0; + for (GT_MetaTileEntity_Hatch tHatch : filterValidMTEs(hatches)) { + rVoltage += tHatch.getBaseMetaTileEntity() + .getInputVoltage(); + } + if (hatches.isEmpty()) { + return 0; + } + return rVoltage / hatches.size(); + } + + public static long getMaxInputAmpsMulti(Collection<? extends GT_MetaTileEntity_Hatch> hatches) { + long rAmp = 0; + for (GT_MetaTileEntity_Hatch tHatch : filterValidMTEs(hatches)) { + rAmp += tHatch.getBaseMetaTileEntity() + .getInputAmperage(); + } + return rAmp; + } + + public static long getMaxWorkingInputAmpsMulti(Collection<? extends GT_MetaTileEntity_Hatch> hatches) { + long rAmp = 0; + for (GT_MetaTileEntity_Hatch tHatch : filterValidMTEs(hatches)) { + rAmp += tHatch.maxWorkingAmperesIn(); + } + return rAmp; + } + + public static List<Class<? extends GT_MetaTileEntity_Hatch>> getAllClasses() { + return Collections.unmodifiableList(sExoticEnergyHatchType); + } +} diff --git a/src/main/java/gregtech/api/util/GT_FoodStat.java b/src/main/java/gregtech/api/util/GT_FoodStat.java new file mode 100644 index 0000000000..5eda76f9d0 --- /dev/null +++ b/src/main/java/gregtech/api/util/GT_FoodStat.java @@ -0,0 +1,122 @@ +package gregtech.api.util; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.Items; +import net.minecraft.item.EnumAction; +import net.minecraft.item.ItemStack; +import net.minecraft.potion.PotionEffect; + +import gregtech.api.damagesources.GT_DamageSources; +import gregtech.api.enums.SoundResource; +import gregtech.api.interfaces.IFoodStat; +import gregtech.api.items.GT_MetaBase_Item; + +public class GT_FoodStat implements IFoodStat { + + private final int mFoodLevel; + private final int[] mPotionEffects; + private final float mSaturation; + private final EnumAction mAction; + private final ItemStack mEmptyContainer; + private final boolean mAlwaysEdible, mInvisibleParticles, mIsRotten; + private boolean mExplosive = false, mMilk = false; + + /** + * @param aFoodLevel Amount of Food in Half Bacon [0 - 20] + * @param aSaturation Amount of Saturation [0.0F - 1.0F] + * @param aAction The Action to be used. If this is null, it uses the Eating Action + * @param aEmptyContainer An empty Container (Optional) + * @param aAlwaysEdible If this Item is always edible, like Golden Apples or Potions + * @param aInvisibleParticles If the Particles of the Potion Effects are invisible + * @param aPotionEffects An Array of Potion Effects with %4==0 Elements as follows ID of a Potion Effect. 0 for + * none Duration of the Potion in Ticks Level of the Effect. [0, 1, 2] are for [I, II, + * III] The likelihood that this Potion Effect takes place upon being eaten [1 - 100] + */ + public GT_FoodStat(int aFoodLevel, float aSaturation, EnumAction aAction, ItemStack aEmptyContainer, + boolean aAlwaysEdible, boolean aInvisibleParticles, boolean aIsRotten, int... aPotionEffects) { + mFoodLevel = aFoodLevel; + mSaturation = aSaturation; + mAction = aAction == null ? EnumAction.eat : aAction; + mPotionEffects = aPotionEffects; + mEmptyContainer = GT_Utility.copyOrNull(aEmptyContainer); + mInvisibleParticles = aInvisibleParticles; + mAlwaysEdible = aAlwaysEdible; + mIsRotten = aIsRotten; + } + + public GT_FoodStat setExplosive() { + mExplosive = true; + return this; + } + + public GT_FoodStat setMilk() { + mMilk = true; + return this; + } + + @Override + public int getFoodLevel(GT_MetaBase_Item aItem, ItemStack aStack, EntityPlayer aPlayer) { + return mFoodLevel; + } + + @Override + public float getSaturation(GT_MetaBase_Item aItem, ItemStack aStack, EntityPlayer aPlayer) { + return mSaturation; + } + + @Override + public void onEaten(GT_MetaBase_Item aItem, ItemStack aStack, EntityPlayer aPlayer) { + aStack.stackSize--; + ItemStack tStack = GT_OreDictUnificator.get(GT_Utility.copyOrNull(mEmptyContainer)); + if (tStack != null && !aPlayer.inventory.addItemStackToInventory(tStack)) + aPlayer.dropPlayerItemWithRandomChoice(tStack, true); + + new WorldSpawnedEventBuilder.SoundAtEntityEventBuilder().setIdentifier(SoundResource.RANDOM_BURP) + .setVolume(0.5F) + .setPitch(aPlayer.worldObj.rand.nextFloat() * 0.1F + 0.9F) + .setEntity(aPlayer) + .setWorld(aPlayer.worldObj) + .run(); + + if (!aPlayer.worldObj.isRemote) { + if (mMilk) { + aPlayer.curePotionEffects(new ItemStack(Items.milk_bucket, 1, 0)); + } + for (int i = 3; i < mPotionEffects.length; i += 4) { + if (aPlayer.worldObj.rand.nextInt(100) < mPotionEffects[i]) { + aPlayer.addPotionEffect( + new PotionEffect( + mPotionEffects[i - 3], + mPotionEffects[i - 2], + mPotionEffects[i - 1], + mInvisibleParticles)); + } + } + if (mExplosive) { + new WorldSpawnedEventBuilder.ExplosionEffectEventBuilder().setSmoking(true) + .setFlaming(true) + .setStrength(4f) + .setPosition(aPlayer.posX, aPlayer.posY, aPlayer.posZ) + .setEntity(aPlayer) + .setWorld(aPlayer.worldObj) + .run(); + aPlayer.attackEntityFrom(GT_DamageSources.getExplodingDamage(), Float.MAX_VALUE); + } + } + } + + @Override + public EnumAction getFoodAction(GT_MetaBase_Item aItem, ItemStack aStack) { + return mAction; + } + + @Override + public boolean alwaysEdible(GT_MetaBase_Item aItem, ItemStack aStack, EntityPlayer aPlayer) { + return mAlwaysEdible; + } + + @Override + public boolean isRotten(GT_MetaBase_Item aItem, ItemStack aStack, EntityPlayer aPlayer) { + return mIsRotten; + } +} diff --git a/src/main/java/gregtech/api/util/GT_Forestry_Compat.java b/src/main/java/gregtech/api/util/GT_Forestry_Compat.java new file mode 100644 index 0000000000..427703e6f7 --- /dev/null +++ b/src/main/java/gregtech/api/util/GT_Forestry_Compat.java @@ -0,0 +1,195 @@ +package gregtech.api.util; + +import java.util.Map; + +import net.minecraft.item.ItemStack; +import net.minecraftforge.fluids.FluidStack; + +import forestry.api.recipes.ICentrifugeRecipe; +import forestry.api.recipes.ISqueezerRecipe; +import forestry.api.recipes.RecipeManagers; +import gregtech.api.enums.GT_Values; +import gregtech.api.enums.ItemList; +import gregtech.api.enums.Materials; +import gregtech.api.recipe.RecipeMaps; + +public class GT_Forestry_Compat { + + public static void populateFakeNeiRecipes() { + if (ItemList.FR_Bee_Drone.get(1L) != null) { + RecipeMaps.scannerFakeRecipes.addFakeRecipe( + false, + new ItemStack[] { ItemList.FR_Bee_Drone.getWildcard(1L) }, + new ItemStack[] { ItemList.FR_Bee_Drone.getWithName(1L, "Scanned Drone") }, + null, + new FluidStack[] { Materials.Honey.getFluid(100L) }, + null, + 500, + 2, + 0); + } + if (ItemList.FR_Bee_Princess.get(1L) != null) { + RecipeMaps.scannerFakeRecipes.addFakeRecipe( + false, + new ItemStack[] { ItemList.FR_Bee_Princess.getWildcard(1L) }, + new ItemStack[] { ItemList.FR_Bee_Princess.getWithName(1L, "Scanned Princess") }, + null, + new FluidStack[] { Materials.Honey.getFluid(100L) }, + null, + 500, + 2, + 0); + } + if (ItemList.FR_Bee_Queen.get(1L) != null) { + RecipeMaps.scannerFakeRecipes.addFakeRecipe( + false, + new ItemStack[] { ItemList.FR_Bee_Queen.getWildcard(1L) }, + new ItemStack[] { ItemList.FR_Bee_Queen.getWithName(1L, "Scanned Queen") }, + null, + new FluidStack[] { Materials.Honey.getFluid(100L) }, + null, + 500, + 2, + 0); + } + if (ItemList.FR_Tree_Sapling.get(1L) != null) { + RecipeMaps.scannerFakeRecipes.addFakeRecipe( + false, + new ItemStack[] { ItemList.FR_Tree_Sapling.getWildcard(1L) }, + new ItemStack[] { ItemList.FR_Tree_Sapling.getWithName(1L, "Scanned Sapling") }, + null, + new FluidStack[] { Materials.Honey.getFluid(100L) }, + null, + 500, + 2, + 0); + } + if (ItemList.FR_Butterfly.get(1L) != null) { + RecipeMaps.scannerFakeRecipes.addFakeRecipe( + false, + new ItemStack[] { ItemList.FR_Butterfly.getWildcard(1L) }, + new ItemStack[] { ItemList.FR_Butterfly.getWithName(1L, "Scanned Butterfly") }, + null, + new FluidStack[] { Materials.Honey.getFluid(100L) }, + null, + 500, + 2, + 0); + } + if (ItemList.FR_Larvae.get(1L) != null) { + RecipeMaps.scannerFakeRecipes.addFakeRecipe( + false, + new ItemStack[] { ItemList.FR_Larvae.getWildcard(1L) }, + new ItemStack[] { ItemList.FR_Larvae.getWithName(1L, "Scanned Larvae") }, + null, + new FluidStack[] { Materials.Honey.getFluid(100L) }, + null, + 500, + 2, + 0); + } + if (ItemList.FR_Serum.get(1L) != null) { + RecipeMaps.scannerFakeRecipes.addFakeRecipe( + false, + new ItemStack[] { ItemList.FR_Serum.getWildcard(1L) }, + new ItemStack[] { ItemList.FR_Serum.getWithName(1L, "Scanned Serum") }, + null, + new FluidStack[] { Materials.Honey.getFluid(100L) }, + null, + 500, + 2, + 0); + } + if (ItemList.FR_Caterpillar.get(1L) != null) { + RecipeMaps.scannerFakeRecipes.addFakeRecipe( + false, + new ItemStack[] { ItemList.FR_Caterpillar.getWildcard(1L) }, + new ItemStack[] { ItemList.FR_Caterpillar.getWithName(1L, "Scanned Caterpillar") }, + null, + new FluidStack[] { Materials.Honey.getFluid(100L) }, + null, + 500, + 2, + 0); + } + if (ItemList.FR_PollenFertile.get(1L) != null) { + RecipeMaps.scannerFakeRecipes.addFakeRecipe( + false, + new ItemStack[] { ItemList.FR_PollenFertile.getWildcard(1L) }, + new ItemStack[] { ItemList.FR_PollenFertile.getWithName(1L, "Scanned Pollen") }, + null, + new FluidStack[] { Materials.Honey.getFluid(100L) }, + null, + 500, + 2, + 0); + } + } + + public static void transferCentrifugeRecipes() { + try { + for (ICentrifugeRecipe tRecipe : RecipeManagers.centrifugeManager.recipes()) { + Map<ItemStack, Float> outputs = tRecipe.getAllProducts(); + ItemStack[] tOutputs = new ItemStack[outputs.size()]; + int[] tChances = new int[outputs.size()]; + int i = 0; + for (Map.Entry<ItemStack, Float> entry : outputs.entrySet()) { + tChances[i] = (int) (entry.getValue() * 10000); + tOutputs[i] = entry.getKey() + .copy(); + i++; + } + RecipeMaps.centrifugeRecipes.addRecipe( + true, + new ItemStack[] { tRecipe.getInput() }, + tOutputs, + null, + tChances, + null, + null, + 128, + 5, + 0); + RecipeMaps.centrifugeNonCellRecipes.addRecipe( + true, + new ItemStack[] { tRecipe.getInput() }, + tOutputs, + null, + tChances, + null, + null, + 128, + 5, + 0); + } + } catch (Throwable e) { + if (GT_Values.D1) { + e.printStackTrace(GT_Log.err); + } + } + } + + public static void transferSqueezerRecipes() { + try { + for (ISqueezerRecipe tRecipe : RecipeManagers.squeezerManager.recipes()) { + if ((tRecipe.getResources().length == 1) && (tRecipe.getFluidOutput() != null)) { + RecipeMaps.fluidExtractionRecipes.addRecipe( + true, + new ItemStack[] { tRecipe.getResources()[0] }, + new ItemStack[] { tRecipe.getRemnants() }, + null, + new int[] { (int) (tRecipe.getRemnantsChance() * 10000) }, + null, + new FluidStack[] { tRecipe.getFluidOutput() }, + 32, + 8, + 0); + } + } + } catch (Throwable e) { + if (GT_Values.D1) { + e.printStackTrace(GT_Log.err); + } + } + } +} diff --git a/src/main/java/gregtech/api/util/GT_GC_Compat.java b/src/main/java/gregtech/api/util/GT_GC_Compat.java new file mode 100644 index 0000000000..24710ab0ac --- /dev/null +++ b/src/main/java/gregtech/api/util/GT_GC_Compat.java @@ -0,0 +1,52 @@ +package gregtech.api.util; + +import static gregtech.api.enums.Mods.GalacticraftCore; + +import net.minecraft.tileentity.TileEntity; +import net.minecraftforge.common.util.ForgeDirection; + +import micdoodle8.mods.galacticraft.api.power.EnergySource; +import micdoodle8.mods.galacticraft.api.power.EnergySource.EnergySourceAdjacent; +import micdoodle8.mods.galacticraft.api.power.IEnergyHandlerGC; +import micdoodle8.mods.galacticraft.api.transmission.NetworkType; +import micdoodle8.mods.galacticraft.api.transmission.tile.IConnector; +import micdoodle8.mods.galacticraft.core.energy.EnergyConfigHandler; + +public class GT_GC_Compat { + + public static long insertEnergyInto(TileEntity tTileEntity, long aVoltage, ForgeDirection tDirection) { + // GC Compat + if (GalacticraftCore.isModLoaded() && tTileEntity instanceof IEnergyHandlerGC) { + if (!(tTileEntity instanceof IConnector) + || ((IConnector) tTileEntity).canConnect(tDirection, NetworkType.POWER)) { + EnergySource eSource = new EnergySourceAdjacent(tDirection); + + float tSizeToReceive = aVoltage * EnergyConfigHandler.IC2_RATIO, + tStored = ((IEnergyHandlerGC) tTileEntity).getEnergyStoredGC(eSource); + if (tSizeToReceive >= tStored + || tSizeToReceive <= ((IEnergyHandlerGC) tTileEntity).getMaxEnergyStoredGC(eSource) - tStored) { + float tReceived = ((IEnergyHandlerGC) tTileEntity).receiveEnergyGC(eSource, tSizeToReceive, false); + if (tReceived > 0) { + tSizeToReceive -= tReceived; + while (tSizeToReceive > 0) { + tReceived = ((IEnergyHandlerGC) tTileEntity) + .receiveEnergyGC(eSource, tSizeToReceive, false); + if (tReceived < 1) break; + tSizeToReceive -= tReceived; + } + return 1; + } + } + } + return 0; + } + return 2; + } + + public static boolean canConnect(TileEntity tTileEntity, ForgeDirection tDirection) { + // GC Compat + return GalacticraftCore.isModLoaded() && tTileEntity instanceof IEnergyHandlerGC + && (!(tTileEntity instanceof IConnector) + || ((IConnector) tTileEntity).canConnect(tDirection, NetworkType.POWER)); + } +} diff --git a/src/main/java/gregtech/api/util/GT_HatchElementBuilder.java b/src/main/java/gregtech/api/util/GT_HatchElementBuilder.java new file mode 100644 index 0000000000..2087ad755c --- /dev/null +++ b/src/main/java/gregtech/api/util/GT_HatchElementBuilder.java @@ -0,0 +1,524 @@ +package gregtech.api.util; + +import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlock; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.EnumSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.function.BiFunction; +import java.util.function.BiPredicate; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +import net.minecraft.block.Block; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.item.ItemStack; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.ChatComponentTranslation; +import net.minecraft.util.IChatComponent; +import net.minecraft.world.World; +import net.minecraftforge.common.util.ForgeDirection; + +import com.gtnewhorizon.structurelib.StructureLibAPI; +import com.gtnewhorizon.structurelib.structure.AutoPlaceEnvironment; +import com.gtnewhorizon.structurelib.structure.IItemSource; +import com.gtnewhorizon.structurelib.structure.IStructureElement; +import com.gtnewhorizon.structurelib.structure.IStructureElementChain; +import com.gtnewhorizon.structurelib.structure.IStructureElementNoPlacement; +import com.gtnewhorizon.structurelib.structure.StructureUtility; +import com.gtnewhorizon.structurelib.util.ItemStackPredicate; + +import gnu.trove.TIntCollection; +import gnu.trove.list.array.TIntArrayList; +import gnu.trove.set.hash.TIntHashSet; +import gregtech.api.interfaces.IHatchElement; +import gregtech.api.interfaces.metatileentity.IMetaTileEntity; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.common.blocks.GT_Item_Machines; + +public class GT_HatchElementBuilder<T> { + + private interface Builtin { + } + + private IGT_HatchAdder<? super T> mAdder; + private int mCasingIndex = -1; + private int mDot = -1; + private BiPredicate<? super T, ? super IGregTechTileEntity> mShouldSkip; + private BiFunction<? super T, ItemStack, ? extends Predicate<ItemStack>> mHatchItemFilter; + private Supplier<String> mHatchItemType; + private Predicate<? super T> mReject; + private boolean mCacheHint; + private boolean mNoStop; + private EnumSet<ForgeDirection> mDisallowedDirection = EnumSet.noneOf(ForgeDirection.class); + + private GT_HatchElementBuilder() {} + + public static <T> GT_HatchElementBuilder<T> builder() { + return new GT_HatchElementBuilder<>(); + } + + // region composite + + /** + * Set all of adder, hint and hatchItemFilter. Provide a reasonable default for shouldSkip. TODO add doc + */ + @SafeVarargs + public final GT_HatchElementBuilder<T> anyOf(IHatchElement<? super T>... elements) { + if (elements == null || elements.length == 0) throw new IllegalArgumentException(); + return adder( + Arrays.stream(elements) + .map( + e -> e.adder() + .rebrand()) + .reduce(IGT_HatchAdder::orElse) + .get()).hatchClasses( + Arrays.stream(elements) + .map(IHatchElement::mteClasses) + .flatMap(Collection::stream) + .collect(Collectors.toList())) + .cacheHint( + () -> Arrays.stream(elements) + .map(IHatchElement::name) + .sorted() + .collect(Collectors.joining(" or ", "of type ", ""))); + } + + /** + * Set all of adder, hint and hatchItemFilter. Provide a reasonable default for shouldSkip. + * <p> + * Will rotate through all elements TODO add doc + */ + @SafeVarargs + public final GT_HatchElementBuilder<T> atLeast(IHatchElement<? super T>... elements) { + if (elements == null || elements.length == 0) throw new IllegalArgumentException(); + return atLeast( + Arrays.stream(elements) + .collect(Collectors.groupingBy(Function.identity(), LinkedHashMap::new, Collectors.counting()))); + } + + /** + * Set all of adder, hint and hatchItemFilter. Provide a reasonable default for shouldSkip. + * <p> + * Will rotate through all elements TODO add doc + */ + public final GT_HatchElementBuilder<T> atLeastList(List<IHatchElement<? super T>> elements) { + if (elements == null || elements.isEmpty()) throw new IllegalArgumentException(); + return atLeast( + elements.stream() + .collect(Collectors.groupingBy(Function.identity(), LinkedHashMap::new, Collectors.counting()))); + } + + /** + * Set all of adder, hint and hatchItemFilter. Provide a reasonable default for shouldSkip. TODO add doc + */ + public final GT_HatchElementBuilder<T> atLeast(Map<IHatchElement<? super T>, ? extends Number> elements) { + if (elements == null || elements.isEmpty() || elements.containsKey(null) || elements.containsValue(null)) + throw new IllegalArgumentException(); + List<Class<? extends IMetaTileEntity>> list = elements.keySet() + .stream() + .map(IHatchElement::mteClasses) + .flatMap(Collection::stream) + .collect(Collectors.toList()); + // map cannot be null or empty, so assert Optional isPresent + return adder( + elements.keySet() + .stream() + .map( + e -> e.adder() + .rebrand()) + .reduce(IGT_HatchAdder::orElse) + .orElseThrow(AssertionError::new)) + .hatchItemFilter( + obj -> GT_StructureUtility.filterByMTEClass( + elements.entrySet() + .stream() + .filter( + entry -> entry.getKey() + .count(obj) + < entry.getValue() + .longValue()) + .flatMap( + entry -> entry.getKey() + .mteClasses() + .stream()) + .collect(Collectors.toList()))) + .shouldReject( + obj -> elements.entrySet() + .stream() + .allMatch( + e -> e.getKey() + .count(obj) + >= e.getValue() + .longValue())) + .shouldSkip( + (BiPredicate<? super T, ? super IGregTechTileEntity> & Builtin) (c, + t) -> t != null && list.stream() + .anyMatch(clazz -> clazz.isInstance(t.getMetaTileEntity()))) + .cacheHint( + () -> elements.keySet() + .stream() + .map(IHatchElement::name) + .sorted() + .collect(Collectors.joining(" or ", "of type ", ""))); + } + // endregion + + // region primitives + + public GT_HatchElementBuilder<T> adder(IGT_HatchAdder<? super T> aAdder) { + if (aAdder == null) throw new IllegalArgumentException(); + mAdder = aAdder; + return this; + } + + public GT_HatchElementBuilder<T> casingIndex(int aCasingIndex) { + if (aCasingIndex <= 0) throw new IllegalArgumentException(); + mCasingIndex = aCasingIndex; + return this; + } + + public GT_HatchElementBuilder<T> dot(int aDot) { + if (aDot <= 0) throw new IllegalArgumentException(); + mDot = aDot; + return this; + } + + public GT_HatchElementBuilder<T> shouldSkip(BiPredicate<? super T, ? super IGregTechTileEntity> aShouldSkip) { + if (!(aShouldSkip instanceof Builtin) || mShouldSkip != null) { + if (!(mShouldSkip instanceof Builtin) && mShouldSkip != null) throw new IllegalStateException(); + if (aShouldSkip == null) throw new IllegalArgumentException(); + } + mShouldSkip = aShouldSkip; + return this; + } + + public GT_HatchElementBuilder<T> shouldReject(Predicate<? super T> aShouldReject) { + if (aShouldReject == null) throw new IllegalArgumentException(); + mReject = aShouldReject; + return this; + } + + public GT_HatchElementBuilder<T> hatchItemFilter( + Function<? super T, ? extends Predicate<ItemStack>> aHatchItemFilter) { + if (aHatchItemFilter == null) throw new IllegalArgumentException(); + mHatchItemFilter = (t, s) -> aHatchItemFilter.apply(t); + return this; + } + + public GT_HatchElementBuilder<T> hatchItemFilterAnd( + Function<? super T, ? extends Predicate<ItemStack>> aHatchItemFilter) { + if (aHatchItemFilter == null) throw new IllegalArgumentException(); + BiFunction<? super T, ItemStack, ? extends Predicate<ItemStack>> tOldFilter = mHatchItemFilter; + mHatchItemFilter = (t, s) -> tOldFilter.apply(t, s) + .and(aHatchItemFilter.apply(t)); + return this; + } + + public GT_HatchElementBuilder<T> hatchItemFilter( + BiFunction<? super T, ItemStack, ? extends Predicate<ItemStack>> aHatchItemFilter) { + if (aHatchItemFilter == null) throw new IllegalArgumentException(); + mHatchItemFilter = aHatchItemFilter; + return this; + } + + public GT_HatchElementBuilder<T> hatchItemFilterAnd( + BiFunction<? super T, ItemStack, ? extends Predicate<ItemStack>> aHatchItemFilter) { + if (aHatchItemFilter == null) throw new IllegalArgumentException(); + BiFunction<? super T, ItemStack, ? extends Predicate<ItemStack>> tOldFilter = mHatchItemFilter; + mHatchItemFilter = (t, s) -> tOldFilter.apply(t, s) + .and(aHatchItemFilter.apply(t, s)); + return this; + } + + // region hint + public GT_HatchElementBuilder<T> hint(Supplier<String> aSupplier) { + if (aSupplier == null) throw new IllegalArgumentException(); + mHatchItemType = aSupplier; + mCacheHint = false; + return this; + } + + public GT_HatchElementBuilder<T> cacheHint(Supplier<String> aSupplier) { + if (aSupplier == null) throw new IllegalArgumentException(); + mHatchItemType = aSupplier; + mCacheHint = true; + return this; + } + + public GT_HatchElementBuilder<T> cacheHint() { + if (mHatchItemType == null) throw new IllegalStateException(); + mCacheHint = true; + return this; + } + // endregion + + public GT_HatchElementBuilder<T> continueIfSuccess() { + mNoStop = true; + return this; + } + + public GT_HatchElementBuilder<T> stopIfSuccess() { + mNoStop = false; + return this; + } + + /** + * Help automatic hatch side determination code by ruling out some directions. Note the automatic hatch side + * determination code will choose to use the default facing if the final allowed facing set is empty. + * <p> + * This will clear the sides set by previous call to this or {@link #allowOnly(ForgeDirection...)} + * <p> + * Usually mandatory for multis with multiple slices, and otherwise not needed if it contains a single slice only. + * + * @param facings disallowed direction in ABC coordinate system + */ + public GT_HatchElementBuilder<T> disallowOnly(ForgeDirection... facings) { + if (facings == null) throw new IllegalArgumentException(); + mDisallowedDirection = EnumSet.copyOf(Arrays.asList(facings)); + return this; + } + + /** + * Help automatic hatch side determination code by allowing only some directions. Note the automatic hatch side + * determination code will choose to use the default facing if the final allowed facing set is empty. + * <p> + * This will clear the sides set by previous call to this or {@link #disallowOnly(ForgeDirection...)} + * <p> + * Usually mandatory for multis with multiple slices, and otherwise not needed if it contains a single slice only. + * + * @param facings allowed direction in ABC coordinate system + */ + public GT_HatchElementBuilder<T> allowOnly(ForgeDirection... facings) { + if (facings == null) throw new IllegalArgumentException(); + mDisallowedDirection = EnumSet.complementOf(EnumSet.copyOf(Arrays.asList(facings))); + mDisallowedDirection.remove(ForgeDirection.UNKNOWN); + return this; + } + // endregion + + // region intermediate + public GT_HatchElementBuilder<T> hatchClass(Class<? extends IMetaTileEntity> clazz) { + return hatchItemFilter(c -> is -> clazz.isInstance(GT_Item_Machines.getMetaTileEntity(is))) + .cacheHint(() -> "of class " + clazz.getSimpleName()) + .shouldSkip( + (BiPredicate<? super T, ? super IGregTechTileEntity> & Builtin) (c, t) -> clazz + .isInstance(t.getMetaTileEntity())); + } + + @SafeVarargs + public final GT_HatchElementBuilder<T> hatchClasses(Class<? extends IMetaTileEntity>... classes) { + return hatchClasses(Arrays.asList(classes)); + } + + public final GT_HatchElementBuilder<T> hatchClasses(List<? extends Class<? extends IMetaTileEntity>> classes) { + List<? extends Class<? extends IMetaTileEntity>> list = new ArrayList<>(classes); + return hatchItemFilter(obj -> GT_StructureUtility.filterByMTEClass(list)).cacheHint( + () -> list.stream() + .map(Class::getSimpleName) + .sorted() + .collect(Collectors.joining(" or ", "of class ", ""))) + .shouldSkip( + (BiPredicate<? super T, ? super IGregTechTileEntity> & Builtin) (c, t) -> t != null && list.stream() + .anyMatch(clazz -> clazz.isInstance(t.getMetaTileEntity()))); + } + + public GT_HatchElementBuilder<T> hatchId(int aId) { + return hatchItemFilter( + c -> is -> GT_Utility.isStackValid(is) && is.getItem() instanceof GT_Item_Machines + && is.getItemDamage() == aId).cacheHint(() -> "of id " + aId) + .shouldSkip( + (BiPredicate<? super T, ? super IGregTechTileEntity> & Builtin) (c, t) -> t != null + && t.getMetaTileID() == aId); + } + + public GT_HatchElementBuilder<T> hatchIds(int... aIds) { + if (aIds == null || aIds.length == 0) throw new IllegalArgumentException(); + if (aIds.length == 1) return hatchId(aIds[0]); + TIntCollection coll = aIds.length < 16 ? new TIntArrayList(aIds) : new TIntHashSet(aIds); + return hatchItemFilter( + c -> is -> GT_Utility.isStackValid(is) && is.getItem() instanceof GT_Item_Machines + && coll.contains(is.getItemDamage())).cacheHint( + () -> Arrays.stream(coll.toArray()) + .sorted() + .mapToObj(String::valueOf) + .collect(Collectors.joining(" or ", "of id ", ""))) + .shouldSkip( + (BiPredicate<? super T, ? super IGregTechTileEntity> & Builtin) (c, t) -> t != null + && coll.contains(t.getMetaTileID())); + } + + // endregion + + @SuppressWarnings("unchecked") + @SafeVarargs + public final IStructureElementChain<T> buildAndChain(IStructureElement<T>... elements) { + List<IStructureElement<T>> l = new ArrayList<>(); + l.add(build()); + l.addAll(Arrays.asList(elements)); + IStructureElement<T>[] array = l.toArray(new IStructureElement[0]); + return () -> array; + } + + public final IStructureElementChain<T> buildAndChain(Block block, int meta) { + return buildAndChain(ofBlock(block, meta)); + } + + public IStructureElement<T> build() { + if (mAdder == null || mCasingIndex == -1 || mDot == -1) { + throw new IllegalArgumentException(); + } + if (mHatchItemFilter == null) { + // no item filter -> no placement + return new IStructureElementNoPlacement<>() { + + @Override + public boolean check(T t, World world, int x, int y, int z) { + TileEntity tileEntity = world.getTileEntity(x, y, z); + return tileEntity instanceof IGregTechTileEntity + && mAdder.apply(t, (IGregTechTileEntity) tileEntity, (short) mCasingIndex); + } + + @Override + public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { + StructureLibAPI.hintParticle(world, x, y, z, StructureLibAPI.getBlockHint(), mDot - 1); + return true; + } + }; + } + return new IStructureElement<>() { + + private String mHint = mHatchItemType == null ? "unspecified GT hatch" : mHatchItemType.get(); + + @Override + public boolean check(T t, World world, int x, int y, int z) { + TileEntity tileEntity = world.getTileEntity(x, y, z); + return tileEntity instanceof IGregTechTileEntity + && mAdder.apply(t, (IGregTechTileEntity) tileEntity, (short) mCasingIndex); + } + + @Override + public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { + StructureLibAPI.hintParticle(world, x, y, z, StructureLibAPI.getBlockHint(), mDot - 1); + return true; + } + + @Override + public boolean placeBlock(T t, World world, int i, int i1, int i2, ItemStack itemStack) { + // TODO + return false; + } + + private String getHint() { + if (mHint != null) return mHint; + String tHint = mHatchItemType.get(); + if (tHint == null) return "?"; + // TODO move this to some .lang instead of half ass it into the crappy gt lang file + tHint = GT_LanguageManager.addStringLocalization("Hatch_Type_" + tHint.replace(' ', '_'), tHint); + if (mCacheHint) { + mHint = tHint; + if (mHint != null) + // yeet the getter, since its product is retrieved and cached + mHatchItemType = null; + } + return tHint; + } + + @Override + public BlocksToPlace getBlocksToPlace(T t, World world, int x, int y, int z, ItemStack trigger, + AutoPlaceEnvironment env) { + return BlocksToPlace.create(mHatchItemFilter.apply(t, trigger)); + } + + @Deprecated + @Override + public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger, + IItemSource s, EntityPlayerMP actor, Consumer<IChatComponent> chatter) { + return survivalPlaceBlock( + t, + world, + x, + y, + z, + trigger, + AutoPlaceEnvironment.fromLegacy(s, actor, chatter)); + } + + @Override + public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger, + AutoPlaceEnvironment env) { + if (mShouldSkip != null) { + TileEntity tileEntity = world.getTileEntity(x, y, z); + if (tileEntity instanceof IGregTechTileEntity + && mShouldSkip.test(t, (IGregTechTileEntity) tileEntity)) return PlaceResult.SKIP; + } + if (!StructureLibAPI.isBlockTriviallyReplaceable(world, x, y, z, env.getActor())) + return PlaceResult.REJECT; + if (mReject != null && mReject.test(t)) return PlaceResult.REJECT; + ItemStack taken = env.getSource() + .takeOne(mHatchItemFilter.apply(t, trigger), true); + if (GT_Utility.isStackInvalid(taken)) { + String type = getHint(); + env.getChatter() + .accept(new ChatComponentTranslation("GT5U.autoplace.error.no_hatch", type)); + return PlaceResult.REJECT; + } + if (StructureUtility.survivalPlaceBlock( + taken, + ItemStackPredicate.NBTMode.IGNORE, + null, + true, + world, + x, + y, + z, + env.getSource(), + env.getActor()) != PlaceResult.ACCEPT) { + return PlaceResult.REJECT; + } + // try to infer facing + EnumSet<ForgeDirection> allowed = EnumSet.noneOf(ForgeDirection.class); + // first find which face of block is not contained in structure + if (env.getAPILevel() == AutoPlaceEnvironment.APILevel.Legacy) { + // a legacy decorator isn't passing down necessary information + // in that case, we just assume all facing is allowed + allowed.addAll(Arrays.asList(ForgeDirection.VALID_DIRECTIONS)); + } else { + for (ForgeDirection direction : ForgeDirection.VALID_DIRECTIONS) { + // as noted on getWorldDirection Y axis should be flipped before use + if (env.isContainedInPiece(direction.offsetX, -direction.offsetY, direction.offsetZ)) continue; + // explicitly rejected, probably obstructed by another slice + if (mDisallowedDirection.contains(direction)) continue; + ForgeDirection rotated = env.getFacing() + .getWorldDirection( + (direction.flag & (ForgeDirection.UP.flag | ForgeDirection.DOWN.flag)) != 0 + ? direction.getOpposite() + : direction); + allowed.add(rotated); + } + } + if (!allowed.isEmpty()) { + TileEntity tileEntity = world.getTileEntity(x, y, z); + if (tileEntity instanceof IGregTechTileEntity) { + ForgeDirection result = null; + // find the first facing available, but prefer a facing that isn't up/down + for (ForgeDirection facing : allowed) { + result = facing; + if ((facing.flag & (ForgeDirection.UP.flag | ForgeDirection.DOWN.flag)) == 0) break; // Horizontal + } + assert result != null; + ((IGregTechTileEntity) tileEntity).setFrontFacing(result); + } + } + return mNoStop ? PlaceResult.ACCEPT : PlaceResult.ACCEPT_STOP; + } + }; + } +} diff --git a/src/main/java/gregtech/api/util/GT_IBoxableWrapper.java b/src/main/java/gregtech/api/util/GT_IBoxableWrapper.java new file mode 100644 index 0000000000..796699c261 --- /dev/null +++ b/src/main/java/gregtech/api/util/GT_IBoxableWrapper.java @@ -0,0 +1,13 @@ +package gregtech.api.util; + +import net.minecraft.item.ItemStack; + +import ic2.api.item.IBoxable; + +public class GT_IBoxableWrapper implements IBoxable { + + @Override + public boolean canBeStoredInToolbox(ItemStack itemstack) { + return GT_Utility.isStackInList(itemstack, GT_ModHandler.sBoxableItems); + } +} diff --git a/src/main/java/gregtech/api/util/GT_ItsNotMyFaultException.java b/src/main/java/gregtech/api/util/GT_ItsNotMyFaultException.java new file mode 100644 index 0000000000..f47a356d7b --- /dev/null +++ b/src/main/java/gregtech/api/util/GT_ItsNotMyFaultException.java @@ -0,0 +1,18 @@ +package gregtech.api.util; + +public class GT_ItsNotMyFaultException extends RuntimeException { + + private static final long serialVersionUID = -8752778866486460495L; + + private final String mError; + + public GT_ItsNotMyFaultException(String aError) { + mError = aError; + } + + @Override + public String toString() { + return "The GregTech-Addon has a Problem.\nIT'S NOT MY FAULT!!! Below is how to fix it.\n" + mError + + "\nDO NOT COME TO ME WITH THIS CRASH. YOU CAUSED IT YOURSELF, AND I TOLD YOU HOW TO FIX IT!!!"; + } +} diff --git a/src/main/java/gregtech/api/util/GT_JubilanceMegaApiary.java b/src/main/java/gregtech/api/util/GT_JubilanceMegaApiary.java new file mode 100644 index 0000000000..f20a58c34a --- /dev/null +++ b/src/main/java/gregtech/api/util/GT_JubilanceMegaApiary.java @@ -0,0 +1,23 @@ +package gregtech.api.util; + +import forestry.api.apiculture.IAlleleBeeSpecies; +import forestry.api.apiculture.IBeeGenome; +import forestry.api.apiculture.IBeeHousing; +import forestry.api.apiculture.IJubilanceProvider; + +public class GT_JubilanceMegaApiary implements IJubilanceProvider { + + public static final GT_JubilanceMegaApiary instance = new GT_JubilanceMegaApiary(); + + protected GT_JubilanceMegaApiary() {} + + @Override + public boolean isJubilant(IAlleleBeeSpecies species, IBeeGenome genome, IBeeHousing housing) { + return false; + } + + @Override + public String getDescription() { + return "Will only be produced in mega Apiary"; + } +} diff --git a/src/main/java/gregtech/api/util/GT_LanguageManager.java b/src/main/java/gregtech/api/util/GT_LanguageManager.java new file mode 100644 index 0000000000..b1bd45476a --- /dev/null +++ b/src/main/java/gregtech/api/util/GT_LanguageManager.java @@ -0,0 +1,600 @@ +package gregtech.api.util; + +import static gregtech.api.enums.GT_Values.E; + +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.StatCollector; +import net.minecraftforge.common.config.Configuration; +import net.minecraftforge.common.config.Property; + +import cpw.mods.fml.common.registry.LanguageRegistry; +import cpw.mods.fml.relauncher.ReflectionHelper; +import gregtech.api.GregTech_API; + +public class GT_LanguageManager { + + /** + * Buffer to reduce memory allocation when injecting data to LanguageRegistry. + */ + private static final HashMap<String, String> TEMPMAP = new HashMap<>(); + /** + * Buffer used when something is trying to add new lang entry while config file is not set up yet. + */ + public static final Map<String, String> BUFFERMAP = new HashMap<>(); + /** + * Map containing all the translation data coming into this class. + */ + private static final Map<String, String> LANGMAP = new HashMap<>(); + /** + * Config file handler bound to GregTech.lang or GregTech_(locale_name).lang. Even though it says English file, + * it's not necessarily English, but on system it's always treated as English (as in, "default" language.) + */ + public static Configuration sEnglishFile; + /** + * If the game is running with en_US language. This does not get updated when user changes language in game; + * GT lang system cannot handle that anyway. + */ + public static boolean isEN_US; + /** + * If placeholder like %material should be used for writing lang entries to file. + */ + public static boolean i18nPlaceholder = true; + /** + * If there's any lang entry that is not found on lang file and waiting to be written. + */ + private static boolean hasUnsavedEntry = false; + + // TODO: convert to enum + public static String FACE_ANY = "gt.lang.face.any", FACE_BOTTOM = "gt.lang.face.bottom", + FACE_TOP = "gt.lang.face.top", FACE_LEFT = "gt.lang.face.left", FACE_FRONT = "gt.lang.face.front", + FACE_RIGHT = "gt.lang.face.right", FACE_BACK = "gt.lang.face.back", FACE_NONE = "gt.lang.face.none"; + + public static String[] FACES = { FACE_BOTTOM, FACE_TOP, FACE_LEFT, FACE_FRONT, FACE_RIGHT, FACE_BACK, FACE_NONE }; + + /** + * Map referencing private field of StringTranslate, used by StatCollector. Used to inject lang entries there. + */ + private static final Map<String, String> stringTranslateLanguageList; + + static { + try { + Field fieldStringTranslateLanguageList = ReflectionHelper + .findField(net.minecraft.util.StringTranslate.class, "languageList", "field_74816_c"); + Field fieldStringTranslateInstance = ReflectionHelper + .findField(net.minecraft.util.StringTranslate.class, "instance", "field_74817_a"); + // noinspection unchecked + stringTranslateLanguageList = (Map<String, String>) fieldStringTranslateLanguageList + .get(fieldStringTranslateInstance.get(null)); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * @deprecated Parameter aWriteIntoLangFile is no longer used, + * use {@link #addStringLocalization(String, String)} or consider migrating to MC lang system instead. + */ + @Deprecated + public static synchronized String addStringLocalization(String aKey, String aEnglish, boolean aWriteIntoLangFile) { + return addStringLocalization(aKey, aEnglish); + } + + /** + * If you newly use this method, please consider using MC lang system instead. + */ + public static synchronized String addStringLocalization(String aKey, String aEnglish) { + String trimmedKey = aKey != null ? aKey.trim() : ""; + if (trimmedKey.isEmpty()) return E; // RIP cascading class loading, don't use GT_Utility here + if (sEnglishFile == null) { + // Lang file is not set up yet + BUFFERMAP.put(trimmedKey, aEnglish); + return aEnglish; + } + if (!BUFFERMAP.isEmpty()) { + // Lang file is now set up, resolve all the buffers + // This won't be visited twice + for (Entry<String, String> tEntry : BUFFERMAP.entrySet()) { + storeTranslation(tEntry.getKey(), tEntry.getValue()); + } + BUFFERMAP.clear(); + } + + if (!LANGMAP.containsKey(trimmedKey)) { + return storeTranslation(trimmedKey, aEnglish); + } + return LANGMAP.get(trimmedKey); + } + + private static synchronized String storeTranslation(String trimmedKey, String english) { + String translation = writeToLangFile(trimmedKey, english); + LANGMAP.put(trimmedKey, translation); + addToMCLangList(trimmedKey, translation); + TEMPMAP.put(trimmedKey, translation); + LanguageRegistry.instance() + // If we use the actual user configured locale here, switching lang to others while running game + // turns everything into unlocalized string. So we make it "default" and call it a day. + .injectLanguage("en_US", TEMPMAP); + TEMPMAP.clear(); + return translation; + } + + private static synchronized String writeToLangFile(String trimmedKey, String aEnglish) { + Property tProperty = sEnglishFile.get("LanguageFile", trimmedKey, aEnglish); + if (hasUnsavedEntry && GregTech_API.sPostloadFinished) { + sEnglishFile.save(); + hasUnsavedEntry = false; + } + String translation = tProperty.getString(); + if (tProperty.wasRead()) { + if (isEN_US && !aEnglish.equals(translation)) { + tProperty.set(aEnglish); + markFileDirty(); + return aEnglish; + } + } else { + markFileDirty(); + } + return translation; + } + + private static synchronized void markFileDirty() { + if (GregTech_API.sPostloadFinished) { + sEnglishFile.save(); + } else { + hasUnsavedEntry = true; + } + } + + public static String getTranslation(String aKey) { + String tTrimmedKey = aKey != null ? aKey.trim() : ""; + if (tTrimmedKey.isEmpty()) return E; + + if (StatCollector.canTranslate(tTrimmedKey)) { + return StatCollector.translateToLocal(tTrimmedKey); + } + String anotherKeyToTry; + if (tTrimmedKey.endsWith(".name")) { + anotherKeyToTry = tTrimmedKey.substring(0, tTrimmedKey.length() - 5); + } else { + anotherKeyToTry = tTrimmedKey + ".name"; + } + if (StatCollector.canTranslate(anotherKeyToTry)) { + return StatCollector.translateToLocal(anotherKeyToTry); + } + return tTrimmedKey; + } + + public static String getTranslation(String aKey, String aSeperator) { + if (aKey == null) return E; + String rTranslation = E; + StringBuilder rTranslationSB = new StringBuilder(rTranslation); + for (String tString : aKey.split(aSeperator)) { + rTranslationSB.append(getTranslation(tString)); + } + rTranslation = String.valueOf(rTranslationSB); + return rTranslation; + } + + @SuppressWarnings("unused") + public static String getTranslateableItemStackName(ItemStack aStack) { + if (GT_Utility.isStackInvalid(aStack)) return "null"; + NBTTagCompound tNBT = aStack.getTagCompound(); + if (tNBT != null && tNBT.hasKey("display")) { + String tName = tNBT.getCompoundTag("display") + .getString("Name"); + if (GT_Utility.isStringValid(tName)) { + return tName; + } + } + return aStack.getUnlocalizedName() + ".name"; + } + + public static void writePlaceholderStrings() { + addStringLocalization("Interaction_DESCRIPTION_Index_001", "Puts out into adjacent Slot #"); + addStringLocalization("Interaction_DESCRIPTION_Index_002", "Grabs in for own Slot #"); + addStringLocalization("Interaction_DESCRIPTION_Index_003", "Enable with Signal"); + addStringLocalization("Interaction_DESCRIPTION_Index_004", "Disable with Signal"); + addStringLocalization("Interaction_DESCRIPTION_Index_005", "Disabled"); + addStringLocalization("Interaction_DESCRIPTION_Index_006", "Export"); + addStringLocalization("Interaction_DESCRIPTION_Index_007", "Import"); + addStringLocalization("Interaction_DESCRIPTION_Index_008", "Export (conditional)"); + addStringLocalization("Interaction_DESCRIPTION_Index_009", "Import (conditional)"); + addStringLocalization("Interaction_DESCRIPTION_Index_010", "Export (invert cond)"); + addStringLocalization("Interaction_DESCRIPTION_Index_011", "Import (invert cond)"); + addStringLocalization("Interaction_DESCRIPTION_Index_012", "Export allow Input"); + addStringLocalization("Interaction_DESCRIPTION_Index_013", "Import allow Output"); + addStringLocalization("Interaction_DESCRIPTION_Index_014", "Export allow Input (conditional)"); + addStringLocalization("Interaction_DESCRIPTION_Index_015", "Import allow Output (conditional)"); + addStringLocalization("Interaction_DESCRIPTION_Index_016", "Export allow Input (invert cond)"); + addStringLocalization("Interaction_DESCRIPTION_Index_017", "Import allow Output (invert cond)"); + addStringLocalization("Interaction_DESCRIPTION_Index_018", "Normal"); + addStringLocalization("Interaction_DESCRIPTION_Index_019", "Inverted"); + addStringLocalization("Interaction_DESCRIPTION_Index_020", "Ready to work"); + addStringLocalization("Interaction_DESCRIPTION_Index_021", "Not ready to work"); + addStringLocalization("Interaction_DESCRIPTION_Index_022", "Import"); + addStringLocalization("Interaction_DESCRIPTION_Index_023", "Import (conditional)"); + addStringLocalization("Interaction_DESCRIPTION_Index_024", "Import (invert cond)"); + addStringLocalization("Interaction_DESCRIPTION_Index_025", "Keep Liquids Away"); + addStringLocalization("Interaction_DESCRIPTION_Index_026", "Keep Liquids Away (conditional)"); + addStringLocalization("Interaction_DESCRIPTION_Index_027", "Keep Liquids Away (invert cond)"); + addStringLocalization("Interaction_DESCRIPTION_Index_031", "Normal Universal Storage"); + addStringLocalization("Interaction_DESCRIPTION_Index_032", "Inverted Universal Storage"); + addStringLocalization("Interaction_DESCRIPTION_Index_033", "Normal Electricity Storage"); + addStringLocalization("Interaction_DESCRIPTION_Index_034", "Inverted Electricity Storage"); + addStringLocalization("Interaction_DESCRIPTION_Index_035", "Normal Steam Storage"); + addStringLocalization("Interaction_DESCRIPTION_Index_036", "Inverted Steam Storage"); + addStringLocalization("Interaction_DESCRIPTION_Index_037", "Normal Average Electric Input"); + addStringLocalization("Interaction_DESCRIPTION_Index_038", "Inverted Average Electric Input"); + addStringLocalization("Interaction_DESCRIPTION_Index_039", "Normal Average Electric Output"); + addStringLocalization("Interaction_DESCRIPTION_Index_040", "Inverted Average Electric Output"); + addStringLocalization("Interaction_DESCRIPTION_Index_041", "Normal Electricity Storage(Including Batteries)"); + addStringLocalization("Interaction_DESCRIPTION_Index_042", "Inverted Electricity Storage(Including Batteries)"); + addStringLocalization("Interaction_DESCRIPTION_Index_043", "Filter input, Deny output"); + addStringLocalization("Interaction_DESCRIPTION_Index_044", "Invert input, Deny output"); + addStringLocalization("Interaction_DESCRIPTION_Index_045", "Filter input, Permit any output"); + addStringLocalization("Interaction_DESCRIPTION_Index_046", "Invert input, Permit any output"); + addStringLocalization("Interaction_DESCRIPTION_Index_047", "Filter Fluid: "); + addStringLocalization("Interaction_DESCRIPTION_Index_048", "Pump speed: "); + addStringLocalization("Interaction_DESCRIPTION_Index_049", "L/tick "); + addStringLocalization("Interaction_DESCRIPTION_Index_050", "L/sec"); + addStringLocalization("Interaction_DESCRIPTION_Index_053", "Slot: "); + addStringLocalization("Interaction_DESCRIPTION_Index_054", "Inverted"); + addStringLocalization("Interaction_DESCRIPTION_Index_055", "Normal"); + addStringLocalization("Interaction_DESCRIPTION_Index_056", "Emit if 1 Maintenance Needed"); + addStringLocalization("Interaction_DESCRIPTION_Index_057", "Emit if 1 Maintenance Needed(inverted)"); + addStringLocalization("Interaction_DESCRIPTION_Index_058", "Emit if 2 Maintenance Needed"); + addStringLocalization("Interaction_DESCRIPTION_Index_059", "Emit if 2 Maintenance Needed(inverted)"); + addStringLocalization("Interaction_DESCRIPTION_Index_060", "Emit if 3 Maintenance Needed"); + addStringLocalization("Interaction_DESCRIPTION_Index_061", "Emit if 3 Maintenance Needed(inverted)"); + addStringLocalization("Interaction_DESCRIPTION_Index_062", "Emit if 4 Maintenance Needed"); + addStringLocalization("Interaction_DESCRIPTION_Index_063", "Emit if 4 Maintenance Needed(inverted)"); + addStringLocalization("Interaction_DESCRIPTION_Index_064", "Emit if 5 Maintenance Needed"); + addStringLocalization("Interaction_DESCRIPTION_Index_065", "Emit if 5 Maintenance Needed(inverted)"); + addStringLocalization("Interaction_DESCRIPTION_Index_066", "Emit if rotor needs maintenance low accuracy mod"); + addStringLocalization( + "Interaction_DESCRIPTION_Index_067", + "Emit if rotor needs maintenance low accuracy mod(inverted)"); + addStringLocalization("Interaction_DESCRIPTION_Index_068", "Emit if rotor needs maintenance high accuracy mod"); + addStringLocalization("Interaction_DESCRIPTION_Index_068.1", "Emit if any Player is close"); + addStringLocalization( + "Interaction_DESCRIPTION_Index_069", + "Emit if rotor needs maintenance high accuracy mod(inverted)"); + addStringLocalization("Interaction_DESCRIPTION_Index_069.1", "Emit if other Player is close"); + addStringLocalization("Interaction_DESCRIPTION_Index_070", "Emit if you are close"); + addStringLocalization("Interaction_DESCRIPTION_Index_071", "Conducts strongest Input"); + addStringLocalization("Interaction_DESCRIPTION_Index_072", "Conducts from bottom Input"); + addStringLocalization("Interaction_DESCRIPTION_Index_073", "Conducts from top Input"); + addStringLocalization("Interaction_DESCRIPTION_Index_074", "Conducts from north Input"); + addStringLocalization("Interaction_DESCRIPTION_Index_075", "Conducts from south Input"); + addStringLocalization("Interaction_DESCRIPTION_Index_076", "Conducts from west Input"); + addStringLocalization("Interaction_DESCRIPTION_Index_077", "Conducts from east Input"); + addStringLocalization("Interaction_DESCRIPTION_Index_078", "Signal = "); + addStringLocalization("Interaction_DESCRIPTION_Index_079", "Conditional Signal = "); + addStringLocalization("Interaction_DESCRIPTION_Index_080", "Inverted Conditional Signal = "); + addStringLocalization("Interaction_DESCRIPTION_Index_081", "Frequency: "); + addStringLocalization("Interaction_DESCRIPTION_Index_082", "Open if work enabled"); + addStringLocalization("Interaction_DESCRIPTION_Index_083", "Open if work disabled"); + addStringLocalization("Interaction_DESCRIPTION_Index_084", "Only Output allowed"); + addStringLocalization("Interaction_DESCRIPTION_Index_085", "Only Input allowed"); + addStringLocalization("Interaction_DESCRIPTION_Index_086", "Auto-Input: "); + addStringLocalization("Interaction_DESCRIPTION_Index_087", "Disabled"); + addStringLocalization("Interaction_DESCRIPTION_Index_088", "Enabled"); + addStringLocalization("Interaction_DESCRIPTION_Index_089", " Auto-Output: "); + addStringLocalization("Interaction_DESCRIPTION_Index_090", "Machine Processing: "); + addStringLocalization("Interaction_DESCRIPTION_Index_091", "Redstone Output at Side "); + addStringLocalization("Interaction_DESCRIPTION_Index_092", " set to: "); + addStringLocalization("Interaction_DESCRIPTION_Index_093", "Strong"); + addStringLocalization("Interaction_DESCRIPTION_Index_094", "Weak"); + addStringLocalization("Interaction_DESCRIPTION_Index_094.1", "Not enough soldering material!"); + addStringLocalization("Interaction_DESCRIPTION_Index_095", "Input from Output Side allowed"); + addStringLocalization("Interaction_DESCRIPTION_Index_096", "Input from Output Side forbidden"); + addStringLocalization("Interaction_DESCRIPTION_Index_098", "Do not regulate Item Stack Size"); + addStringLocalization("Interaction_DESCRIPTION_Index_099", "Regulate Item Stack Size to: "); + addStringLocalization("Interaction_DESCRIPTION_Index_100", "This is "); + addStringLocalization("Interaction_DESCRIPTION_Index_101", " Ore."); + addStringLocalization("Interaction_DESCRIPTION_Index_102", "There is Lava behind this Rock."); + addStringLocalization("Interaction_DESCRIPTION_Index_103", "There is a Liquid behind this Rock."); + addStringLocalization("Interaction_DESCRIPTION_Index_104", "There is an Air Pocket behind this Rock."); + addStringLocalization("Interaction_DESCRIPTION_Index_105", "Material is changing behind this Rock."); + addStringLocalization("Interaction_DESCRIPTION_Index_106", "Found traces of "); + addStringLocalization("Interaction_DESCRIPTION_Index_107", "No Ores found."); + addStringLocalization("Interaction_DESCRIPTION_Index_108", "Outputs misc. Fluids, Steam and Items"); + addStringLocalization("Interaction_DESCRIPTION_Index_109", "Outputs Steam and Items"); + addStringLocalization("Interaction_DESCRIPTION_Index_110", "Outputs Steam and misc. Fluids"); + addStringLocalization("Interaction_DESCRIPTION_Index_111", "Outputs Steam"); + addStringLocalization("Interaction_DESCRIPTION_Index_112", "Outputs misc. Fluids and Items"); + addStringLocalization("Interaction_DESCRIPTION_Index_113", "Outputs only Items"); + addStringLocalization("Interaction_DESCRIPTION_Index_114", "Outputs only misc. Fluids"); + addStringLocalization("Interaction_DESCRIPTION_Index_115", "Outputs nothing"); + // 116 moved to lang files + // 117 obsolete + // 118 moved to lang files + // 119 obsolete + // 120 moved to lang files + // 121 obsolete + addStringLocalization("Interaction_DESCRIPTION_Index_122", "Emit Redstone if slots contain something"); + addStringLocalization("Interaction_DESCRIPTION_Index_123", "Don't emit Redstone"); + // 124 moved to lang files + addStringLocalization("Interaction_DESCRIPTION_Index_124.1", "Blacklist Mode"); + // 125 obsolete + addStringLocalization("Interaction_DESCRIPTION_Index_125.1", "Whitelist Mode"); + // 126 moved to lang files + // 127 obsolete + addStringLocalization("Interaction_DESCRIPTION_Index_128", "Redstone"); + addStringLocalization("Interaction_DESCRIPTION_Index_128.1", "Redstone "); + addStringLocalization("Interaction_DESCRIPTION_Index_129", "Energy"); + addStringLocalization("Interaction_DESCRIPTION_Index_129.1", "Energy "); + addStringLocalization("Interaction_DESCRIPTION_Index_130", "Fluids"); + addStringLocalization("Interaction_DESCRIPTION_Index_130.1", "Fluids "); + addStringLocalization("Interaction_DESCRIPTION_Index_131", "Items"); + addStringLocalization("Interaction_DESCRIPTION_Index_131.1", "Items "); + addStringLocalization("Interaction_DESCRIPTION_Index_132", "Pipe is loose."); + addStringLocalization("Interaction_DESCRIPTION_Index_133", "Screws are loose."); + addStringLocalization("Interaction_DESCRIPTION_Index_134", "Something is stuck."); + addStringLocalization("Interaction_DESCRIPTION_Index_135", "Platings are dented."); + addStringLocalization("Interaction_DESCRIPTION_Index_136", "Circuitry burned out."); + addStringLocalization("Interaction_DESCRIPTION_Index_137", "That doesn't belong there."); + addStringLocalization("Interaction_DESCRIPTION_Index_138", "Incomplete Structure."); + addStringLocalization("Interaction_DESCRIPTION_Index_139", "Hit with Soft Mallet"); + addStringLocalization("Interaction_DESCRIPTION_Index_140", "to (re-)start the Machine"); + addStringLocalization("Interaction_DESCRIPTION_Index_141", "if it doesn't start."); + addStringLocalization("Interaction_DESCRIPTION_Index_142", "Running perfectly."); + addStringLocalization("Interaction_DESCRIPTION_Index_143", "Missing Mining Pipe"); + addStringLocalization("Interaction_DESCRIPTION_Index_144", "Missing Turbine Rotor"); + addStringLocalization("Interaction_DESCRIPTION_Index_145", "Step Down, In: "); + addStringLocalization("Interaction_DESCRIPTION_Index_146", "Step Up, In: "); + addStringLocalization("Interaction_DESCRIPTION_Index_147", "A, Out: "); + addStringLocalization("Interaction_DESCRIPTION_Index_148", "V "); + addStringLocalization("Interaction_DESCRIPTION_Index_149", "A"); + addStringLocalization("Interaction_DESCRIPTION_Index_150", "Chance: "); + addStringLocalization("Interaction_DESCRIPTION_Index_151", "Does not get consumed in the process"); + addStringLocalization("Interaction_DESCRIPTION_Index_151.1", "Outputs items and 1 specific Fluid"); + addStringLocalization("Interaction_DESCRIPTION_Index_151.2", "Outputs 1 specific Fluid"); + addStringLocalization("Interaction_DESCRIPTION_Index_151.4", "Successfully locked Fluid to %s"); + addStringLocalization("Interaction_DESCRIPTION_Index_152", "Total: "); + addStringLocalization("Interaction_DESCRIPTION_Index_153", "Usage: "); + addStringLocalization("Interaction_DESCRIPTION_Index_154", "Voltage: "); + addStringLocalization("Interaction_DESCRIPTION_Index_155", "Amperage: "); + addStringLocalization("Interaction_DESCRIPTION_Index_156", "Voltage: unspecified"); + addStringLocalization("Interaction_DESCRIPTION_Index_157", "Amperage: unspecified"); + addStringLocalization("Interaction_DESCRIPTION_Index_158", "Time: "); + addStringLocalization("Interaction_DESCRIPTION_Index_159", "Needs Low Gravity"); + addStringLocalization("Interaction_DESCRIPTION_Index_160", "Needs Cleanroom"); + addStringLocalization("Interaction_DESCRIPTION_Index_160.1", "Needs Cleanroom & LowGrav"); + addStringLocalization("Interaction_DESCRIPTION_Index_161", " secs"); + addStringLocalization("Interaction_DESCRIPTION_Index_162", "Name: "); + addStringLocalization("Interaction_DESCRIPTION_Index_163", " MetaData: "); + addStringLocalization("Interaction_DESCRIPTION_Index_164", "Hardness: "); + addStringLocalization("Interaction_DESCRIPTION_Index_165", " Blast Resistance: "); + addStringLocalization("Interaction_DESCRIPTION_Index_166", "Is valid Beacon Pyramid Material"); + addStringLocalization("Interaction_DESCRIPTION_Index_167", "Tank "); + addStringLocalization("Interaction_DESCRIPTION_Index_168", "Heat: "); + addStringLocalization("Interaction_DESCRIPTION_Index_169", "HEM: "); + addStringLocalization("Interaction_DESCRIPTION_Index_170", " Base EU Output: "); + addStringLocalization("Interaction_DESCRIPTION_Index_171", "Facing: "); + addStringLocalization("Interaction_DESCRIPTION_Index_172", " / Chance: "); + addStringLocalization("Interaction_DESCRIPTION_Index_173", "You can remove this with a Wrench"); + addStringLocalization("Interaction_DESCRIPTION_Index_174", "You can NOT remove this with a Wrench"); + addStringLocalization("Interaction_DESCRIPTION_Index_175", "Conduction Loss: "); + addStringLocalization("Interaction_DESCRIPTION_Index_176", "Contained Energy: "); + addStringLocalization("Interaction_DESCRIPTION_Index_177", "Has Muffler Upgrade"); + addStringLocalization("Interaction_DESCRIPTION_Index_178", "Progress/Load: "); + addStringLocalization("Interaction_DESCRIPTION_Index_179", "Max IN: "); + addStringLocalization("Interaction_DESCRIPTION_Index_181", "Max OUT: "); + addStringLocalization("Interaction_DESCRIPTION_Index_182", " EU at "); + addStringLocalization("Interaction_DESCRIPTION_Index_183", " A"); + addStringLocalization("Interaction_DESCRIPTION_Index_184", "Energy: "); + addStringLocalization("Interaction_DESCRIPTION_Index_186", "Owned by: "); + addStringLocalization("Interaction_DESCRIPTION_Index_187", "Type -- Crop-Name: "); + addStringLocalization("Interaction_DESCRIPTION_Index_188", " Growth: "); + addStringLocalization("Interaction_DESCRIPTION_Index_189", " Gain: "); + addStringLocalization("Interaction_DESCRIPTION_Index_190", " Resistance: "); + addStringLocalization("Interaction_DESCRIPTION_Index_191", "Plant -- Fertilizer: "); + addStringLocalization("Interaction_DESCRIPTION_Index_192", " Water: "); + addStringLocalization("Interaction_DESCRIPTION_Index_193", " Weed-Ex: "); + addStringLocalization("Interaction_DESCRIPTION_Index_194", " Scan-Level: "); + addStringLocalization("Interaction_DESCRIPTION_Index_195", "Environment -- Nutrients: "); + addStringLocalization("Interaction_DESCRIPTION_Index_196", " Humidity: "); + addStringLocalization("Interaction_DESCRIPTION_Index_197", " Air-Quality: "); + addStringLocalization("Interaction_DESCRIPTION_Index_198", "Attributes:"); + addStringLocalization("Interaction_DESCRIPTION_Index_199", "Discovered by: "); + addStringLocalization("Interaction_DESCRIPTION_Index_200", "Sort mode: "); + addStringLocalization("Interaction_DESCRIPTION_Index_200.1", "Automatic Item Shuffling: "); + addStringLocalization("Interaction_DESCRIPTION_Index_201", "Nothing"); + addStringLocalization("Interaction_DESCRIPTION_Index_202", "Pollution in Chunk: "); + addStringLocalization("Interaction_DESCRIPTION_Index_203", " gibbl"); + addStringLocalization("Interaction_DESCRIPTION_Index_204", "No Pollution in Chunk! HAYO!"); + addStringLocalization("Interaction_DESCRIPTION_Index_206", "Scan for Assembly Line"); + addStringLocalization( + "Interaction_DESCRIPTION_Index_207", + "Pump speed: %dL every %d ticks, %.2f L/sec on average"); + addStringLocalization("Interaction_DESCRIPTION_Index_208", " L"); + addStringLocalization("Interaction_DESCRIPTION_Index_209", " ticks"); + addStringLocalization("Interaction_DESCRIPTION_Index_209.1", " tick"); + addStringLocalization("Interaction_DESCRIPTION_Index_210", "Average: %.2f L/sec"); + addStringLocalization("Interaction_DESCRIPTION_Index_211", "Items per side: "); + addStringLocalization("Interaction_DESCRIPTION_Index_212", "Input enabled"); + addStringLocalization("Interaction_DESCRIPTION_Index_213", "Input disabled"); + addStringLocalization("Interaction_DESCRIPTION_Index_214", "Connected"); + addStringLocalization("Interaction_DESCRIPTION_Index_215", "Disconnected"); + addStringLocalization("Interaction_DESCRIPTION_Index_216", "Deprecated Recipe"); + addStringLocalization("Interaction_DESCRIPTION_Index_219", "Extended Facing: "); + addStringLocalization("Interaction_DESCRIPTION_Index_220", "Single recipe locking disabled."); + addStringLocalization("Interaction_DESCRIPTION_Index_221", "Item threshold"); + addStringLocalization("Interaction_DESCRIPTION_Index_222", "Fluid threshold"); + addStringLocalization("Interaction_DESCRIPTION_Index_222.1", "Energy threshold"); + addStringLocalization( + "Interaction_DESCRIPTION_Index_223", + "Single recipe locking enabled. Will lock to next recipe."); + addStringLocalization("Interaction_DESCRIPTION_Index_224", "Always On"); + addStringLocalization("Interaction_DESCRIPTION_Index_225", "Active with Redstone Signal"); + addStringLocalization("Interaction_DESCRIPTION_Index_226", "Inactive with Redstone Signal"); + addStringLocalization("Interaction_DESCRIPTION_Index_227", "Allow Input"); + addStringLocalization("Interaction_DESCRIPTION_Index_228", "Block Input"); + addStringLocalization("Interaction_DESCRIPTION_Index_229", "Export/Import"); + addStringLocalization("Interaction_DESCRIPTION_Index_230", "Conditional"); + addStringLocalization("Interaction_DESCRIPTION_Index_231", "Enable Input"); + addStringLocalization("Interaction_DESCRIPTION_Index_232", "Filter Input"); + addStringLocalization("Interaction_DESCRIPTION_Index_233", "Filter Output"); + addStringLocalization("Interaction_DESCRIPTION_Index_234", "Block Output"); + addStringLocalization("Interaction_DESCRIPTION_Index_235", "Allow Output"); + addStringLocalization("Interaction_DESCRIPTION_Index_236", "Whitelist Fluid"); + addStringLocalization("Interaction_DESCRIPTION_Index_237", "Blacklist Fluid"); + addStringLocalization("Interaction_DESCRIPTION_Index_238", "Filter Direction"); + addStringLocalization("Interaction_DESCRIPTION_Index_239", "Filter Type"); + addStringLocalization("Interaction_DESCRIPTION_Index_240", "Block Flow"); + addStringLocalization("Interaction_DESCRIPTION_Index_241", "Recipe progress"); + addStringLocalization("Interaction_DESCRIPTION_Index_242", "Machine idle"); + addStringLocalization("Interaction_DESCRIPTION_Index_243", "Enable with Redstone"); + addStringLocalization("Interaction_DESCRIPTION_Index_244", "Disable with Redstone"); + addStringLocalization("Interaction_DESCRIPTION_Index_245", "Disable machine"); + addStringLocalization("Interaction_DESCRIPTION_Index_246", "Frequency"); + addStringLocalization("Interaction_DESCRIPTION_Index_247", "1 Issue"); + addStringLocalization("Interaction_DESCRIPTION_Index_248", "2 Issues"); + addStringLocalization("Interaction_DESCRIPTION_Index_249", "3 Issues"); + addStringLocalization("Interaction_DESCRIPTION_Index_250", "4 Issues"); + addStringLocalization("Interaction_DESCRIPTION_Index_251", "5 Issues"); + addStringLocalization("Interaction_DESCRIPTION_Index_252", "Rotor < 80%"); + addStringLocalization("Interaction_DESCRIPTION_Index_253", "Rotor < 100%"); + addStringLocalization("Interaction_DESCRIPTION_Index_254", "Detect slot#"); + addStringLocalization("Interaction_DESCRIPTION_Index_254.0", "Detect Slot"); + addStringLocalization("Interaction_DESCRIPTION_Index_254.1", "Internal slot#"); + addStringLocalization("Interaction_DESCRIPTION_Index_255", "Adjacent slot#"); + addStringLocalization("Interaction_DESCRIPTION_Index_256", "Universal Storage"); + addStringLocalization("Interaction_DESCRIPTION_Index_257", "Electricity Storage"); + addStringLocalization("Interaction_DESCRIPTION_Index_258", "Steam Storage"); + addStringLocalization("Interaction_DESCRIPTION_Index_259", "Average Electric Input"); + addStringLocalization("Interaction_DESCRIPTION_Index_260", "Average Electric Output"); + addStringLocalization("Interaction_DESCRIPTION_Index_261", "Electricity Storage(Including Batteries)"); + addStringLocalization("Interaction_DESCRIPTION_Index_262", "Fluid Auto Output Disabled"); + addStringLocalization("Interaction_DESCRIPTION_Index_263", "Fluid Auto Output Enabled"); + addStringLocalization( + "Interaction_DESCRIPTION_Index_264", + "currently none, will be locked to the next that is put in"); + addStringLocalization("Interaction_DESCRIPTION_Index_265", "1 specific Fluid"); + addStringLocalization("Interaction_DESCRIPTION_Index_266", "Lock Fluid Mode Disabled"); + addStringLocalization("Interaction_DESCRIPTION_Index_267", "Overflow Voiding Mode Disabled"); + addStringLocalization("Interaction_DESCRIPTION_Index_268", "Overflow Voiding Mode Enabled"); + addStringLocalization("Interaction_DESCRIPTION_Index_269", "Void Full Mode Disabled"); + addStringLocalization("Interaction_DESCRIPTION_Index_270", "Void Full Mode Enabled"); + addStringLocalization("Interaction_DESCRIPTION_Index_271", "unspecified"); + addStringLocalization("Interaction_DESCRIPTION_Index_272", "Recipe by: "); + addStringLocalization("Interaction_DESCRIPTION_Index_273", "Original Recipe by: "); + addStringLocalization("Interaction_DESCRIPTION_Index_274", "Modified by: "); + addStringLocalization("Interaction_DESCRIPTION_Index_275", "Original voltage: "); + addStringLocalization("Interaction_DESCRIPTION_Index_299", "Item Filter: "); + addStringLocalization("Interaction_DESCRIPTION_Index_300", "Filter Cleared!"); + addStringLocalization("Interaction_DESCRIPTION_Index_300.1", "Fluid Lock Cleared."); + addStringLocalization("Interaction_DESCRIPTION_Index_301", "Universal"); + addStringLocalization("Interaction_DESCRIPTION_Index_302", "Int. EU"); + addStringLocalization("Interaction_DESCRIPTION_Index_303", "Steam"); + addStringLocalization("Interaction_DESCRIPTION_Index_304", "Avg. Input"); + addStringLocalization("Interaction_DESCRIPTION_Index_305", "Avg. Output"); + addStringLocalization("Interaction_DESCRIPTION_Index_306", "EU stored"); + addStringLocalization("Interaction_DESCRIPTION_Index_307", "Deny input, Filter output"); + addStringLocalization("Interaction_DESCRIPTION_Index_308", "Deny input, Invert output"); + addStringLocalization("Interaction_DESCRIPTION_Index_309", "Permit any input, Filter output"); + addStringLocalization("Interaction_DESCRIPTION_Index_310", "Permit any input, Invert output"); + addStringLocalization("Interaction_DESCRIPTION_Index_311", "Block Output"); + addStringLocalization("Interaction_DESCRIPTION_Index_312", "Allow Output"); + addStringLocalization("Interaction_DESCRIPTION_Index_313", "Block Input"); + addStringLocalization("Interaction_DESCRIPTION_Index_314", "Allow Input"); + addStringLocalization("Interaction_DESCRIPTION_Index_315", "Filter Empty"); + addStringLocalization("Interaction_DESCRIPTION_Index_316", "Pump speed limit reached!"); + addStringLocalization("Interaction_DESCRIPTION_Index_317", "Filter: "); + addStringLocalization("Interaction_DESCRIPTION_Index_318", "Check Mode"); + addStringLocalization("Interaction_DESCRIPTION_Index_319", "Any player"); + addStringLocalization("Interaction_DESCRIPTION_Index_320", "Other players"); + addStringLocalization("Interaction_DESCRIPTION_Index_321", "Only owner"); + addStringLocalization("Interaction_DESCRIPTION_Index_322", "Overflow point: "); + addStringLocalization("Interaction_DESCRIPTION_Index_323", "L"); + addStringLocalization("Interaction_DESCRIPTION_Index_324", "Now"); + addStringLocalization("Interaction_DESCRIPTION_Index_325", "Max"); + addStringLocalization("Interaction_DESCRIPTION_Index_326", "Public"); + addStringLocalization("Interaction_DESCRIPTION_Index_327", "Private"); + addStringLocalization("Interaction_DESCRIPTION_Index_328", "Channel"); + addStringLocalization("Interaction_DESCRIPTION_Index_329", "Public/Private"); + addStringLocalization("Interaction_DESCRIPTION_Index_330", "Sneak Rightclick to switch Mode"); + addStringLocalization("Interaction_DESCRIPTION_Index_331", "AND Gate"); + addStringLocalization("Interaction_DESCRIPTION_Index_332", "NAND Gate"); + addStringLocalization("Interaction_DESCRIPTION_Index_333", "OR Gate"); + addStringLocalization("Interaction_DESCRIPTION_Index_334", "NOR Gate"); + addStringLocalization("Interaction_DESCRIPTION_Index_335", "Gate Mode"); + addStringLocalization("Interaction_DESCRIPTION_Index_336", "PCB Factory Tier: "); + addStringLocalization("Interaction_DESCRIPTION_Index_337", "Upgrade Required: "); + addStringLocalization("Interaction_DESCRIPTION_Index_338", "Bio"); + addStringLocalization("Interaction_DESCRIPTION_Index_339", "Biochamber Upgrade Enabled"); + addStringLocalization("Interaction_DESCRIPTION_Index_339.1", "Biochamber Upgrade Disabled"); + addStringLocalization("Interaction_DESCRIPTION_Index_340", "Rotated biochamber enabled"); + addStringLocalization("Interaction_DESCRIPTION_Index_340.1", "Rotated biochamber disabled"); + addStringLocalization("Interaction_DESCRIPTION_Index_341", "Tier 1 cooling enabled"); + addStringLocalization("Interaction_DESCRIPTION_Index_341.1", "Tier 1 cooling disabled"); + addStringLocalization("Interaction_DESCRIPTION_Index_342", "Tier 2 cooling enabled"); + addStringLocalization("Interaction_DESCRIPTION_Index_342.1", "Tier 2 cooling disabled"); + addStringLocalization("Interaction_DESCRIPTION_Index_343", "Use Machine Processing State"); + addStringLocalization("Interaction_DESCRIPTION_Index_343.1", "Use Inverted Machine Processing State"); + addStringLocalization("Interaction_DESCRIPTION_Index_344", "Input Blocking"); + addStringLocalization("Interaction_DESCRIPTION_Index_344.1", "Output Blocking"); + addStringLocalization("Interaction_DESCRIPTION_Index_500", "Fitting: Loose - More Flow"); + addStringLocalization("Interaction_DESCRIPTION_Index_501", "Fitting: Tight - More Efficiency"); + addStringLocalization("Interaction_DESCRIPTION_Index_502", "Mining chunk loading enabled"); + addStringLocalization("Interaction_DESCRIPTION_Index_503", "Mining chunk loading disabled"); + addStringLocalization("Interaction_DESCRIPTION_Index_505", "Enable with Signal (Safe)"); + addStringLocalization("Interaction_DESCRIPTION_Index_506", "Disable with Signal (Safe)"); + addStringLocalization("Interaction_DESCRIPTION_Index_507", "Safe Mode"); + addStringLocalization("Interaction_DESCRIPTION_Index_602", "Use Private Frequency"); + addStringLocalization("Interaction_DESCRIPTION_Index_756", "Connectable: "); + addStringLocalization("Interaction_DESCRIPTION_Index_ALL", "All"); + addStringLocalization("Interaction_DESCRIPTION_Index_ANY", "Any"); + addStringLocalization("Interaction_DESCRIPTION_Index_INVERTED", "Inverted"); + addStringLocalization("Interaction_DESCRIPTION_Index_NORMAL", "Normal"); + addStringLocalization("Interaction_DESCRIPTION_Index_SIDE", "Side: "); + + addStringLocalization("Item_DESCRIPTION_Index_000", "Stored Heat: %s"); + addStringLocalization("Item_DESCRIPTION_Index_001", "Durability: %s/%s"); + addStringLocalization("Item_DESCRIPTION_Index_002", "%s lvl %s"); + addStringLocalization("Item_DESCRIPTION_Index_003", "Attack Damage: %s"); + addStringLocalization("Item_DESCRIPTION_Index_004", "Mining Speed: %s"); + addStringLocalization("Item_DESCRIPTION_Index_005", "Turbine Efficiency: %s"); + addStringLocalization("Item_DESCRIPTION_Index_006", "Optimal Steam flow: %s L/t"); + addStringLocalization("Item_DESCRIPTION_Index_007", "Energy from Optimal Gas Flow: %s EU/t"); + addStringLocalization("Item_DESCRIPTION_Index_008", "Energy from Optimal Plasma Flow: %s EU/t"); + addStringLocalization("Item_DESCRIPTION_Index_009", "Contains %s EU Tier: %s"); + addStringLocalization("Item_DESCRIPTION_Index_010", "Empty. You should recycle it properly."); + addStringLocalization("Item_DESCRIPTION_Index_011", "%s / %s EU - Voltage: %s"); + addStringLocalization("Item_DESCRIPTION_Index_012", "No Fluids Contained"); + addStringLocalization("Item_DESCRIPTION_Index_013", "%sL / %sL"); + addStringLocalization("Item_DESCRIPTION_Index_014", "Missing Coodinates!"); + addStringLocalization("Item_DESCRIPTION_Index_015", "Device at:"); + addStringLocalization("Item_DESCRIPTION_Index_018", "State: %s"); + addStringLocalization("Item_DESCRIPTION_Index_019", "Bath with neutron in a hot reactor"); + addStringLocalization("Item_DESCRIPTION_Index_020", "Progress: %s/%s"); + addStringLocalization("Item_DESCRIPTION_Index_021", "Radiation Hazard"); + addStringLocalization("Item_DESCRIPTION_Index_500", "Turbine Efficiency (Loose): %s"); + addStringLocalization("Item_DESCRIPTION_Index_501", "Optimal Steam flow (Loose): %s L/t"); + addStringLocalization("Item_DESCRIPTION_Index_502", "Overflow Efficiency Tier: %s"); + addStringLocalization("Item_DESCRIPTION_Index_900", "Energy from Optimal Steam Flow: %s EU/t"); + addStringLocalization("Item_DESCRIPTION_Index_901", "Energy from Optimal Steam Flow (Loose): %s EU/t"); + + addStringLocalization(FACE_ANY, "Any Side"); + addStringLocalization(FACE_BOTTOM, "Bottom"); + addStringLocalization(FACE_TOP, "Top"); + addStringLocalization(FACE_LEFT, "Left"); + addStringLocalization(FACE_FRONT, "Front"); + addStringLocalization(FACE_RIGHT, "Right"); + addStringLocalization(FACE_BACK, "Back"); + addStringLocalization(FACE_NONE, "None"); + } + + private static void addToMCLangList(String aKey, String translation) { + if (stringTranslateLanguageList != null) { + stringTranslateLanguageList.put(aKey, translation); + } + } +} diff --git a/src/main/java/gregtech/api/util/GT_Log.java b/src/main/java/gregtech/api/util/GT_Log.java new file mode 100644 index 0000000000..2d00c2e061 --- /dev/null +++ b/src/main/java/gregtech/api/util/GT_Log.java @@ -0,0 +1,45 @@ +package gregtech.api.util; + +import java.io.File; +import java.io.OutputStream; +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.List; + +/** + * NEVER INCLUDE THIS FILE IN YOUR MOD!!! + * <p/> + * Just a simple Logging Function. If on Server, then this will point to System.out and System.err + */ +public class GT_Log { + + public static PrintStream out = System.out; + public static PrintStream err = System.err; + public static PrintStream ore = new LogBuffer(); + public static PrintStream pal = null; + public static PrintStream exp = new LogBuffer(); + public static File mLogFile; + public static File mOreDictLogFile; + public static File mPlayerActivityLogFile; + public static File mExplosionLog; + + public static class LogBuffer extends PrintStream { + + public final List<String> mBufferedOreDictLog = new ArrayList<>(); + + public LogBuffer() { + super(new OutputStream() { + + @Override + public void write(int arg0) { + /* Do nothing */ + } + }); + } + + @Override + public void println(String aString) { + mBufferedOreDictLog.add(aString); + } + } +} diff --git a/src/main/java/gregtech/api/util/GT_ModHandler.java b/src/main/java/gregtech/api/util/GT_ModHandler.java new file mode 100644 index 0000000000..70dc2f30b0 --- /dev/null +++ b/src/main/java/gregtech/api/util/GT_ModHandler.java @@ -0,0 +1,2551 @@ +package gregtech.api.util; + +import static gregtech.GT_Mod.GT_FML_LOGGER; +import static gregtech.api.enums.GT_Values.B; +import static gregtech.api.enums.GT_Values.D1; +import static gregtech.api.enums.GT_Values.DW; +import static gregtech.api.enums.GT_Values.E; +import static gregtech.api.enums.GT_Values.M; +import static gregtech.api.enums.GT_Values.RA; +import static gregtech.api.enums.GT_Values.V; +import static gregtech.api.enums.GT_Values.W; +import static gregtech.api.recipe.RecipeMaps.alloySmelterRecipes; +import static gregtech.api.recipe.RecipeMaps.extractorRecipes; +import static gregtech.api.recipe.RecipeMaps.oreWasherRecipes; +import static gregtech.api.util.GT_RecipeBuilder.SECONDS; +import static gregtech.api.util.GT_RecipeBuilder.TICKS; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import javax.annotation.Nullable; + +import net.minecraft.block.Block; +import net.minecraft.enchantment.Enchantment; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.Blocks; +import net.minecraft.init.Items; +import net.minecraft.inventory.Container; +import net.minecraft.inventory.IInventory; +import net.minecraft.inventory.InventoryCrafting; +import net.minecraft.item.Item; +import net.minecraft.item.ItemBlock; +import net.minecraft.item.ItemStack; +import net.minecraft.item.crafting.CraftingManager; +import net.minecraft.item.crafting.FurnaceRecipes; +import net.minecraft.item.crafting.IRecipe; +import net.minecraft.item.crafting.ShapedRecipes; +import net.minecraft.item.crafting.ShapelessRecipes; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.tileentity.TileEntityFurnace; +import net.minecraft.world.World; +import net.minecraftforge.fluids.FluidRegistry; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.oredict.ShapedOreRecipe; +import net.minecraftforge.oredict.ShapelessOreRecipe; + +import cpw.mods.fml.common.registry.GameRegistry; +import gregtech.api.GregTech_API; +import gregtech.api.enums.GT_Values; +import gregtech.api.enums.ItemList; +import gregtech.api.enums.Materials; +import gregtech.api.enums.OreDictNames; +import gregtech.api.enums.OrePrefixes; +import gregtech.api.enums.ToolDictNames; +import gregtech.api.interfaces.IDamagableItem; +import gregtech.api.interfaces.IItemContainer; +import gregtech.api.interfaces.internal.IGT_CraftingRecipe; +import gregtech.api.items.GT_MetaBase_Item; +import gregtech.api.objects.GT_HashSet; +import gregtech.api.objects.GT_ItemStack; +import gregtech.api.objects.ItemData; +import gregtech.api.recipe.RecipeCategories; +import gregtech.api.recipe.RecipeMap; +import ic2.api.item.IBoxable; +import ic2.api.item.IC2Items; +import ic2.api.item.IElectricItem; +import ic2.api.reactor.IReactorComponent; +import ic2.api.recipe.IRecipeInput; +import ic2.api.recipe.RecipeInputItemStack; +import ic2.api.recipe.RecipeOutput; +import ic2.core.item.ItemToolbox; + +/** + * NEVER INCLUDE THIS FILE IN YOUR MOD!!! + * <p/> + * This is the Interface I use for interacting with other Mods. + * <p/> + * Due to the many imports, this File can cause compile Problems if not all the APIs are installed + */ +public class GT_ModHandler { + + public static final List<IRecipe> sSingleNonBlockDamagableRecipeList = new ArrayList<>(1000); + private static final Map<String, ItemStack> sIC2ItemMap = new HashMap<>(); + + private static final List<IRecipe> sAllRecipeList = new ArrayList<>(5000), + sBufferRecipeList = new ArrayList<>(1000); + private static final List<ItemStack> delayedRemovalByOutput = new ArrayList<>(); + private static final List<InventoryCrafting> delayedRemovalByRecipe = new ArrayList<>(); + + public static Collection<String> sNativeRecipeClasses = new HashSet<>(), sSpecialRecipeClasses = new HashSet<>(); + public static GT_HashSet<GT_ItemStack> sNonReplaceableItems = new GT_HashSet<>(); + public static Object sBoxableWrapper = new GT_IBoxableWrapper(); + public static Collection<GT_ItemStack> sBoxableItems = new ArrayList<>(); + private static final Map<IRecipeInput, RecipeOutput> emptyRecipeMap = new HashMap<>(); + private static Set<GT_Utility.ItemId> recyclerWhitelist; + private static Set<GT_Utility.ItemId> recyclerBlacklist; + + private static boolean sBufferCraftingRecipes = true; + public static List<Integer> sSingleNonBlockDamagableRecipeList_list = new ArrayList<>(100); + private static final boolean sSingleNonBlockDamagableRecipeList_create = true; + private static final ItemStack sMt1 = new ItemStack(Blocks.dirt, 1, 0), sMt2 = new ItemStack(Blocks.dirt, 1, 0); + private static final String s_H = "h", s_F = "f", s_I = "I", s_P = "P", s_R = "R"; + private static final ItemStack[][] sShapes1 = new ItemStack[][] { + { sMt1, null, sMt1, sMt1, sMt1, sMt1, null, sMt1, null }, + { sMt1, null, sMt1, sMt1, null, sMt1, sMt1, sMt1, sMt1 }, + { null, sMt1, null, sMt1, sMt1, sMt1, sMt1, null, sMt1 }, + { sMt1, sMt1, sMt1, sMt1, null, sMt1, null, null, null }, + { sMt1, null, sMt1, sMt1, sMt1, sMt1, sMt1, sMt1, sMt1 }, + { sMt1, sMt1, sMt1, sMt1, null, sMt1, sMt1, null, sMt1 }, + { null, null, null, sMt1, null, sMt1, sMt1, null, sMt1 }, + { null, sMt1, null, null, sMt1, null, null, sMt2, null }, + { sMt1, sMt1, sMt1, null, sMt2, null, null, sMt2, null }, + { null, sMt1, null, null, sMt2, null, null, sMt2, null }, + { sMt1, sMt1, null, sMt1, sMt2, null, null, sMt2, null }, + { null, sMt1, sMt1, null, sMt2, sMt1, null, sMt2, null }, + { sMt1, sMt1, null, null, sMt2, null, null, sMt2, null }, + { null, sMt1, sMt1, null, sMt2, null, null, sMt2, null }, + { null, sMt1, null, sMt1, null, null, null, sMt1, sMt2 }, + { null, sMt1, null, null, null, sMt1, sMt2, sMt1, null }, + { null, sMt1, null, sMt1, null, sMt1, null, null, sMt2 }, + { null, sMt1, null, sMt1, null, sMt1, sMt2, null, null }, + { null, sMt2, null, null, sMt1, null, null, sMt1, null }, + { null, sMt2, null, null, sMt2, null, sMt1, sMt1, sMt1 }, + { null, sMt2, null, null, sMt2, null, null, sMt1, null }, + { null, sMt2, null, sMt1, sMt2, null, sMt1, sMt1, null }, + { null, sMt2, null, null, sMt2, sMt1, null, sMt1, sMt1 }, + { null, sMt2, null, null, sMt2, null, sMt1, sMt1, null }, + { sMt1, null, null, null, sMt2, null, null, null, sMt2 }, + { null, null, sMt1, null, sMt2, null, sMt2, null, null }, + { sMt1, null, null, null, sMt2, null, null, null, null }, + { null, null, sMt1, null, sMt2, null, null, null, null }, + { sMt1, sMt2, null, null, null, null, null, null, null }, + { sMt2, sMt1, null, null, null, null, null, null, null }, + { sMt1, null, null, sMt2, null, null, null, null, null }, + { sMt2, null, null, sMt1, null, null, null, null, null }, + { sMt1, sMt1, sMt1, sMt1, sMt1, sMt1, null, sMt2, null }, + { sMt1, sMt1, null, sMt1, sMt1, sMt2, sMt1, sMt1, null }, + { null, sMt1, sMt1, sMt2, sMt1, sMt1, null, sMt1, sMt1 }, + { null, sMt2, null, sMt1, sMt1, sMt1, sMt1, sMt1, sMt1 }, + { sMt1, sMt1, sMt1, sMt1, sMt2, sMt1, null, sMt2, null }, + { sMt1, sMt1, null, sMt1, sMt2, sMt2, sMt1, sMt1, null }, + { null, sMt1, sMt1, sMt2, sMt2, sMt1, null, sMt1, sMt1 }, + { null, sMt2, null, sMt1, sMt2, sMt1, sMt1, sMt1, sMt1 }, + { sMt1, null, null, null, sMt1, null, null, null, null }, + { null, sMt1, null, sMt1, null, null, null, null, null }, + { sMt1, sMt1, null, sMt2, null, sMt1, sMt2, null, null }, + { null, sMt1, sMt1, sMt1, null, sMt2, null, null, sMt2 } }; + public static List<Integer> sSingleNonBlockDamagableRecipeList_validsShapes1 = new ArrayList<>(44); + public static boolean sSingleNonBlockDamagableRecipeList_validsShapes1_update = false; + public static List<Integer> sSingleNonBlockDamagableRecipeList_warntOutput = new ArrayList<>(50); + public static List<Integer> sVanillaRecipeList_warntOutput = new ArrayList<>(50); + public static final List<IRecipe> sSingleNonBlockDamagableRecipeList_verified = new ArrayList<>(1000); + public static List<Integer> sAnySteamFluidIDs = new ArrayList<>(); + public static List<Integer> sSuperHeatedSteamFluidIDs = new ArrayList<>(); + + static { + sNativeRecipeClasses.add(ShapedRecipes.class.getName()); + sNativeRecipeClasses.add(ShapedOreRecipe.class.getName()); + sNativeRecipeClasses.add(GT_Shaped_Recipe.class.getName()); + sNativeRecipeClasses.add(ShapelessRecipes.class.getName()); + sNativeRecipeClasses.add(ShapelessOreRecipe.class.getName()); + sNativeRecipeClasses.add(GT_Shapeless_Recipe.class.getName()); + sNativeRecipeClasses.add(ic2.core.AdvRecipe.class.getName()); + sNativeRecipeClasses.add(ic2.core.AdvShapelessRecipe.class.getName()); + sNativeRecipeClasses.add("appeng.recipes.game.ShapedRecipe"); + sNativeRecipeClasses.add("appeng.recipes.game.ShapelessRecipe"); + sNativeRecipeClasses.add("forestry.core.utils.ShapedRecipeCustom"); + + // Recipe Classes, which should never be removed. + sSpecialRecipeClasses.add(net.minecraft.item.crafting.RecipeFireworks.class.getName()); + sSpecialRecipeClasses.add(net.minecraft.item.crafting.RecipesArmorDyes.class.getName()); + sSpecialRecipeClasses.add(net.minecraft.item.crafting.RecipeBookCloning.class.getName()); + sSpecialRecipeClasses.add(net.minecraft.item.crafting.RecipesMapCloning.class.getName()); + sSpecialRecipeClasses.add(net.minecraft.item.crafting.RecipesMapExtending.class.getName()); + sSpecialRecipeClasses.add("jds.bibliocraft.BiblioSpecialRecipes"); + sSpecialRecipeClasses.add("dan200.qcraft.shared.EntangledQBlockRecipe"); + sSpecialRecipeClasses.add("dan200.qcraft.shared.EntangledQuantumComputerRecipe"); + sSpecialRecipeClasses.add("dan200.qcraft.shared.QBlockRecipe"); + sSpecialRecipeClasses.add("appeng.recipes.game.FacadeRecipe"); + sSpecialRecipeClasses.add("appeng.recipes.game.DisassembleRecipe"); + sSpecialRecipeClasses.add("mods.railcraft.common.carts.LocomotivePaintingRecipe"); + sSpecialRecipeClasses.add("mods.railcraft.common.util.crafting.RotorRepairRecipe"); + sSpecialRecipeClasses.add("mods.railcraft.common.util.crafting.RoutingTableCopyRecipe"); + sSpecialRecipeClasses.add("mods.railcraft.common.util.crafting.RoutingTicketCopyRecipe"); + sSpecialRecipeClasses.add("mods.railcraft.common.util.crafting.TankCartFilterRecipe"); + sSpecialRecipeClasses.add("mods.railcraft.common.emblems.LocomotiveEmblemRecipe"); + sSpecialRecipeClasses.add("mods.railcraft.common.emblems.EmblemPostColorRecipe"); + sSpecialRecipeClasses.add("mods.railcraft.common.emblems.EmblemPostEmblemRecipe"); + sSpecialRecipeClasses.add("mods.immibis.redlogic.interaction.RecipeDyeLumarButton"); + sSpecialRecipeClasses.add("thaumcraft.common.items.armor.RecipesRobeArmorDyes"); + sSpecialRecipeClasses.add("thaumcraft.common.items.armor.RecipesVoidRobeArmorDyes"); + sSpecialRecipeClasses.add("thaumcraft.common.lib.crafting.ShapelessNBTOreRecipe"); + sSpecialRecipeClasses.add("twilightforest.item.TFMapCloningRecipe"); + sSpecialRecipeClasses.add("forestry.lepidopterology.MatingRecipe"); + sSpecialRecipeClasses.add("micdoodle8.mods.galacticraft.planets.asteroids.recipe.CanisterRecipes"); + sSpecialRecipeClasses.add("shedar.mods.ic2.nuclearcontrol.StorageArrayRecipe"); + } + + /** + * Returns if that Liquid is Water or Distilled Water + */ + public static boolean isWater(FluidStack aFluid) { + if (aFluid == null) return false; + return aFluid.isFluidEqual(getWater(1)) || aFluid.isFluidEqual(getDistilledWater(1)); + } + + /** + * Returns a Liquid Stack with given amount of Water. + */ + public static FluidStack getWater(long aAmount) { + return FluidRegistry.getFluidStack("water", (int) aAmount); + } + + /** + * Returns a Liquid Stack with given amount of distilled Water. + */ + public static FluidStack getDistilledWater(long aAmount) { + FluidStack tFluid = FluidRegistry.getFluidStack("ic2distilledwater", (int) aAmount); + if (tFluid == null) tFluid = getWater(aAmount); + return tFluid; + } + + /** + * Returns if that Liquid is Lava + */ + public static boolean isLava(FluidStack aFluid) { + if (aFluid == null) return false; + return aFluid.isFluidEqual(getLava(1)); + } + + /** + * Returns a Liquid Stack with given amount of Lava. + */ + public static FluidStack getLava(long aAmount) { + return FluidRegistry.getFluidStack("lava", (int) aAmount); + } + + /** + * Returns if that Liquid is Steam + */ + public static boolean isSteam(FluidStack aFluid) { + if (aFluid == null) return false; + return aFluid.isFluidEqual(getSteam(1)); + } + + /** + * Returns if that Liquid is Any Steam (including other mods) + */ + public static boolean isAnySteam(FluidStack aFluid) { + return (aFluid != null && (isSteam(aFluid) || sAnySteamFluidIDs.contains(aFluid.getFluidID()))); + } + + /** + * Returns if that Liquid is Super Heated Steam (including other mods) + */ + public static boolean isSuperHeatedSteam(FluidStack aFluid) { + return (aFluid != null && sSuperHeatedSteamFluidIDs.contains(aFluid.getFluidID())); + } + + /** + * Returns a Liquid Stack with given amount of Steam. + */ + public static FluidStack getSteam(long aAmount) { + return FluidRegistry.getFluidStack("steam", (int) aAmount); + } + + /** + * Returns if that Liquid is Milk + */ + public static boolean isMilk(FluidStack aFluid) { + if (aFluid == null) return false; + return aFluid.isFluidEqual(getMilk(1)); + } + + /** + * Returns a Liquid Stack with given amount of Milk. + */ + public static FluidStack getMilk(long aAmount) { + return FluidRegistry.getFluidStack("milk", (int) aAmount); + } + + @Deprecated + public static ItemStack getEmptyFuelCan(long aAmount) { + return null; + } + + public static ItemStack getEmptyCell(long aAmount) { + return ItemList.Cell_Empty.get(aAmount); + } + + public static ItemStack getAirCell(long aAmount) { + return ItemList.Cell_Air.get(aAmount); + } + + public static ItemStack getWaterCell(long aAmount) { + return ItemList.Cell_Water.get(aAmount); + } + + public static ItemStack getLavaCell(long aAmount) { + return ItemList.Cell_Lava.get(aAmount); + } + + /** + * @param aValue the Value of this Stack, when burning inside a Furnace (200 = 1 Burn Process = 500 EU, max = 32767 + * (that is 81917.5 EU)), limited to Short because the vanilla Furnace otherwise can't handle it + * properly, stupid Mojang... + */ + public static ItemStack setFuelValue(ItemStack aStack, short aValue) { + aStack.setTagCompound(GT_Utility.getNBTContainingShort(aStack.getTagCompound(), "GT.ItemFuelValue", aValue)); + return aStack; + } + + /** + * @return the Value of this Stack, when burning inside a Furnace (200 = 1 Burn Process = 500 EU, max = 32767 (that + * is 81917.5 EU)), limited to Short because the vanilla Furnace otherwise can't handle it properly, stupid + * Mojang... + */ + public static int getFuelValue(ItemStack aStack) { + return TileEntityFurnace.getItemBurnTime(aStack); + } + + /** + * @param aValue Fuel value in EU + */ + @Deprecated + public static ItemStack getFuelCan(int aValue) { + return null; + } + + /** + * @param aFuelCan the Item you want to check + * @return the exact Value in EU the Fuel Can is worth if its even a Fuel Can. + */ + @Deprecated + public static int getFuelCanValue(ItemStack aFuelCan) { + return 0; + } + + /** + * Gets an Item from IndustrialCraft, and returns a Replacement Item if not possible + */ + public static ItemStack getIC2Item(String aItem, long aAmount, ItemStack aReplacement) { + if (GT_Utility.isStringInvalid(aItem) || !GregTech_API.sPreloadStarted) return null; + // if (D1) GT_Log.out.println("Requested the Item '" + aItem + "' from the IC2-API"); + if (!sIC2ItemMap.containsKey(aItem)) try { + ItemStack tStack = IC2Items.getItem(aItem); + sIC2ItemMap.put(aItem, tStack); + if (tStack == null && D1) GT_Log.err.println(aItem + " is not found in the IC2 Items!"); + } catch (Throwable e) { + /* Do nothing */ + } + return GT_Utility.copyAmount(aAmount, sIC2ItemMap.get(aItem), aReplacement); + } + + /** + * Gets an Item from IndustrialCraft, but the Damage Value can be specified, and returns a Replacement Item with the + * same Damage if not possible + */ + public static ItemStack getIC2Item(String aItem, long aAmount, int aMeta, ItemStack aReplacement) { + ItemStack rStack = getIC2Item(aItem, aAmount, aReplacement); + if (rStack == null) return null; + Items.feather.setDamage(rStack, aMeta); + return rStack; + } + + /** + * Gets an Item from IndustrialCraft, but the Damage Value can be specified + */ + public static ItemStack getIC2Item(String aItem, long aAmount, int aMeta) { + return getIC2Item(aItem, aAmount, aMeta, null); + } + + /** + * Gets an Item from IndustrialCraft + */ + public static ItemStack getIC2Item(String aItem, long aAmount) { + return getIC2Item(aItem, aAmount, null); + } + + /** + * Gets an Item from the specified mod + */ + public static ItemStack getModItem(String aModID, String aItem, long aAmount) { + return getModItem(aModID, aItem, aAmount, null); + } + + /** + * Gets an Item from the specified mod, and returns a Replacement Item if not possible + */ + public static ItemStack getModItem(String aModID, String aItem, long aAmount, ItemStack aReplacement) { + ItemStack result; + if (GT_Utility.isStringInvalid(aItem) || !GregTech_API.sPreloadStarted) { + result = null; + } else { + result = GT_Utility + .copyAmount(aAmount, GameRegistry.findItemStack(aModID, aItem, (int) aAmount), aReplacement); + } + + if (result == null) { + String reason; + if (GT_Utility.isStringInvalid(aItem)) { + reason = "the name of the item is an invalid string"; + } else if (!GregTech_API.sPreloadStarted) { + reason = "the GT5U preloading phase has not yet started"; + } else { + reason = "the item was not found in the game registry"; + } + String log_message = "getModItem call: object \"" + aItem + + "\" with mod id \"" + + aModID + + "\" has returned null because " + + reason; + GT_Log.out.println(log_message); + new Exception().printStackTrace(GT_Log.out); + } + return result; + } + + /** + * Gets an Item from the specified mod, but the Damage Value can be specified + */ + public static ItemStack getModItem(String aModID, String aItem, long aAmount, int aMeta) { + ItemStack rStack = getModItem(aModID, aItem, aAmount); + if (rStack == null) return null; + Items.feather.setDamage(rStack, aMeta); + return rStack; + } + + /** + * Gets an Item from the specified mod, but the Damage Value can be specified, and returns a Replacement Item with + * the same Damage if not possible + */ + public static ItemStack getModItem(String aModID, String aItem, long aAmount, int aMeta, ItemStack aReplacement) { + ItemStack rStack = getModItem(aModID, aItem, aAmount, aReplacement); + if (rStack == null) return null; + Items.feather.setDamage(rStack, aMeta); + return rStack; + } + + /** + * OUT OF ORDER + */ + public static boolean getModeKeyDown(EntityPlayer aPlayer) { + return false; + } + + /** + * OUT OF ORDER + */ + public static boolean getBoostKeyDown(EntityPlayer aPlayer) { + return false; + } + + /** + * OUT OF ORDER + */ + public static boolean getJumpKeyDown(EntityPlayer aPlayer) { + return false; + } + + /** + * Adds a Valuable Ore to the Miner + */ + public static boolean addValuableOre(Block aBlock, int aMeta, int aValue) { + if (aValue <= 0) return false; + try { + Class.forName("ic2.core.IC2") + .getMethod("addValuableOre", IRecipeInput.class, int.class) + .invoke(null, new RecipeInputItemStack(new ItemStack(aBlock, 1, aMeta)), aValue); + } catch (Throwable e) { + /* Do nothing */ + } + return true; + } + + /** + * Adds a Scrapbox Drop. Fails at April first for the "suddenly Hoes"-Feature of IC2 + */ + public static boolean addScrapboxDrop(float aChance, ItemStack aOutput) { + aOutput = GT_OreDictUnificator.get(true, aOutput); + if (aOutput == null || aChance <= 0) return false; + aOutput.stackSize = 1; + if (GT_Config.troll && !GT_Utility.areStacksEqual(aOutput, new ItemStack(Items.wooden_hoe, 1, 0))) return false; + try { + GT_Utility.callMethod( + GT_Utility.getFieldContent("ic2.api.recipe.Recipes", "scrapboxDrops", true, true), + "addDrop", + true, + false, + true, + GT_Utility.copyOrNull(aOutput), + aChance); + GT_Utility.callMethod( + GT_Utility.getFieldContent("ic2.api.recipe.Recipes", "scrapboxDrops", true, true), + "addRecipe", + true, + true, + false, + GT_Utility.copyOrNull(aOutput), + aChance); + } catch (Throwable e) { + /* Do nothing */ + } + return true; + } + + /** + * Adds an Item to the Recycler Blacklist + */ + public static boolean addToRecyclerBlackList(ItemStack aRecycledStack) { + if (aRecycledStack == null) return false; + try { + ic2.api.recipe.Recipes.recyclerBlacklist.add(new RecipeInputItemStack(aRecycledStack)); + } catch (Throwable e) { + /* Do nothing */ + } + return true; + } + + /** + * Just simple Furnace smelting. Unbelievable how Minecraft fails at making a simple ItemStack->ItemStack mapping... + */ + public static boolean addSmeltingRecipe(ItemStack aInput, ItemStack aOutput) { + aOutput = GT_OreDictUnificator.get(true, aOutput); + if (aInput == null || aOutput == null) return false; + FurnaceRecipes.smelting() + .func_151394_a(aInput, GT_Utility.copyOrNull(aOutput), 0.0F); + return true; + } + + /** + * Adds to Furnace AND Alloy Smelter + */ + public static boolean addSmeltingAndAlloySmeltingRecipe(ItemStack aInput, ItemStack aOutput, boolean hidden) { + if (aInput == null || aOutput == null) { + return false; + } + boolean temp = aInput.stackSize == 1 && addSmeltingRecipe(aInput, aOutput); + ItemStack input2 = OrePrefixes.ingot.contains(aOutput) ? ItemList.Shape_Mold_Ingot.get(0) + : OrePrefixes.block.contains(aOutput) ? ItemList.Shape_Mold_Block.get(0) + : OrePrefixes.nugget.contains(aOutput) ? ItemList.Shape_Mold_Nugget.get(0) : null; + if (Materials.Graphite.contains(aInput)) { + return false; + } + if ((input2 == null) && ((OrePrefixes.ingot.contains(aInput)) || (OrePrefixes.dust.contains(aInput)) + || (OrePrefixes.gem.contains(aInput)))) { + return false; + } + GT_RecipeBuilder recipeBuilder = GT_Values.RA.stdBuilder(); + if (input2 == null) { + recipeBuilder.itemInputs(aInput); + } else { + recipeBuilder.itemInputs(aInput, input2); + } + recipeBuilder.itemOutputs(aOutput) + .duration(6 * SECONDS + 10 * TICKS) + .eut(3) + .recipeCategory(RecipeCategories.alloySmelterRecycling); + if (hidden) { + recipeBuilder.hidden(); + } + recipeBuilder.addTo(alloySmelterRecipes); + return true; + } + + /** + * LiquidTransposer Recipe for both directions + */ + @Deprecated + public static boolean addLiquidTransposerRecipe(ItemStack aEmptyContainer, FluidStack aLiquid, + ItemStack aFullContainer, int aMJ) { + return true; + } + + /** + * LiquidTransposer Recipe for filling Containers + */ + @Deprecated + public static boolean addLiquidTransposerFillRecipe(ItemStack aEmptyContainer, FluidStack aLiquid, + ItemStack aFullContainer, int aMJ) { + return true; + } + + /** + * LiquidTransposer Recipe for emptying Containers + */ + @Deprecated + public static boolean addLiquidTransposerEmptyRecipe(ItemStack aFullContainer, FluidStack aLiquid, + ItemStack aEmptyContainer, int aMJ) { + return true; + } + + /** + * IC2-Extractor Recipe. Overloads old Recipes automatically + */ + @Deprecated + public static boolean addExtractionRecipe(ItemStack aInput, ItemStack aOutput) { + aOutput = GT_OreDictUnificator.get(true, aOutput); + if (aInput == null || aOutput == null) return false; + RA.stdBuilder() + .itemInputs(aInput) + .itemOutputs(aOutput) + .duration(15 * SECONDS) + .eut(2) + .addTo(extractorRecipes); + return true; + } + + /** + * RC-BlastFurnace Recipes + */ + @Deprecated + public static boolean addRCBlastFurnaceRecipe(ItemStack aInput, ItemStack aOutput, int aTime) { + return true; + } + + @Deprecated + public static boolean addPulverisationRecipe(ItemStack aInput, ItemStack aOutput1) { + return addPulverisationRecipe(aInput, aOutput1, null, 0, false); + } + + @Deprecated + public static boolean addPulverisationRecipe(ItemStack aInput, ItemStack aOutput1, ItemStack aOutput2) { + return addPulverisationRecipe(aInput, aOutput1, aOutput2, 100, false); + } + + @Deprecated + public static boolean addPulverisationRecipe(ItemStack aInput, ItemStack aOutput1, ItemStack aOutput2, + int aChance) { + return addPulverisationRecipe(aInput, aOutput1, aOutput2, aChance, false); + } + + @Deprecated + public static boolean addPulverisationRecipe(ItemStack aInput, ItemStack aOutput1, boolean aOverwrite) { + return addPulverisationRecipe(aInput, aOutput1, null, 0, aOverwrite); + } + + @Deprecated + public static boolean addPulverisationRecipe(ItemStack aInput, ItemStack aOutput1, ItemStack aOutput2, + boolean aOverwrite) { + return addPulverisationRecipe(aInput, aOutput1, aOutput2, 100, aOverwrite); + } + + @Deprecated + public static boolean addPulverisationRecipe(ItemStack aInput, ItemStack aOutput1, ItemStack aOutput2, int aChance, + boolean aOverwrite) { + return addPulverisationRecipe(aInput, aOutput1, aOutput2, aChance, null, 0, aOverwrite); + } + + /** + * Adds Several Pulverizer-Type Recipes. + */ + @Deprecated + public static boolean addPulverisationRecipe(ItemStack aInput, ItemStack aOutput1, ItemStack aOutput2, int aChance2, + ItemStack aOutput3, int aChance3, boolean aOverwrite) { + aOutput1 = GT_OreDictUnificator.get(true, aOutput1); + aOutput2 = GT_OreDictUnificator.get(true, aOutput2); + if (GT_Utility.isStackInvalid(aInput) || GT_Utility.isStackInvalid(aOutput1)) return false; + + if (GT_Utility.getContainerItem(aInput, false) == null) { + RA.addPulveriserRecipe( + aInput, + new ItemStack[] { aOutput1, aOutput2, aOutput3 }, + new int[] { 10000, aChance2 <= 0 ? 1000 : 100 * aChance2, aChance3 <= 0 ? 1000 : 100 * aChance3 }, + 400, + 2); + } + return true; + } + + @Deprecated + public static boolean addPulverisationRecipe(ItemStack aInputItem, ItemStack[] aOutputArray, int[] aChanceArray, + int aEUt, int aRecipeDurationInTicks) { + + ItemStack[] aUnifiedOutputArray = new ItemStack[aOutputArray.length]; + int counter = 0; + + for (ItemStack item : aOutputArray) { + aUnifiedOutputArray[counter] = GT_OreDictUnificator.get(true, item); + counter++; + } + + RA.addPulveriserRecipe(aInputItem, aOutputArray, aChanceArray, aRecipeDurationInTicks, aEUt); + + return true; + } + + @Deprecated + public static boolean addImmersiveEngineeringRecipe(ItemStack aInput, ItemStack aOutput1, ItemStack aOutput2, + int aChance2, ItemStack aOutput3, int aChance3) { + return true; + } + + @Deprecated + public static boolean addMagneticraftRecipe(ItemStack aInput, ItemStack aOutput1, ItemStack aOutput2, int aChance2, + ItemStack aOutput3, int aChance3) { + return true; + } + + /** + * Adds a Recipe to the Sawmills of ThermalCraft + */ + @Deprecated + public static boolean addSawmillRecipe(ItemStack aInput1, ItemStack aOutput1, ItemStack aOutput2) { + return true; + } + + /** + * Induction Smelter Recipes and Alloy Smelter Recipes + */ + @Deprecated + public static boolean addAlloySmelterRecipe(ItemStack aInput1, ItemStack aInput2, ItemStack aOutput1, int aDuration, + int aEUt, boolean aAllowSecondaryInputEmpty) { + if (aInput1 == null || (aInput2 == null && !aAllowSecondaryInputEmpty) || aOutput1 == null) return false; + aOutput1 = GT_OreDictUnificator.get(true, aOutput1); + RA.stdBuilder() + .itemInputs(aInput1, aInput2) + .itemOutputs(aOutput1) + .duration(aDuration) + .eut(aEUt) + .addTo(alloySmelterRecipes); + return true; + } + + /** + * Induction Smelter Recipes for TE + */ + public static boolean addInductionSmelterRecipe(ItemStack aInput1, ItemStack aInput2, ItemStack aOutput1, + ItemStack aOutput2, int aEnergy, int aChance) { + return true; + } + + /** + * Smelts Ores to Ingots + */ + public static boolean addOreToIngotSmeltingRecipe(ItemStack aInput, ItemStack aOutput) { + aOutput = GT_OreDictUnificator.get(true, aOutput); + if (aInput == null || aOutput == null) return false; + FurnaceRecipes.smelting() + .func_151394_a(aInput, GT_Utility.copyOrNull(aOutput), 0.0F); + return true; + } + + /** + * Adds GT versions of the IC2 recipes from the supplied IC2RecipeList. + */ + public static void addIC2RecipesToGT(Map<IRecipeInput, RecipeOutput> aIC2RecipeList, RecipeMap<?> aGTRecipeMap, + boolean aAddGTRecipe, boolean aRemoveIC2Recipe, boolean aExcludeGTIC2Items) { + Map<ItemStack, ItemStack> aRecipesToRemove = new HashMap<>(); + for (Entry<IRecipeInput, RecipeOutput> iRecipeInputRecipeOutputEntry : aIC2RecipeList.entrySet()) { + if (iRecipeInputRecipeOutputEntry.getValue().items.isEmpty()) { + continue; + } + + for (ItemStack tStack : (iRecipeInputRecipeOutputEntry.getKey()).getInputs()) { + if (!GT_Utility.isStackValid(tStack)) { + continue; + } + + if (aAddGTRecipe) { + try { + if (aExcludeGTIC2Items && ((tStack.getUnlocalizedName() + .contains("gt.metaitem.01") + || tStack.getUnlocalizedName() + .contains("gt.blockores") + || tStack.getUnlocalizedName() + .contains("ic2.itemCrushed") + || tStack.getUnlocalizedName() + .contains("ic2.itemPurifiedCrushed")))) + continue; + switch (aGTRecipeMap.unlocalizedName) { + case "gt.recipe.macerator", "gt.recipe.extractor", "gt.recipe.compressor" -> aGTRecipeMap + .addRecipe( + true, + new ItemStack[] { GT_Utility.copyAmount( + iRecipeInputRecipeOutputEntry.getKey() + .getAmount(), + tStack) }, + iRecipeInputRecipeOutputEntry.getValue().items.toArray(new ItemStack[0]), + null, + null, + null, + null, + 300, + 2, + 0); + case "gt.recipe.thermalcentrifuge" -> aGTRecipeMap.addRecipe( + true, + new ItemStack[] { GT_Utility.copyAmount( + iRecipeInputRecipeOutputEntry.getKey() + .getAmount(), + tStack) }, + iRecipeInputRecipeOutputEntry.getValue().items.toArray(new ItemStack[0]), + null, + null, + null, + null, + 500, + 48, + 0); + } + } catch (Exception e) { + System.err.println(e); + } + } + if (aRemoveIC2Recipe) { + aRecipesToRemove.put(tStack, iRecipeInputRecipeOutputEntry.getValue().items.get(0)); + } + + } + + } + GT_Utility.bulkRemoveSimpleIC2MachineRecipe(aRecipesToRemove, aIC2RecipeList); + } + + public static Map<IRecipeInput, RecipeOutput> getExtractorRecipeList() { + try { + return ic2.api.recipe.Recipes.extractor.getRecipes(); + } catch (Throwable e) { + /* Do nothing */ + } + return emptyRecipeMap; + } + + public static Map<IRecipeInput, RecipeOutput> getCompressorRecipeList() { + try { + return ic2.api.recipe.Recipes.compressor.getRecipes(); + } catch (Throwable e) { + /* Do nothing */ + } + return emptyRecipeMap; + } + + public static Map<IRecipeInput, RecipeOutput> getMaceratorRecipeList() { + try { + return ic2.api.recipe.Recipes.macerator.getRecipes(); + } catch (Throwable e) { + /* Do nothing */ + } + return emptyRecipeMap; + } + + public static Map<IRecipeInput, RecipeOutput> getThermalCentrifugeRecipeList() { + try { + return ic2.api.recipe.Recipes.centrifuge.getRecipes(); + } catch (Throwable e) { + /* Do nothing */ + } + return emptyRecipeMap; + } + + public static Map<IRecipeInput, RecipeOutput> getOreWashingRecipeList() { + try { + return ic2.api.recipe.Recipes.oreWashing.getRecipes(); + } catch (Throwable e) { + /* Do nothing */ + } + return emptyRecipeMap; + } + + public static Map<IRecipeInput, RecipeOutput> getMassFabricatorList() { + try { + return ic2.api.recipe.Recipes.matterAmplifier.getRecipes(); + } catch (Throwable e) { + /* Do nothing */ + } + return emptyRecipeMap; + } + + /** + * IC2-ThermalCentrifuge Recipe. Overloads old Recipes automatically + */ + @Deprecated + public static boolean addThermalCentrifugeRecipe(ItemStack aInput, int[] aChances, int aHeat, Object... aOutput) { + if (aInput == null || aOutput == null || aOutput.length == 0 || aOutput[0] == null) return false; + RA.addThermalCentrifugeRecipe( + aInput, + (ItemStack) aOutput[0], + aOutput.length >= 2 ? (ItemStack) aOutput[1] : null, + aOutput.length >= 3 ? (ItemStack) aOutput[2] : null, + aChances, + 500, + 48); + return true; + } + + @Deprecated + public static boolean addThermalCentrifugeRecipe(ItemStack aInput, int aHeat, Object... aOutput) { + if (aInput == null || aOutput == null || aOutput.length == 0 || aOutput[0] == null) return false; + RA.addThermalCentrifugeRecipe( + aInput, + (ItemStack) aOutput[0], + aOutput.length >= 2 ? (ItemStack) aOutput[1] : null, + aOutput.length >= 3 ? (ItemStack) aOutput[2] : null, + 500, + 48); + return true; + } + + /** + * IC2-OreWasher Recipe. Overloads old Recipes automatically + */ + public static boolean addOreWasherRecipe(ItemStack aInput, int[] aChances, int aWaterAmount, Object... aOutput) { + if (aInput == null || aOutput == null || aOutput.length == 0 || aOutput[0] == null) return false; + RA.stdBuilder() + .itemInputs(aInput) + .itemOutputs((ItemStack) aOutput[0], (ItemStack) aOutput[1], (ItemStack) aOutput[2]) + .outputChances(aChances) + .fluidInputs(GT_ModHandler.getWater(aWaterAmount)) + .duration(25 * SECONDS) + .eut(16) + .addTo(oreWasherRecipes); + + RA.stdBuilder() + .itemInputs(aInput) + .itemOutputs((ItemStack) aOutput[0], (ItemStack) aOutput[1], (ItemStack) aOutput[2]) + .outputChances(aChances) + .fluidInputs(GT_ModHandler.getDistilledWater(aWaterAmount / 5)) + .duration(15 * SECONDS) + .eut(16) + .addTo(oreWasherRecipes); + return true; + } + + public static boolean addOreWasherRecipe(ItemStack aInput, int aWaterAmount, Object... aOutput) { + if (aInput == null || aOutput == null || aOutput.length == 0 || aOutput[0] == null) return false; + RA.stdBuilder() + .itemInputs(aInput) + .itemOutputs((ItemStack) aOutput[0], (ItemStack) aOutput[1], (ItemStack) aOutput[2]) + .fluidInputs(GT_ModHandler.getWater(aWaterAmount)) + .duration(25 * SECONDS) + .eut(16) + .addTo(oreWasherRecipes); + + RA.stdBuilder() + .itemInputs(aInput) + .itemOutputs((ItemStack) aOutput[0], (ItemStack) aOutput[1], (ItemStack) aOutput[2]) + .fluidInputs(GT_ModHandler.getDistilledWater(aWaterAmount / 5)) + .duration(15 * SECONDS) + .eut(16) + .addTo(oreWasherRecipes); + return true; + } + + /** + * IC2-Compressor Recipe. Overloads old Recipes automatically + */ + @Deprecated + public static boolean addCompressionRecipe(ItemStack aInput, ItemStack aOutput) { + return addCompressionRecipe(aInput, aOutput, 300, 2); + } + + /** + * IC2-Compressor Recipe. Overloads old Recipes automatically + */ + @Deprecated + public static boolean addCompressionRecipe(ItemStack aInput, ItemStack aOutput, int duration, int EUPerTick) { + aOutput = GT_OreDictUnificator.get(true, aOutput); + if (aInput == null || aOutput == null || GT_Utility.areStacksEqual(aInput, aOutput, true)) return false; + RA.addCompressorRecipe(aInput, aOutput, duration, EUPerTick); + return true; + } + + /** + * @param aValue Scrap = 5000, Scrapbox = 45000, Diamond Dust 125000 + */ + public static boolean addIC2MatterAmplifier(ItemStack aAmplifier, int aValue) { + if (aAmplifier == null || aValue <= 0) return false; + try { + NBTTagCompound tNBT = new NBTTagCompound(); + tNBT.setInteger("amplification", aValue); + GT_Utility + .callMethod(ic2.api.recipe.Recipes.matterAmplifier, "addRecipe", false, false, false, aAmplifier, tNBT); + } catch (Throwable e) { + /* Do nothing */ + } + return true; + } + + /** + * Rolling Machine Crafting Recipe + */ + public static boolean addRollingMachineRecipe(ItemStack aResult, Object[] aRecipe) { + aResult = GT_OreDictUnificator.get(true, aResult); + if (aResult == null || aRecipe == null || aResult.stackSize <= 0) return false; + try { + mods.railcraft.api.crafting.RailcraftCraftingManager.rollingMachine.getRecipeList() + .add(new ShapedOreRecipe(GT_Utility.copyOrNull(aResult), aRecipe)); + } catch (Throwable e) { + return addCraftingRecipe(GT_Utility.copyOrNull(aResult), aRecipe); + } + return true; + } + + public static void stopBufferingCraftingRecipes() { + sBufferCraftingRecipes = false; + + bulkRemoveRecipeByOutput(delayedRemovalByOutput); + bulkRemoveByRecipe(delayedRemovalByRecipe); + sBufferRecipeList.forEach(GameRegistry::addRecipe); + + delayedRemovalByOutput.clear(); + delayedRemovalByRecipe.clear(); + sBufferRecipeList.clear(); + } + + /** + * Shapeless Crafting Recipes. Deletes conflicting Recipes too. + */ + public static boolean addCraftingRecipe(ItemStack aResult, Enchantment[] aEnchantmentsAdded, + int[] aEnchantmentLevelsAdded, Object[] aRecipe) { + return addCraftingRecipe( + aResult, + aEnchantmentsAdded, + aEnchantmentLevelsAdded, + false, + true, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + true, + aRecipe); + } + + /** + * Regular Crafting Recipes. Deletes conflicting Recipes too. + * <p/> + * You can insert instances of IItemContainer into the Recipe Input Array directly without having to call "get(1)" + * on them. + * <p/> + * Enums are automatically getting their "name()"-Method called in order to deliver an OreDict String. + * <p/> + * Lowercase Letters are reserved for Tools. They are as follows: + * <p/> + * 'b' ToolDictNames.craftingToolBlade 'c' ToolDictNames.craftingToolCrowbar, 'd' + * ToolDictNames.craftingToolScrewdriver, 'f' ToolDictNames.craftingToolFile, 'h' + * ToolDictNames.craftingToolHardHammer, 'i' ToolDictNames.craftingToolSolderingIron, 'j' + * ToolDictNames.craftingToolSolderingMetal, 'k' ToolDictNames.craftingToolKnive 'm' + * ToolDictNames.craftingToolMortar, 'p' ToolDictNames.craftingToolDrawplate, 'r' + * ToolDictNames.craftingToolSoftHammer, 's' ToolDictNames.craftingToolSaw, 'w' ToolDictNames.craftingToolWrench, + * 'x' ToolDictNames.craftingToolWireCutter, + */ + public static boolean addCraftingRecipe(ItemStack aResult, Object[] aRecipe) { + return addCraftingRecipe(aResult, 0, aRecipe); + } + + /** + * Regular Crafting Recipes. Deletes conflicting Recipes too. + * <p/> + * You can insert instances of IItemContainer into the Recipe Input Array directly without having to call "get(1)" + * on them. + * <p/> + * Enums are automatically getting their "name()"-Method called in order to deliver an OreDict String. + * <p/> + * Lowercase Letters are reserved for Tools. They are as follows: + * <p/> + * 'b' ToolDictNames.craftingToolBlade 'c' ToolDictNames.craftingToolCrowbar, 'd' + * ToolDictNames.craftingToolScrewdriver, 'f' ToolDictNames.craftingToolFile, 'h' + * ToolDictNames.craftingToolHardHammer, 'i' ToolDictNames.craftingToolSolderingIron, 'j' + * ToolDictNames.craftingToolSolderingMetal, 'k' ToolDictNames.craftingToolKnive 'm' + * ToolDictNames.craftingToolMortar, 'p' ToolDictNames.craftingToolDrawplate, 'r' + * ToolDictNames.craftingToolSoftHammer, 's' ToolDictNames.craftingToolSaw, 'w' ToolDictNames.craftingToolWrench, + * 'x' ToolDictNames.craftingToolWireCutter, + */ + public static boolean addCraftingRecipe(ItemStack aResult, long aBitMask, Object[] aRecipe) { + return addCraftingRecipe( + aResult, + new Enchantment[0], + new int[0], + (aBitMask & RecipeBits.MIRRORED) != 0, + (aBitMask & RecipeBits.BUFFERED) != 0, + (aBitMask & RecipeBits.KEEPNBT) != 0, + (aBitMask & RecipeBits.DISMANTLEABLE) != 0, + (aBitMask & RecipeBits.NOT_REMOVABLE) == 0, + (aBitMask & RecipeBits.REVERSIBLE) != 0, + (aBitMask & RecipeBits.DELETE_ALL_OTHER_RECIPES) != 0, + (aBitMask & RecipeBits.DELETE_ALL_OTHER_RECIPES_IF_SAME_NBT) != 0, + (aBitMask & RecipeBits.DELETE_ALL_OTHER_SHAPED_RECIPES) != 0, + (aBitMask & RecipeBits.DELETE_ALL_OTHER_NATIVE_RECIPES) != 0, + (aBitMask & RecipeBits.DO_NOT_CHECK_FOR_COLLISIONS) == 0, + (aBitMask & RecipeBits.ONLY_ADD_IF_THERE_IS_ANOTHER_RECIPE_FOR_IT) != 0, + (aBitMask & RecipeBits.ONLY_ADD_IF_RESULT_IS_NOT_NULL) != 0, + aRecipe); + } + + /** + * Internal realisation of the Crafting Recipe adding Process. + */ + private static boolean addCraftingRecipe(ItemStack aResult, Enchantment[] aEnchantmentsAdded, + int[] aEnchantmentLevelsAdded, boolean aMirrored, boolean aBuffered, boolean aKeepNBT, boolean aDismantleable, + boolean aRemovable, boolean aReversible, boolean aRemoveAllOthersWithSameOutput, + boolean aRemoveAllOthersWithSameOutputIfTheyHaveSameNBT, boolean aRemoveAllOtherShapedsWithSameOutput, + boolean aRemoveAllOtherNativeRecipes, boolean aCheckForCollisions, + boolean aOnlyAddIfThereIsAnyRecipeOutputtingThis, boolean aOnlyAddIfResultIsNotNull, Object[] aRecipe) { + + aResult = GT_OreDictUnificator.get(true, aResult); + if (aOnlyAddIfResultIsNotNull && aResult == null) return false; + if (aResult != null && Items.feather.getDamage(aResult) == W) Items.feather.setDamage(aResult, 0); + if (aRecipe == null || aRecipe.length == 0) return false; + + // The renamed variable clarifies what's happening + // noinspection UnnecessaryLocalVariable + boolean tDoWeCareIfThereWasARecipe = aOnlyAddIfThereIsAnyRecipeOutputtingThis; + boolean tThereWasARecipe = false; + + for (byte i = 0; i < aRecipe.length; i++) { + if (aRecipe[i] instanceof IItemContainer) aRecipe[i] = ((IItemContainer) aRecipe[i]).get(1); + else if (aRecipe[i] instanceof Enum) aRecipe[i] = ((Enum<?>) aRecipe[i]).name(); + else if (!(aRecipe[i] == null || aRecipe[i] instanceof ItemStack + || aRecipe[i] instanceof ItemData + || aRecipe[i] instanceof String + || aRecipe[i] instanceof Character)) aRecipe[i] = aRecipe[i].toString(); + } + + try { + StringBuilder shape = new StringBuilder(E); + int idx = 0; + if (aRecipe[idx] instanceof Boolean) { + throw new IllegalArgumentException(); + } + + ArrayList<Object> tRecipeList = new ArrayList<>(Arrays.asList(aRecipe)); + + while (aRecipe[idx] instanceof String) { + StringBuilder s = new StringBuilder((String) aRecipe[idx++]); + shape.append(s); + while (s.length() < 3) s.append(" "); + if (s.length() > 3) throw new IllegalArgumentException(); + + for (char c : s.toString() + .toCharArray()) { + switch (c) { + case 'b' -> { + tRecipeList.add(c); + tRecipeList.add(ToolDictNames.craftingToolBlade.name()); + } + case 'c' -> { + tRecipeList.add(c); + tRecipeList.add(ToolDictNames.craftingToolCrowbar.name()); + } + case 'd' -> { + tRecipeList.add(c); + tRecipeList.add(ToolDictNames.craftingToolScrewdriver.name()); + } + case 'f' -> { + tRecipeList.add(c); + tRecipeList.add(ToolDictNames.craftingToolFile.name()); + } + case 'h' -> { + tRecipeList.add(c); + tRecipeList.add(ToolDictNames.craftingToolHardHammer.name()); + } + case 'i' -> { + tRecipeList.add(c); + tRecipeList.add(ToolDictNames.craftingToolSolderingIron.name()); + } + case 'j' -> { + tRecipeList.add(c); + tRecipeList.add(ToolDictNames.craftingToolSolderingMetal.name()); + } + case 'k' -> { + tRecipeList.add(c); + tRecipeList.add(ToolDictNames.craftingToolKnife.name()); + } + case 'm' -> { + tRecipeList.add(c); + tRecipeList.add(ToolDictNames.craftingToolMortar.name()); + } + case 'p' -> { + tRecipeList.add(c); + tRecipeList.add(ToolDictNames.craftingToolDrawplate.name()); + } + case 'r' -> { + tRecipeList.add(c); + tRecipeList.add(ToolDictNames.craftingToolSoftHammer.name()); + } + case 's' -> { + tRecipeList.add(c); + tRecipeList.add(ToolDictNames.craftingToolSaw.name()); + } + case 'w' -> { + tRecipeList.add(c); + tRecipeList.add(ToolDictNames.craftingToolWrench.name()); + } + case 'x' -> { + tRecipeList.add(c); + tRecipeList.add(ToolDictNames.craftingToolWireCutter.name()); + } + } + } + } + + aRecipe = tRecipeList.toArray(); + + if (aRecipe[idx] instanceof Boolean) { + idx++; + } + Map<Character, ItemStack> tItemStackMap = new HashMap<>(); + Map<Character, ItemData> tItemDataMap = new HashMap<>(); + tItemStackMap.put(' ', null); + + boolean tRemoveRecipe = true; + + for (; idx < aRecipe.length; idx += 2) { + if (aRecipe[idx] == null || aRecipe[idx + 1] == null) { + if (D1) { + GT_Log.err.println( + "WARNING: Missing Item for shaped Recipe: " + + (aResult == null ? "null" : aResult.getDisplayName())); + for (Object tContent : aRecipe) GT_Log.err.println(tContent); + } + return false; + } + Character chr = (Character) aRecipe[idx]; + Object in = aRecipe[idx + 1]; + if (in instanceof ItemStack is) { + tItemStackMap.put(chr, GT_Utility.copyOrNull(is)); + tItemDataMap.put(chr, GT_OreDictUnificator.getItemData(is)); + } else if (in instanceof ItemData) { + String tString = in.toString(); + switch (tString) { + case "plankWood" -> tItemDataMap.put(chr, new ItemData(Materials.Wood, M)); + case "stoneNetherrack" -> tItemDataMap.put(chr, new ItemData(Materials.Netherrack, M)); + case "stoneObsidian" -> tItemDataMap.put(chr, new ItemData(Materials.Obsidian, M)); + case "stoneEndstone" -> tItemDataMap.put(chr, new ItemData(Materials.Endstone, M)); + default -> tItemDataMap.put(chr, (ItemData) in); + } + ItemStack tStack = GT_OreDictUnificator.getFirstOre(in, 1); + if (tStack == null) tRemoveRecipe = false; + else tItemStackMap.put(chr, tStack); + in = aRecipe[idx + 1] = in.toString(); + } else if (in instanceof String) { + if (in.equals(OreDictNames.craftingChest.toString())) + tItemDataMap.put(chr, new ItemData(Materials.Wood, M * 8)); + else if (in.equals(OreDictNames.craftingBook.toString())) + tItemDataMap.put(chr, new ItemData(Materials.Paper, M * 3)); + else if (in.equals(OreDictNames.craftingPiston.toString())) + tItemDataMap.put(chr, new ItemData(Materials.Stone, M * 4, Materials.Wood, M * 3)); + else if (in.equals(OreDictNames.craftingFurnace.toString())) + tItemDataMap.put(chr, new ItemData(Materials.Stone, M * 8)); + else if (in.equals(OreDictNames.craftingIndustrialDiamond.toString())) + tItemDataMap.put(chr, new ItemData(Materials.Diamond, M)); + else if (in.equals(OreDictNames.craftingAnvil.toString())) + tItemDataMap.put(chr, new ItemData(Materials.Iron, M * 10)); + ItemStack tStack = GT_OreDictUnificator.getFirstOre(in, 1); + if (tStack == null) tRemoveRecipe = false; + else tItemStackMap.put(chr, tStack); + } else { + throw new IllegalArgumentException(); + } + } + + if (aReversible && aResult != null) { + ItemData[] tData = new ItemData[9]; + int x = -1; + for (char chr : shape.toString() + .toCharArray()) tData[++x] = tItemDataMap.get(chr); + if (GT_Utility.arrayContainsNonNull(tData)) + GT_OreDictUnificator.addItemData(aResult, new ItemData(tData)); + } + + if (aCheckForCollisions && tRemoveRecipe) { + ItemStack[] tRecipe = new ItemStack[9]; + int x = -1; + for (char chr : shape.toString() + .toCharArray()) { + tRecipe[++x] = tItemStackMap.get(chr); + if (tRecipe[x] != null && Items.feather.getDamage(tRecipe[x]) == W) + Items.feather.setDamage(tRecipe[x], 0); + } + if (tDoWeCareIfThereWasARecipe || !aBuffered) tThereWasARecipe = removeRecipe(tRecipe) != null; + else removeRecipeDelayed(tRecipe); + } + } catch (Throwable e) { + e.printStackTrace(GT_Log.err); + } + + if (aResult == null || aResult.stackSize <= 0) return false; + + if (aRemoveAllOthersWithSameOutput || aRemoveAllOthersWithSameOutputIfTheyHaveSameNBT + || aRemoveAllOtherShapedsWithSameOutput + || aRemoveAllOtherNativeRecipes) { + if (tDoWeCareIfThereWasARecipe || !aBuffered) tThereWasARecipe = removeRecipeByOutput( + aResult, + !aRemoveAllOthersWithSameOutputIfTheyHaveSameNBT, + aRemoveAllOtherShapedsWithSameOutput, + aRemoveAllOtherNativeRecipes) || tThereWasARecipe; + else removeRecipeByOutputDelayed(aResult); + } + + if (aOnlyAddIfThereIsAnyRecipeOutputtingThis && !tDoWeCareIfThereWasARecipe && !tThereWasARecipe) { + ArrayList<IRecipe> tList = (ArrayList<IRecipe>) CraftingManager.getInstance() + .getRecipeList(); + int tList_sS = tList.size(); + for (int i = 0; i < tList_sS && !tThereWasARecipe; i++) { + IRecipe tRecipe = tList.get(i); + if (sSpecialRecipeClasses.contains( + tRecipe.getClass() + .getName())) + continue; + if (GT_Utility.areStacksEqual(GT_OreDictUnificator.get(tRecipe.getRecipeOutput()), aResult, true)) { + tList.remove(i--); + tList_sS = tList.size(); + tThereWasARecipe = true; + } + } + } + + if (Items.feather.getDamage(aResult) == W || Items.feather.getDamage(aResult) < 0) + Items.feather.setDamage(aResult, 0); + + GT_Utility.updateItemStack(aResult); + + if (tThereWasARecipe || !aOnlyAddIfThereIsAnyRecipeOutputtingThis) { + if (sBufferCraftingRecipes && aBuffered) sBufferRecipeList.add( + new GT_Shaped_Recipe( + GT_Utility.copyOrNull(aResult), + aDismantleable, + aRemovable, + aKeepNBT, + aEnchantmentsAdded, + aEnchantmentLevelsAdded, + aRecipe).setMirrored(aMirrored)); + else GameRegistry.addRecipe( + new GT_Shaped_Recipe( + GT_Utility.copyOrNull(aResult), + aDismantleable, + aRemovable, + aKeepNBT, + aEnchantmentsAdded, + aEnchantmentLevelsAdded, + aRecipe).setMirrored(aMirrored)); + } + return true; + } + + /** + * Shapeless Crafting Recipes. Deletes conflicting Recipes too. + */ + public static boolean addShapelessEnchantingRecipe(ItemStack aResult, Enchantment[] aEnchantmentsAdded, + int[] aEnchantmentLevelsAdded, Object[] aRecipe) { + return addShapelessCraftingRecipe( + aResult, + aEnchantmentsAdded, + aEnchantmentLevelsAdded, + true, + false, + false, + false, + aRecipe); + } + + /** + * Shapeless Crafting Recipes. Deletes conflicting Recipes too. + */ + public static boolean addShapelessCraftingRecipe(ItemStack aResult, Object[] aRecipe) { + return addShapelessCraftingRecipe( + aResult, + RecipeBits.DO_NOT_CHECK_FOR_COLLISIONS | RecipeBits.BUFFERED, + aRecipe); + } + + /** + * Shapeless Crafting Recipes. Deletes conflicting Recipes too. + */ + public static boolean addShapelessCraftingRecipe(ItemStack aResult, long aBitMask, Object[] aRecipe) { + return addShapelessCraftingRecipe( + aResult, + new Enchantment[0], + new int[0], + (aBitMask & RecipeBits.BUFFERED) != 0, + (aBitMask & RecipeBits.KEEPNBT) != 0, + (aBitMask & RecipeBits.DISMANTLEABLE) != 0, + (aBitMask & RecipeBits.NOT_REMOVABLE) == 0, + aRecipe); + } + + /** + * Shapeless Crafting Recipes. Deletes conflicting Recipes too. + */ + private static boolean addShapelessCraftingRecipe(ItemStack aResult, Enchantment[] aEnchantmentsAdded, + int[] aEnchantmentLevelsAdded, boolean aBuffered, boolean aKeepNBT, boolean aDismantleable, boolean aRemovable, + Object[] aRecipe) { + aResult = GT_OreDictUnificator.get(true, aResult); + if (aRecipe == null || aRecipe.length == 0) return false; + for (byte i = 0; i < aRecipe.length; i++) { + if (aRecipe[i] instanceof IItemContainer) aRecipe[i] = ((IItemContainer) aRecipe[i]).get(1); + else if (aRecipe[i] instanceof Enum) aRecipe[i] = ((Enum<?>) aRecipe[i]).name(); + else if (!(aRecipe[i] == null || aRecipe[i] instanceof ItemStack + || aRecipe[i] instanceof String + || aRecipe[i] instanceof Character)) aRecipe[i] = aRecipe[i].toString(); + } + try { + ItemStack[] tRecipe = new ItemStack[9]; + int i = 0; + for (Object tObject : aRecipe) { + if (tObject == null) { + if (D1) GT_Log.err.println( + "WARNING: Missing Item for shapeless Recipe: " + + (aResult == null ? "null" : aResult.getDisplayName())); + for (Object tContent : aRecipe) GT_Log.err.println(tContent); + return false; + } + if (tObject instanceof ItemStack) { + tRecipe[i] = (ItemStack) tObject; + } else if (tObject instanceof String) { + tRecipe[i] = GT_OreDictUnificator.getFirstOre(tObject, 1); + if (tRecipe[i] == null) break; + } + i++; + } + if (sBufferCraftingRecipes && aBuffered) removeRecipeDelayed(tRecipe); + else removeRecipe(tRecipe); + } catch (Throwable e) { + e.printStackTrace(GT_Log.err); + } + + if (aResult == null || aResult.stackSize <= 0) return false; + + if (Items.feather.getDamage(aResult) == W || Items.feather.getDamage(aResult) < 0) + Items.feather.setDamage(aResult, 0); + + GT_Utility.updateItemStack(aResult); + + if (sBufferCraftingRecipes && aBuffered) sBufferRecipeList.add( + new GT_Shapeless_Recipe( + GT_Utility.copyOrNull(aResult), + aDismantleable, + aRemovable, + aKeepNBT, + aEnchantmentsAdded, + aEnchantmentLevelsAdded, + aRecipe)); + else GameRegistry.addRecipe( + new GT_Shapeless_Recipe( + GT_Utility.copyOrNull(aResult), + aDismantleable, + aRemovable, + aKeepNBT, + aEnchantmentsAdded, + aEnchantmentLevelsAdded, + aRecipe)); + return true; + } + + /** + * Removes a Smelting Recipe + */ + public static boolean removeFurnaceSmelting(ItemStack aInput) { + if (aInput != null) { + for (ItemStack tInput : FurnaceRecipes.smelting() + .getSmeltingList() + .keySet()) { + if (GT_Utility.isStackValid(tInput) && GT_Utility.areStacksEqual(aInput, tInput, true)) { + FurnaceRecipes.smelting() + .getSmeltingList() + .remove(tInput); + return true; + } + } + } + return false; + } + + /** + * Removes all matching Smelting Recipes by output + */ + public static boolean removeFurnaceSmeltingByOutput(ItemStack aOutput) { + if (aOutput != null) { + return FurnaceRecipes.smelting() + .getSmeltingList() + .values() + .removeIf( + tOutput -> GT_Utility.isStackValid(tOutput) && GT_Utility.areStacksEqual(aOutput, tOutput, true)); + } + return false; + } + + /** + * Removes a Crafting Recipe and gives you the former output of it. + * + * @param aRecipe The content of the Crafting Grid as ItemStackArray with length 9 + * @return the output of the old Recipe or null if there was nothing. + */ + public static ItemStack removeRecipe(ItemStack... aRecipe) { + if (aRecipe == null) return null; + if (Arrays.stream(aRecipe) + .noneMatch(Objects::nonNull)) return null; + + ItemStack rReturn = null; + InventoryCrafting aCrafting = new InventoryCrafting(new Container() { + + @Override + public boolean canInteractWith(EntityPlayer player) { + return false; + } + }, 3, 3); + for (int i = 0; i < aRecipe.length && i < 9; i++) aCrafting.setInventorySlotContents(i, aRecipe[i]); + ArrayList<IRecipe> tList = (ArrayList<IRecipe>) CraftingManager.getInstance() + .getRecipeList(); + int tList_sS = tList.size(); + try { + for (int i = 0; i < tList_sS; i++) { + for (; i < tList_sS; i++) { + if ((!(tList.get(i) instanceof IGT_CraftingRecipe) + || ((IGT_CraftingRecipe) tList.get(i)).isRemovable()) && tList.get(i) + .matches(aCrafting, DW)) { + rReturn = tList.get(i) + .getCraftingResult(aCrafting); + if (rReturn != null) tList.remove(i--); + tList_sS = tList.size(); + } + } + } + } catch (Throwable e) { + e.printStackTrace(GT_Log.err); + } + return rReturn; + } + + public static void removeRecipeDelayed(ItemStack... aRecipe) { + if (!sBufferCraftingRecipes) { + removeRecipe(aRecipe); + return; + } + + if (aRecipe == null) return; + if (Arrays.stream(aRecipe) + .noneMatch(Objects::nonNull)) return; + + InventoryCrafting aCrafting = new InventoryCrafting(new Container() { + + @Override + public boolean canInteractWith(EntityPlayer player) { + return false; + } + }, 3, 3); + for (int i = 0; i < aRecipe.length && i < 9; i++) aCrafting.setInventorySlotContents(i, aRecipe[i]); + delayedRemovalByRecipe.add(aCrafting); + } + + public static void bulkRemoveByRecipe(List<InventoryCrafting> toRemove) { + ArrayList<IRecipe> tList = (ArrayList<IRecipe>) CraftingManager.getInstance() + .getRecipeList(); + GT_FML_LOGGER.info("BulkRemoveByRecipe: tList: " + tList.size() + " toRemove: " + toRemove.size()); + + Set<IRecipe> tListToRemove = tList.parallelStream() + .filter(tRecipe -> { + if ((tRecipe instanceof IGT_CraftingRecipe) && !((IGT_CraftingRecipe) tRecipe).isRemovable()) + return false; + return toRemove.stream() + .anyMatch(aCrafting -> tRecipe.matches(aCrafting, DW)); + }) + .collect(Collectors.toSet()); + + tList.removeIf(tListToRemove::contains); + } + + public static boolean removeRecipeByOutputDelayed(ItemStack aOutput) { + if (sBufferCraftingRecipes) return delayedRemovalByOutput.add(aOutput); + else return removeRecipeByOutput(aOutput); + } + + public static boolean removeRecipeByOutputDelayed(ItemStack aOutput, boolean aIgnoreNBT, + boolean aNotRemoveShapelessRecipes, boolean aOnlyRemoveNativeHandlers) { + if (sBufferCraftingRecipes && (aIgnoreNBT && !aNotRemoveShapelessRecipes && !aOnlyRemoveNativeHandlers)) + // Too lazy to handle deferred versions of the parameters that aren't used very often + return delayedRemovalByOutput.add(aOutput); + else return removeRecipeByOutput(aOutput, aIgnoreNBT, aNotRemoveShapelessRecipes, aOnlyRemoveNativeHandlers); + } + + public static boolean removeRecipeByOutput(ItemStack aOutput) { + return removeRecipeByOutput(aOutput, true, false, false); + } + + /** + * Removes a Crafting Recipe. + * + * @param aOutput The output of the Recipe. + * @return if it has removed at least one Recipe. + */ + public static boolean removeRecipeByOutput(ItemStack aOutput, boolean aIgnoreNBT, + boolean aNotRemoveShapelessRecipes, boolean aOnlyRemoveNativeHandlers) { + if (aOutput == null) return false; + boolean rReturn = false; + final ArrayList<IRecipe> tList = (ArrayList<IRecipe>) CraftingManager.getInstance() + .getRecipeList(); + aOutput = GT_OreDictUnificator.get(aOutput); + int tList_sS = tList.size(); + for (int i = 0; i < tList_sS; i++) { + final IRecipe tRecipe = tList.get(i); + if (aNotRemoveShapelessRecipes + && (tRecipe instanceof ShapelessRecipes || tRecipe instanceof ShapelessOreRecipe)) continue; + if (aOnlyRemoveNativeHandlers) { + if (!sNativeRecipeClasses.contains( + tRecipe.getClass() + .getName())) + continue; + } else { + if (sSpecialRecipeClasses.contains( + tRecipe.getClass() + .getName())) + continue; + } + ItemStack tStack = tRecipe.getRecipeOutput(); + if ((!(tRecipe instanceof IGT_CraftingRecipe) || ((IGT_CraftingRecipe) tRecipe).isRemovable()) + && GT_Utility.areStacksEqual(GT_OreDictUnificator.get(tStack), aOutput, aIgnoreNBT)) { + tList.remove(i--); + tList_sS = tList.size(); + rReturn = true; + } + } + return rReturn; + } + + public static boolean bulkRemoveRecipeByOutput(List<ItemStack> toRemove) { + ArrayList<IRecipe> tList = (ArrayList<IRecipe>) CraftingManager.getInstance() + .getRecipeList(); + + Set<ItemStack> setToRemove = toRemove.parallelStream() + .map(GT_OreDictUnificator::get_nocopy) + .collect(Collectors.toSet()); + + GT_FML_LOGGER.info("BulkRemoveRecipeByOutput: tList: " + tList.size() + " setToRemove: " + setToRemove.size()); + + Set<IRecipe> tListToRemove = tList.parallelStream() + .filter(tRecipe -> { + if ((tRecipe instanceof IGT_CraftingRecipe) && !((IGT_CraftingRecipe) tRecipe).isRemovable()) + return false; + if (sSpecialRecipeClasses.contains( + tRecipe.getClass() + .getName())) + return false; + final ItemStack tStack = GT_OreDictUnificator.get_nocopy(tRecipe.getRecipeOutput()); + return setToRemove.stream() + .anyMatch(aOutput -> GT_Utility.areStacksEqual(tStack, aOutput, true)); + }) + .collect(Collectors.toSet()); + + tList.removeIf(tListToRemove::contains); + return true; + } + + /** + * Checks all Crafting Handlers for Recipe Output Used for the Autocrafting Table + */ + public static ItemStack getAllRecipeOutput(World aWorld, ItemStack... aRecipe) { + if (aRecipe == null || aRecipe.length == 0) return null; + + if (aWorld == null) aWorld = DW; + + boolean temp = false; + for (ItemStack itemStack : aRecipe) { + if (itemStack != null) { + temp = true; + break; + } + } + if (!temp) return null; + InventoryCrafting aCrafting = new InventoryCrafting(new Container() { + + @Override + public boolean canInteractWith(EntityPlayer player) { + return false; + } + }, 3, 3); + for (int i = 0; i < 9 && i < aRecipe.length; i++) aCrafting.setInventorySlotContents(i, aRecipe[i]); + List<IRecipe> tList = CraftingManager.getInstance() + .getRecipeList(); + synchronized (sAllRecipeList) { + if (sAllRecipeList.size() != tList.size()) { + sAllRecipeList.clear(); + sAllRecipeList.addAll(tList); + } + for (int i = 0, j = sAllRecipeList.size(); i < j; i++) { + IRecipe tRecipe = sAllRecipeList.get(i); + if (tRecipe.matches(aCrafting, aWorld)) { + if (i > 10) { + sAllRecipeList.remove(i); + sAllRecipeList.add(i - 10, tRecipe); + } + return tRecipe.getCraftingResult(aCrafting); + } + } + } + + int tIndex = 0; + ItemStack tStack1 = null, tStack2 = null; + for (int i = 0, j = aCrafting.getSizeInventory(); i < j; i++) { + ItemStack tStack = aCrafting.getStackInSlot(i); + if (tStack != null) { + if (tIndex == 0) tStack1 = tStack; + if (tIndex == 1) tStack2 = tStack; + tIndex++; + } + } + + if (tIndex == 2 && tStack2 != null) { + if (tStack1.getItem() == tStack2.getItem() && tStack1.stackSize == 1 + && tStack2.stackSize == 1 + && tStack1.getItem() + .isRepairable()) { + int tNewDamage = tStack1.getMaxDamage() + tStack1.getItemDamage() + - tStack2.getItemDamage() + + tStack1.getMaxDamage() / 20; + return new ItemStack(tStack1.getItem(), 1, Math.max(tNewDamage, 0)); + } + } + + return null; + } + + /** + * Gives you a copy of the Output from a Crafting Recipe Used for Recipe Detection. + */ + public static ItemStack getRecipeOutput(ItemStack... aRecipe) { + return getRecipeOutput(false, true, aRecipe); + } + + public static ItemStack getRecipeOutputNoOreDict(ItemStack... aRecipe) { + return getRecipeOutput(false, false, aRecipe); + } + + public static ItemStack getRecipeOutput(boolean aUncopiedStack, ItemStack... aRecipe) { + return getRecipeOutput(aUncopiedStack, true, aRecipe); + } + + /** + * Gives you a copy of the Output from a Crafting Recipe Used for Recipe Detection. + */ + public static ItemStack getRecipeOutput(boolean aUncopiedStack, boolean allowOreDict, ItemStack... aRecipe) { + if (aRecipe == null || Arrays.stream(aRecipe) + .noneMatch(Objects::nonNull)) return null; + + InventoryCrafting aCrafting = new InventoryCrafting(new Container() { + + @Override + public boolean canInteractWith(EntityPlayer player) { + return false; + } + }, 3, 3); + for (int i = 0; i < 9 && i < aRecipe.length; i++) aCrafting.setInventorySlotContents(i, aRecipe[i]); + ArrayList<IRecipe> tList = (ArrayList<IRecipe>) CraftingManager.getInstance() + .getRecipeList(); + boolean found = false; + + for (IRecipe iRecipe : tList) { + found = false; + if (!allowOreDict && iRecipe instanceof ShapedOreRecipe) continue; + + try { + found = iRecipe.matches(aCrafting, DW); + } catch (Throwable e) { + e.printStackTrace(GT_Log.err); + } + if (found) { + ItemStack tOutput = aUncopiedStack ? iRecipe.getRecipeOutput() : iRecipe.getCraftingResult(aCrafting); + if (tOutput == null || tOutput.stackSize <= 0) { + // Seriously, who would ever do that shit? + if (!GregTech_API.sPostloadFinished) throw new GT_ItsNotMyFaultException( + "Seems another Mod added a Crafting Recipe with null Output. Tell the Developer of said Mod to fix that."); + } else { + if (aUncopiedStack) return tOutput; + return GT_Utility.copyOrNull(tOutput); + } + } + } + return null; + } + + /** + * Gives you a list of the Outputs from a Crafting Recipe If you have multiple Mods, which add Bronze Armor for + * example This also removes old Recipes from the List. + */ + public static List<ItemStack> getVanillyToolRecipeOutputs(ItemStack... aRecipe) { + if (!GregTech_API.sPostloadStarted || GregTech_API.sPostloadFinished) + sSingleNonBlockDamagableRecipeList.clear(); + if (sSingleNonBlockDamagableRecipeList.isEmpty()) { + for (IRecipe tRecipe : CraftingManager.getInstance() + .getRecipeList()) { + ItemStack tStack = tRecipe.getRecipeOutput(); + if (GT_Utility.isStackValid(tStack) && tStack.getMaxStackSize() == 1 + && tStack.getMaxDamage() > 0 + && !(tStack.getItem() instanceof ItemBlock) + && !(tStack.getItem() instanceof IReactorComponent) + && !isElectricItem(tStack) + && !GT_Utility.isStackInList(tStack, sNonReplaceableItems)) { + if (!(tRecipe instanceof ShapelessRecipes || tRecipe instanceof ShapelessOreRecipe)) { + if (tRecipe instanceof ShapedOreRecipe) { + boolean temp = true; + for (Object tObject : ((ShapedOreRecipe) tRecipe).getInput()) if (tObject != null) { + if (tObject instanceof ItemStack && (((ItemStack) tObject).getItem() == null + || ((ItemStack) tObject).getMaxStackSize() < 2 + || ((ItemStack) tObject).getMaxDamage() > 0 + || ((ItemStack) tObject).getItem() instanceof ItemBlock)) { + temp = false; + break; + } + if (tObject instanceof List && ((List<?>) tObject).isEmpty()) { + temp = false; + break; + } + } + if (temp) sSingleNonBlockDamagableRecipeList.add(tRecipe); + } else if (tRecipe instanceof ShapedRecipes) { + boolean temp = true; + for (ItemStack tObject : ((ShapedRecipes) tRecipe).recipeItems) { + if (tObject != null && (tObject.getItem() == null || tObject.getMaxStackSize() < 2 + || tObject.getMaxDamage() > 0 + || tObject.getItem() instanceof ItemBlock)) { + temp = false; + break; + } + } + if (temp) sSingleNonBlockDamagableRecipeList.add(tRecipe); + } else { + sSingleNonBlockDamagableRecipeList.add(tRecipe); + } + } + } + } + GT_Log.out.println( + "GT_Mod: Created a List of Tool Recipes containing " + sSingleNonBlockDamagableRecipeList.size() + + " Recipes for recycling." + + (sSingleNonBlockDamagableRecipeList.size() > 1024 + ? " Scanning all these Recipes is the reason for the startup Lag you receive right now." + : E)); + } + List<ItemStack> rList = getRecipeOutputs(sSingleNonBlockDamagableRecipeList, true, aRecipe); + if (!GregTech_API.sPostloadStarted || GregTech_API.sPostloadFinished) + sSingleNonBlockDamagableRecipeList.clear(); + return rList; + } + + /** + * Gives you a list of the Outputs from a Crafting Recipe If you have multiple Mods, which add Bronze Armor for + * example + */ + public static List<ItemStack> getRecipeOutputs(ItemStack... aRecipe) { + return getRecipeOutputs( + CraftingManager.getInstance() + .getRecipeList(), + false, + aRecipe); + } + + private static List<IRecipe> bufferedRecipes = null; + + /** + * Gives you a list of the Outputs from a Crafting Recipe If you have multiple Mods, which add Bronze Armor for + * example Buffers a List which only has armor-alike crafting in it + */ + public static List<ItemStack> getRecipeOutputsBuffered(ItemStack... aRecipe) { + + if (bufferedRecipes == null) bufferedRecipes = CraftingManager.getInstance() + .getRecipeList() + .stream() + .filter( + tRecipe -> !(tRecipe instanceof ShapelessRecipes) && !(tRecipe instanceof ShapelessOreRecipe) + && !(tRecipe instanceof IGT_CraftingRecipe)) + .filter(tRecipe -> { + try { + ItemStack tOutput = tRecipe.getRecipeOutput(); + if (tOutput.stackSize == 1 && tOutput.getMaxDamage() > 0 && tOutput.getMaxStackSize() == 1) { + return true; + } + } catch (Exception ignored) {} + return false; + }) + .collect(Collectors.toList()); + return getRecipeOutputs(bufferedRecipes, false, aRecipe); + } + + /** + * Gives you a list of the Outputs from a Crafting Recipe If you have multiple Mods, which add Bronze Armor for + * example + */ + public static List<ItemStack> getRecipeOutputs(List<IRecipe> aList, boolean aDeleteFromList, ItemStack... aRecipe) { + List<ItemStack> rList = new ArrayList<>(); + if (aRecipe == null || Arrays.stream(aRecipe) + .noneMatch(Objects::nonNull)) return rList; + InventoryCrafting aCrafting = new InventoryCrafting(new Container() { + + @Override + public boolean canInteractWith(EntityPlayer player) { + return false; + } + }, 3, 3); + for (int i = 0; i < 9 && i < aRecipe.length; i++) aCrafting.setInventorySlotContents(i, aRecipe[i]); + if (!aDeleteFromList) { + HashSet<ItemStack> stacks = new HashSet<>(); + aList.stream() + .filter(tRecipe -> { + if (tRecipe instanceof ShapelessRecipes || tRecipe instanceof ShapelessOreRecipe + || tRecipe instanceof IGT_CraftingRecipe) return false; + try { + return tRecipe.matches(aCrafting, DW); + } catch (Throwable e) { + e.printStackTrace(GT_Log.err); + return false; + } + }) + .forEach(tRecipe -> stacks.add(tRecipe.getCraftingResult(aCrafting))); + rList = stacks.stream() + .filter( + tOutput -> tOutput.stackSize == 1 && tOutput.getMaxDamage() > 0 && tOutput.getMaxStackSize() == 1) + .collect(Collectors.toList()); + } else for (Iterator<IRecipe> iterator = aList.iterator(); iterator.hasNext();) { + IRecipe tRecipe = iterator.next(); + boolean matched = false; + + try { + matched = tRecipe.matches(aCrafting, DW); + } catch (Throwable e) { + e.printStackTrace(GT_Log.err); + } + if (matched) { + ItemStack tOutput = tRecipe.getCraftingResult(aCrafting); + + if (tOutput == null || tOutput.stackSize <= 0) { + // Seriously, who would ever do that shit? + if (!GregTech_API.sPostloadFinished) throw new GT_ItsNotMyFaultException( + "Seems another Mod added a Crafting Recipe with null Output. Tell the Developer of said Mod to fix that."); + continue; + } + if (tOutput.stackSize != 1) continue; + if (tOutput.getMaxDamage() <= 0) continue; + if (tOutput.getMaxStackSize() != 1) continue; + if (tRecipe instanceof ShapelessRecipes) continue; + if (tRecipe instanceof ShapelessOreRecipe) continue; + if (tRecipe instanceof IGT_CraftingRecipe) continue; + rList.add(GT_Utility.copyOrNull(tOutput)); + iterator.remove(); + } + } + return rList; + } + + /** + * Used in my own Macerator. Decreases StackSize of the Input if wanted. + */ + @Deprecated + public static ItemStack getMaceratorOutput(ItemStack aInput, boolean aRemoveInput, ItemStack aOutputSlot) { + return GT_Utility.copyOrNull( + getMachineOutput(aInput, getMaceratorRecipeList(), aRemoveInput, new NBTTagCompound(), aOutputSlot)[0]); + } + + /** + * Used in my own Extractor. Decreases StackSize of the Input if wanted. + */ + @Deprecated + public static ItemStack getExtractorOutput(ItemStack aInput, boolean aRemoveInput, ItemStack aOutputSlot) { + return GT_Utility.copyOrNull( + getMachineOutput(aInput, getExtractorRecipeList(), aRemoveInput, new NBTTagCompound(), aOutputSlot)[0]); + } + + /** + * Used in my own Compressor. Decreases StackSize of the Input if wanted. + */ + @Deprecated + public static ItemStack getCompressorOutput(ItemStack aInput, boolean aRemoveInput, ItemStack aOutputSlot) { + return GT_Utility.copyOrNull( + getMachineOutput(aInput, getCompressorRecipeList(), aRemoveInput, new NBTTagCompound(), aOutputSlot)[0]); + } + + /** + * Used in my own Furnace. + */ + public static ItemStack getSmeltingOutput(ItemStack aInput, boolean aRemoveInput, ItemStack aOutputSlot) { + if (aInput == null || aInput.stackSize < 1) return null; + ItemStack rStack = GT_OreDictUnificator.get( + FurnaceRecipes.smelting() + .getSmeltingResult(aInput)); + + if (rStack != null && (aOutputSlot == null || (GT_Utility.areStacksEqual(rStack, aOutputSlot) + && rStack.stackSize + aOutputSlot.stackSize <= aOutputSlot.getMaxStackSize()))) { + if (aRemoveInput) aInput.stackSize--; + return rStack; + } + return null; + } + + /** + * Used in my own Machines. Decreases StackSize of the Input if wanted. + * <p/> + * Checks also if there is enough Space in the Output Slots. + */ + public static ItemStack[] getMachineOutput(ItemStack aInput, Map<IRecipeInput, RecipeOutput> aRecipeList, + boolean aRemoveInput, NBTTagCompound rRecipeMetaData, ItemStack... aOutputSlots) { + if (aOutputSlots == null || aOutputSlots.length == 0) return new ItemStack[0]; + if (aInput == null) return new ItemStack[aOutputSlots.length]; + try { + for (Entry<IRecipeInput, RecipeOutput> tEntry : aRecipeList.entrySet()) { + if (tEntry.getKey() + .matches(aInput)) { + if (tEntry.getKey() + .getAmount() <= aInput.stackSize) { + ItemStack[] tList = tEntry.getValue().items.toArray(new ItemStack[0]); + if (tList.length == 0) break; + ItemStack[] rList = new ItemStack[aOutputSlots.length]; + rRecipeMetaData.setTag("return", tEntry.getValue().metadata); + for (byte i = 0; i < aOutputSlots.length && i < tList.length; i++) { + if (tList[i] != null) { + if (aOutputSlots[i] == null || (GT_Utility.areStacksEqual(tList[i], aOutputSlots[i]) + && tList[i].stackSize + aOutputSlots[i].stackSize + <= aOutputSlots[i].getMaxStackSize())) { + rList[i] = GT_Utility.copyOrNull(tList[i]); + } else { + return new ItemStack[aOutputSlots.length]; + } + } + } + + if (aRemoveInput) aInput.stackSize -= tEntry.getKey() + .getAmount(); + return rList; + } + break; + } + } + } catch (Throwable e) { + if (D1) e.printStackTrace(GT_Log.err); + } + return new ItemStack[aOutputSlots.length]; + } + + /** + * Used in my own Recycler. + * <p/> + * Only produces Scrap if aScrapChance == 0. aScrapChance is usually the random Number I give to the Function If you + * directly insert 0 as aScrapChance then you can check if its Recycler-Blacklisted or similar + */ + @Nullable + public static ItemStack getRecyclerOutput(ItemStack aInput, int aScrapChance) { + if (aInput == null || aScrapChance != 0) return null; + if (recyclerWhitelist == null) { + generateRecyclerCache(); + } + + if (recyclerWhitelist.isEmpty()) { + if (searchRecyclerCache(aInput, recyclerBlacklist)) { + return null; + } else { + return ItemList.IC2_Scrap.get(1); + } + } else { + if (searchRecyclerCache(aInput, recyclerWhitelist)) { + return ItemList.IC2_Scrap.get(1); + } else { + return null; + } + } + } + + private static void generateRecyclerCache() { + recyclerWhitelist = new HashSet<>(); + for (IRecipeInput input : ic2.api.recipe.Recipes.recyclerWhitelist) { + for (ItemStack stack : input.getInputs()) { + recyclerWhitelist.add(GT_Utility.ItemId.create(stack.getItem(), stack.getItemDamage(), null)); + } + } + recyclerBlacklist = new HashSet<>(); + for (IRecipeInput input : ic2.api.recipe.Recipes.recyclerBlacklist) { + for (ItemStack stack : input.getInputs()) { + recyclerBlacklist.add(GT_Utility.ItemId.create(stack.getItem(), stack.getItemDamage(), null)); + } + } + } + + private static boolean searchRecyclerCache(ItemStack stack, Set<GT_Utility.ItemId> set) { + if (set.contains(GT_Utility.ItemId.createWithoutNBT(stack))) { + return true; + } + // ic2.api.recipe.RecipeInputItemStack#matches expects item with wildcard meta to accept arbitrary meta + return set.contains(GT_Utility.ItemId.createAsWildcard(stack)); + } + + /** + * For the Scrapboxinator + */ + public static ItemStack getRandomScrapboxDrop() { + return ic2.api.recipe.Recipes.scrapboxDrops.getDrop(ItemList.IC2_Scrapbox.get(1), false); + } + + /** + * Charges an Electric Item. Only if it's a valid Electric Item of course. This forces the Usage of proper Voltages + * (so not the transfer limits defined by the Items) unless you ignore the Transfer Limit. If aTier is + * Integer.MAX_VALUE it will ignore Tier based Limitations. + * + * @return the actually used Energy. + */ + public static int chargeElectricItem(ItemStack aStack, int aCharge, int aTier, boolean aIgnoreLimit, + boolean aSimulate) { + try { + if (isElectricItem(aStack)) { + int tTier = ((ic2.api.item.IElectricItem) aStack.getItem()).getTier(aStack); + if (tTier < 0 || tTier == aTier || aTier == Integer.MAX_VALUE) { + if (!aIgnoreLimit && tTier >= 0) + aCharge = (int) Math.min(aCharge, V[Math.max(0, Math.min(V.length - 1, tTier))]); + if (aCharge > 0) { + int rCharge = (int) Math.max( + 0.0, + ic2.api.item.ElectricItem.manager.charge(aStack, aCharge, tTier, true, aSimulate)); + return rCharge + (rCharge * 4 > aTier ? aTier : 0); + } + } + } + } catch (Throwable e) { + /* Do nothing */ + } + return 0; + } + + /** + * Discharges an Electric Item. Only if it's a valid Electric Item for that of course. This forces the Usage of + * proper Voltages (so not the transfer limits defined by the Items) unless you ignore the Transfer Limit. If aTier + * is Integer.MAX_VALUE it will ignore Tier based Limitations. + * + * @return the Energy got from the Item. + */ + public static int dischargeElectricItem(ItemStack aStack, int aCharge, int aTier, boolean aIgnoreLimit, + boolean aSimulate, boolean aIgnoreDischargability) { + try { + // if (isElectricItem(aStack) && (aIgnoreDischargability || + // ((ic2.api.item.IElectricItem)aStack.getItem()).canProvideEnergy(aStack))) { + if (isElectricItem(aStack)) { + int tTier = ((ic2.api.item.IElectricItem) aStack.getItem()).getTier(aStack); + if (tTier < 0 || tTier == aTier || aTier == Integer.MAX_VALUE) { + if (!aIgnoreLimit && tTier >= 0) aCharge = (int) Math.min( + aCharge, + V[Math.max(0, Math.min(V.length - 1, tTier))] + B[Math.max(0, Math.min(V.length - 1, tTier))]); + if (aCharge > 0) { + int rCharge = (int) Math.max( + 0, + ic2.api.item.ElectricItem.manager.discharge( + aStack, + aCharge + (aCharge * 4 > aTier ? aTier : 0), + tTier, + true, + !aIgnoreDischargability, + aSimulate)); + return rCharge - (rCharge * 4 > aTier ? aTier : 0); + } + } + } + } catch (Throwable e) { + /* Do nothing */ + } + return 0; + } + + /** + * Uses an Electric Item. Only if it's a valid Electric Item for that of course. + * + * @return if the action was successful + */ + public static boolean canUseElectricItem(ItemStack aStack, int aCharge) { + try { + if (isElectricItem(aStack)) { + return ic2.api.item.ElectricItem.manager.canUse(aStack, aCharge); + } + } catch (Throwable e) { + /* Do nothing */ + } + return false; + } + + /** + * Uses an Electric Item. Only if it's a valid Electric Item for that of course. + * + * @return if the action was successful + */ + public static boolean useElectricItem(ItemStack aStack, int aCharge, EntityPlayer aPlayer) { + try { + if (isElectricItem(aStack)) { + ic2.api.item.ElectricItem.manager.use(aStack, 0, aPlayer); + if (ic2.api.item.ElectricItem.manager.canUse(aStack, aCharge)) { + return ic2.api.item.ElectricItem.manager.use(aStack, aCharge, aPlayer); + } + } + } catch (Throwable e) { + /* Do nothing */ + } + return false; + } + + /** + * Uses an Item. Tries to discharge in case of Electric Items + */ + public static boolean damageOrDechargeItem(ItemStack aStack, int aDamage, int aDecharge, EntityLivingBase aPlayer) { + if (GT_Utility.isStackInvalid(aStack) || (aStack.getMaxStackSize() <= 1 && aStack.stackSize > 1)) return false; + if (aPlayer instanceof EntityPlayer && ((EntityPlayer) aPlayer).capabilities.isCreativeMode) return true; + if (aStack.getItem() instanceof IDamagableItem) { + return ((IDamagableItem) aStack.getItem()).doDamageToItem(aStack, aDamage); + } else if (GT_ModHandler.isElectricItem(aStack)) { + if (canUseElectricItem(aStack, aDecharge)) { + if (aPlayer instanceof EntityPlayer) { + return GT_ModHandler.useElectricItem(aStack, aDecharge, (EntityPlayer) aPlayer); + } + return GT_ModHandler.dischargeElectricItem(aStack, aDecharge, Integer.MAX_VALUE, true, false, true) + >= aDecharge; + } + } else if (aStack.getItem() + .isDamageable()) { + if (aPlayer == null) { + aStack.setItemDamage(aStack.getItemDamage() + aDamage); + } else { + aStack.damageItem(aDamage, aPlayer); + } + if (aStack.getItemDamage() >= aStack.getMaxDamage()) { + aStack.setItemDamage(aStack.getMaxDamage() + 1); + ItemStack tStack = GT_Utility.getContainerItem(aStack, true); + if (tStack != null) { + aStack.func_150996_a(tStack.getItem()); + aStack.setItemDamage(tStack.getItemDamage()); + aStack.stackSize = tStack.stackSize; + aStack.setTagCompound(tStack.getTagCompound()); + } else if (aStack.stackSize > 0) { + aStack.stackSize--; + } + } + return true; + } + return false; + } + + /** + * Uses a Soldering Iron from player or external inventory + */ + public static boolean useSolderingIron(ItemStack aStack, EntityLivingBase aPlayer, IInventory aExternalInventory) { + if (aPlayer == null || aStack == null) return false; + if (GT_Utility.isStackInList(aStack, GregTech_API.sSolderingToolList)) { + if (aPlayer instanceof EntityPlayer tPlayer) { + if (tPlayer.capabilities.isCreativeMode) return true; + if (isElectricItem(aStack) && ic2.api.item.ElectricItem.manager.getCharge(aStack) > 1000.0d) { + if (consumeSolderingMaterial(tPlayer) + || (aExternalInventory != null && consumeSolderingMaterial(aExternalInventory))) { + if (canUseElectricItem(aStack, 10000)) { + return GT_ModHandler.useElectricItem(aStack, 10000, (EntityPlayer) aPlayer); + } + GT_ModHandler.useElectricItem( + aStack, + (int) ic2.api.item.ElectricItem.manager.getCharge(aStack), + (EntityPlayer) aPlayer); + return false; + } else { + GT_Utility.sendChatToPlayer( + (EntityPlayer) aPlayer, + GT_Utility.trans("094.1", "Not enough soldering material!")); + } + } + } else { + damageOrDechargeItem(aStack, 1, 1000, aPlayer); + return true; + } + } + return false; + } + + public static boolean useSolderingIron(ItemStack aStack, EntityLivingBase aPlayer) { + return useSolderingIron(aStack, aPlayer, null); + } + + public static boolean consumeSolderingMaterial(EntityPlayer aPlayer) { + if (aPlayer.capabilities.isCreativeMode) return true; + IInventory tInventory = aPlayer.inventory; + if (consumeSolderingMaterial(tInventory)) { + if (aPlayer.inventoryContainer != null) { + aPlayer.inventoryContainer.detectAndSendChanges(); + } + return true; + } + for (int i = 0; i < tInventory.getSizeInventory(); i++) { + ItemStack tStack = tInventory.getStackInSlot(i); + if (tStack != null && tStack.getItem() instanceof ItemToolbox) { + IInventory tToolboxInventory = ((ItemToolbox) tStack.getItem()).getInventory(aPlayer, tStack); + if (consumeSolderingMaterial(tToolboxInventory)) { + tInventory.markDirty(); + if (aPlayer.inventoryContainer != null) { + aPlayer.inventoryContainer.detectAndSendChanges(); + } + return true; + } + } + } + return false; + } + + /** + * Consumes soldering material from given inventory + */ + public static boolean consumeSolderingMaterial(IInventory aInventory) { + for (int i = 0; i < aInventory.getSizeInventory(); i++) { + ItemStack tStack = aInventory.getStackInSlot(i); + if (GT_Utility.isStackInList(tStack, GregTech_API.sSolderingMetalList)) { + if (tStack.stackSize < 1) return false; + if (tStack.stackSize == 1) { + tStack = null; + } else { + tStack.stackSize--; + } + aInventory.setInventorySlotContents(i, tStack); + aInventory.markDirty(); + return true; + } + } + return false; + } + + /** + * Is this an electric Item, which can charge other Items? + */ + public static boolean isChargerItem(ItemStack aStack) { + try { + if (isElectricItem(aStack)) { + return ((ic2.api.item.IElectricItem) aStack.getItem()).canProvideEnergy(aStack); + } + } catch (Throwable e) { + /* Do nothing */ + } + return false; + } + + /** + * Is this an electric Item? + */ + public static boolean isElectricItem(ItemStack aStack) { + try { + return aStack != null && aStack.getItem() instanceof ic2.api.item.IElectricItem + && ((IElectricItem) aStack.getItem()).getTier(aStack) < Integer.MAX_VALUE; + } catch (Throwable e) { + /* Do nothing */ + } + return false; + } + + public static boolean isElectricItem(ItemStack aStack, byte aTier) { + try { + return aStack != null && aStack.getItem() instanceof ic2.api.item.IElectricItem + && ((IElectricItem) aStack.getItem()).getTier(aStack) == aTier; + } catch (Throwable e) { + /* Do nothing */ + } + return false; + } + + /** + * Returns the current charge and maximum charge of an ItemStack. + * + * @param aStack Any ItemStack. + * @return Optional.empty() if the stack is null or not an electric item, or an Optional containing a payload of an + * array containing [ current_charge, maximum_charge ] on success. + */ + public static Optional<Long[]> getElectricItemCharge(ItemStack aStack) { + if (aStack == null || !isElectricItem(aStack)) { + return Optional.empty(); + } + + final Item item = aStack.getItem(); + + if (item instanceof final GT_MetaBase_Item metaBaseItem) { + final Long[] stats = metaBaseItem.getElectricStats(aStack); + if (stats != null && stats.length > 0) { + return Optional.of(new Long[] { metaBaseItem.getRealCharge(aStack), stats[0] }); + } + + } else if (item instanceof final IElectricItem ic2ElectricItem) { + return Optional.of( + new Long[] { (long) ic2.api.item.ElectricItem.manager.getCharge(aStack), + (long) ic2ElectricItem.getMaxCharge(aStack) }); + } + + return Optional.empty(); + } + + /** + * Allow item to be inserted into ic2 toolbox + */ + public static void registerBoxableItemToToolBox(ItemStack aStack) { + if (aStack != null) { + try { + ic2.api.item.ItemWrapper.registerBoxable(aStack.getItem(), (IBoxable) sBoxableWrapper); + } catch (Throwable ignored) { + /* Do nothing */ + } + sBoxableItems.add(new GT_ItemStack(aStack)); + } + } + + @Deprecated + public static void registerBoxableItemToToolBox(Item aItem) { + registerBoxableItemToToolBox(new ItemStack(aItem, 1, GT_Values.W)); + } + + public static int getCapsuleCellContainerCountMultipliedWithStackSize(ItemStack... aStacks) { + int rAmount = 0; + for (ItemStack tStack : aStacks) + if (tStack != null) rAmount += getCapsuleCellContainerCount(tStack) * tStack.stackSize; + return rAmount; + } + + public static int getCapsuleCellContainerCount(ItemStack aStack) { + if (aStack == null) return 0; + + if (GT_Utility.areStacksEqual(GT_Utility.getContainerForFilledItem(aStack, true), ItemList.Cell_Empty.get(1))) { + return 1; + } + + if (GT_Utility.areStacksEqual(aStack, getIC2Item("waterCell", 1, W))) { + return 1; + } + + for (OrePrefixes cellType : OrePrefixes.CELL_TYPES) { + if (cellType.contains(aStack)) { + return 1; + } + } + + return 0; + } + + public static class RecipeBits { + + /** + * Mirrors the Recipe + */ + public static long MIRRORED = B[0]; + /** + * Buffers the Recipe for later addition. This makes things more efficient. + */ + public static long BUFFERED = B[1]; + /** + * This is a special Tag I used for crafting Coins up and down. + */ + public static long KEEPNBT = B[2]; + /** + * Makes the Recipe Reverse Craftable in the Disassembler. + */ + public static long DISMANTLEABLE = B[3]; + /** + * Prevents the Recipe from accidentally getting removed by my own Handlers. + */ + public static long NOT_REMOVABLE = B[4]; + /** + * Reverses the Output of the Recipe for smelting and pulverising. + */ + public static long REVERSIBLE = B[5]; + /** + * Removes all Recipes with the same Output Item regardless of NBT, unless another Recipe Deletion Bit is added + * too. + */ + public static long DELETE_ALL_OTHER_RECIPES = B[6]; + /** + * Removes all Recipes with the same Output Item limited to the same NBT. + */ + public static long DELETE_ALL_OTHER_RECIPES_IF_SAME_NBT = B[7]; + /** + * Removes all Recipes with the same Output Item limited to Shaped Recipes. + */ + public static long DELETE_ALL_OTHER_SHAPED_RECIPES = B[8]; + /** + * Removes all Recipes with the same Output Item limited to native Recipe Handlers. + */ + public static long DELETE_ALL_OTHER_NATIVE_RECIPES = B[9]; + /** + * Disables the check for colliding Recipes. + */ + public static long DO_NOT_CHECK_FOR_COLLISIONS = B[10]; + /** + * Only adds the Recipe if there is another Recipe having that Output + */ + public static long ONLY_ADD_IF_THERE_IS_ANOTHER_RECIPE_FOR_IT = B[11]; + /** + * Only adds the Recipe if it has an Output + */ + public static long ONLY_ADD_IF_RESULT_IS_NOT_NULL = B[12]; + /** + * Don't remove shapeless recipes with this output + */ + public static long DONT_REMOVE_SHAPELESS = B[13]; + } + + /** + * Copy of the original Helper Class of Thermal Expansion, just to make sure it works even when other Mods include + * TE-APIs + */ + public static class ThermalExpansion { + + public static void addFurnaceRecipe(int energy, ItemStack input, ItemStack output) {} + + public static void addPulverizerRecipe(int energy, ItemStack input, ItemStack primaryOutput) {} + + public static void addPulverizerRecipe(int energy, ItemStack input, ItemStack primaryOutput, + ItemStack secondaryOutput) {} + + public static void addPulverizerRecipe(int energy, ItemStack input, ItemStack primaryOutput, + ItemStack secondaryOutput, int secondaryChance) {} + + public static void addSawmillRecipe(int energy, ItemStack input, ItemStack primaryOutput) {} + + public static void addSawmillRecipe(int energy, ItemStack input, ItemStack primaryOutput, + ItemStack secondaryOutput) {} + + public static void addSawmillRecipe(int energy, ItemStack input, ItemStack primaryOutput, + ItemStack secondaryOutput, int secondaryChance) {} + + public static void addSmelterRecipe(int energy, ItemStack primaryInput, ItemStack secondaryInput, + ItemStack primaryOutput) {} + + public static void addSmelterRecipe(int energy, ItemStack primaryInput, ItemStack secondaryInput, + ItemStack primaryOutput, ItemStack secondaryOutput) {} + + public static void addSmelterRecipe(int energy, ItemStack primaryInput, ItemStack secondaryInput, + ItemStack primaryOutput, ItemStack secondaryOutput, int secondaryChance) {} + + public static void addSmelterBlastOre(Materials aMaterial) {} + + public static void addCrucibleRecipe(int energy, ItemStack input, FluidStack output) {} + + public static void addTransposerFill(int energy, ItemStack input, ItemStack output, FluidStack fluid, + boolean reversible) {} + + public static void addTransposerExtract(int energy, ItemStack input, ItemStack output, FluidStack fluid, + int chance, boolean reversible) {} + + public static void addMagmaticFuel(String fluidName, int energy) {} + + public static void addCompressionFuel(String fluidName, int energy) {} + + public static void addCoolant(String fluidName, int energy) {} + } +} diff --git a/src/main/java/gregtech/api/util/GT_Multiblock_Tooltip_Builder.java b/src/main/java/gregtech/api/util/GT_Multiblock_Tooltip_Builder.java new file mode 100644 index 0000000000..43eeccdc9f --- /dev/null +++ b/src/main/java/gregtech/api/util/GT_Multiblock_Tooltip_Builder.java @@ -0,0 +1,720 @@ +package gregtech.api.util; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.StatCollector; + +import com.google.common.collect.Multimaps; +import com.google.common.collect.SetMultimap; +import com.gtnewhorizon.structurelib.StructureLibAPI; + +/** + * This makes it easier to build multi tooltips, with a standardized format. <br> + * Info section order should be:<br> + * addMachineType<br> + * addInfo, for what it does, special notes, etc.<br> + * addSeparator, if you need it<br> + * addPollutionAmount<br> + * <br> + * Structure order should be:<br> + * beginStructureBlock<br> + * addController<br> + * addCasingInfo<br> + * addOtherStructurePart, for secondary structure block info (pipes, coils, etc)<br> + * addEnergyHatch/addDynamoHatch<br> + * addMaintenanceHatch<br> + * addMufflerHatch<br> + * addInputBus/addInputHatch/addOutputBus/addOutputHatch, in that order<br> + * Use addStructureInfo for any comments on nonstandard structure info wherever needed <br> + * toolTipFinisher goes at the very end<br> + * <br> + * Originally created by kekzdealer + */ +public class GT_Multiblock_Tooltip_Builder { + + private static final String TAB = " "; + private static final String COLON = ": "; + private static final String SEPARATOR = ", "; + + private final List<String> iLines; + private final List<String> sLines; + private final List<String> hLines; + private final SetMultimap<Integer, String> hBlocks; + + private String[] iArray; + private String[] sArray; + private String[] hArray; + + // Localized tooltips + private static final String TT_machineType = StatCollector.translateToLocal("GT5U.MBTT.MachineType"); + private static final String TT_dimensions = StatCollector.translateToLocal("GT5U.MBTT.Dimensions"); + private static final String TT_hollow = StatCollector.translateToLocal("GT5U.MBTT.Hollow"); + private static final String TT_structure = StatCollector.translateToLocal("GT5U.MBTT.Structure"); + private static final String TT_controller = StatCollector.translateToLocal("GT5U.MBTT.Controller"); + private static final String TT_minimum = StatCollector.translateToLocal("GT5U.MBTT.Minimum"); + private static final String TT_tiered = StatCollector.translateToLocal("GT5U.MBTT.Tiered"); + private static final String TT_maintenancehatch = StatCollector.translateToLocal("GT5U.MBTT.MaintenanceHatch"); + private static final String TT_energyhatch = StatCollector.translateToLocal("GT5U.MBTT.EnergyHatch"); + private static final String TT_dynamohatch = StatCollector.translateToLocal("GT5U.MBTT.DynamoHatch"); + private static final String TT_mufflerhatch = StatCollector.translateToLocal("GT5U.MBTT.MufflerHatch"); + private static final String TT_inputbus = StatCollector.translateToLocal("GT5U.MBTT.InputBus"); + private static final String TT_inputhatch = StatCollector.translateToLocal("GT5U.MBTT.InputHatch"); + private static final String TT_outputbus = StatCollector.translateToLocal("GT5U.MBTT.OutputBus"); + private static final String TT_outputhatch = StatCollector.translateToLocal("GT5U.MBTT.OutputHatch"); + private static final String TT_causes = StatCollector.translateToLocal("GT5U.MBTT.Causes"); + private static final String TT_pps = StatCollector.translateToLocal("GT5U.MBTT.PPS"); + private static final String TT_hold = StatCollector.translateToLocal("GT5U.MBTT.Hold"); + private static final String TT_todisplay = StatCollector.translateToLocal("GT5U.MBTT.Display"); + private static final String TT_structurehint = StatCollector.translateToLocal("GT5U.MBTT.StructureHint"); + private static final String TT_mod = StatCollector.translateToLocal("GT5U.MBTT.Mod"); + private static final String TT_air = StatCollector.translateToLocal("GT5U.MBTT.Air"); + private static final String[] TT_dots = IntStream.range(0, 16) + .mapToObj(i -> StatCollector.translateToLocal("structurelib.blockhint." + i + ".name")) + .toArray(String[]::new); + + public GT_Multiblock_Tooltip_Builder() { + iLines = new LinkedList<>(); + sLines = new LinkedList<>(); + hLines = new LinkedList<>(); + hBlocks = Multimaps.newSetMultimap(new HashMap<>(), HashSet::new); + hBlocks.put(StructureLibAPI.HINT_BLOCK_META_AIR, TT_air); + } + + /** + * Add a line telling you what the machine type is. Usually, this will be the name of a SB version.<br> + * Machine Type: machine + * + * @param machine Name of the machine type + * + * @return Instance this method was called on. + */ + public GT_Multiblock_Tooltip_Builder addMachineType(String machine) { + iLines.add(TT_machineType + COLON + EnumChatFormatting.YELLOW + machine + EnumChatFormatting.RESET); + return this; + } + + /** + * Add a basic line of information about this structure + * + * @param info The line to be added. + * @return Instance this method was called on. + */ + public GT_Multiblock_Tooltip_Builder addInfo(String info) { + iLines.add(info); + return this; + } + + /** + * Add a separator line like this:<br> + * ----------------------------------------- + * + * @return Instance this method was called on. + */ + public GT_Multiblock_Tooltip_Builder addSeparator() { + iLines.add("-----------------------------------------"); + return this; + } + + /** + * Add a line telling how much this machine pollutes. + * + * @param pollution Amount of pollution per second when active + * + * @return Instance this method was called on. + */ + public GT_Multiblock_Tooltip_Builder addPollutionAmount(int pollution) { + iLines.add( + TT_causes + COLON + EnumChatFormatting.DARK_PURPLE + pollution + " " + EnumChatFormatting.GRAY + TT_pps); + return this; + } + + /** + * Begin adding structural information by adding a line about the structure's dimensions and then inserting a + * "Structure:" line. + * + * @param w Structure width. + * @param h Structure height. + * @param l Structure depth/length. + * @param hollow T/F, adds a (hollow) comment if true + * @return Instance this method was called on. + */ + public GT_Multiblock_Tooltip_Builder beginStructureBlock(int w, int h, int l, boolean hollow) { + sLines.add( + EnumChatFormatting.WHITE + TT_dimensions + + COLON + + EnumChatFormatting.GOLD + + w + + EnumChatFormatting.GRAY + + "x" + + EnumChatFormatting.GOLD + + h + + EnumChatFormatting.GRAY + + "x" + + EnumChatFormatting.GOLD + + l + + EnumChatFormatting.GRAY + + " (" + + EnumChatFormatting.GOLD + + "W" + + EnumChatFormatting.GRAY + + "x" + + EnumChatFormatting.GOLD + + "H" + + EnumChatFormatting.GRAY + + "x" + + EnumChatFormatting.GOLD + + "L" + + EnumChatFormatting.GRAY + + ") " + + EnumChatFormatting.RED + + (hollow ? EnumChatFormatting.RED + TT_hollow : "")); + sLines.add(EnumChatFormatting.WHITE + TT_structure + COLON); + return this; + } + + /** + * Begin adding structural information by adding a line about the structure's dimensions<br> + * and then inserting a "Structure:" line. Variable version displays min and max + * + * @param wmin Structure min width. + * @param wmax Structure max width. + * @param hmin Structure min height. + * @param hmax Structure max height. + * @param lmin Structure min depth/length. + * @param lmax Structure max depth/length. + * @param hollow T/F, adds a (hollow) comment if true + * @return Instance this method was called on. + */ + public GT_Multiblock_Tooltip_Builder beginVariableStructureBlock(int wmin, int wmax, int hmin, int hmax, int lmin, + int lmax, boolean hollow) { + sLines.add( + EnumChatFormatting.WHITE + TT_dimensions + + COLON + + EnumChatFormatting.GOLD + + wmin + + (wmin != wmax ? "-" + wmax : "") + + EnumChatFormatting.GRAY + + "x" + + EnumChatFormatting.GOLD + + hmin + + (hmin != hmax ? "-" + hmax : "") + + EnumChatFormatting.GRAY + + "x" + + EnumChatFormatting.GOLD + + lmin + + (lmin != lmax ? "-" + lmax : "") + + EnumChatFormatting.GRAY + + " (" + + EnumChatFormatting.GOLD + + "W" + + EnumChatFormatting.GRAY + + "x" + + EnumChatFormatting.GOLD + + "H" + + EnumChatFormatting.GRAY + + "x" + + EnumChatFormatting.GOLD + + "L" + + EnumChatFormatting.GRAY + + ") " + + (hollow ? EnumChatFormatting.RED + TT_hollow : "")); + sLines.add(EnumChatFormatting.WHITE + TT_structure + COLON); + return this; + } + + /** + * Add a line of information about the structure:<br> + * (indent)Controller: info + * + * @param info Positional information. + * @return Instance this method was called on. + */ + public GT_Multiblock_Tooltip_Builder addController(String info) { + sLines.add(TAB + EnumChatFormatting.WHITE + TT_controller + COLON + EnumChatFormatting.GRAY + info); + return this; + } + + /** + * Add a line of information about the structure:<br> + * (indent)minCountx casingName (minimum) (tiered) + * + * @param casingName Name of the Casing. + * @param minCount Minimum needed for valid structure check. + * @return Instance this method was called on. + * + * @deprecated Replaced by {@link #addCasingInfoMin(String, int, boolean)} + * + */ + @Deprecated + public GT_Multiblock_Tooltip_Builder addCasingInfo(String casingName, int minCount) { + return addCasingInfoMin(casingName, minCount, false); + } + + /** + * Add a line of information about the structure:<br> + * (indent)countx casingName (tiered) + * + * @param casingName Name of the Casing. + * @param isTiered Flag if this casing accepts multiple tiers (e.g. coils) + * @return Instance this method was called on. + */ + public GT_Multiblock_Tooltip_Builder addCasingInfoExactly(String casingName, int count, boolean isTiered) { + return addCasingInfoExactlyColored( + casingName, + EnumChatFormatting.GRAY, + count, + EnumChatFormatting.GOLD, + isTiered); + } + + /** + * Add a line of information about the structure:<br> + * (indent)countx casingName (tiered) + * + * @param casingName Name of the Casing. + * @param isTiered Flag if this casing accepts multiple tiers (e.g. coils) + * @param countColor Color of the casing count text + * @param textColor Color of the casing name text + * @return Instance this method was called on. + */ + public GT_Multiblock_Tooltip_Builder addCasingInfoExactlyColored(String casingName, EnumChatFormatting textColor, + int count, EnumChatFormatting countColor, boolean isTiered) { + sLines.add( + countColor + TAB + + count + + "x " + + EnumChatFormatting.RESET + + textColor + + casingName + + (isTiered ? " " + TT_tiered : "")); + return this; + } + + /** + * Add a line of information about the structure:<br> + * (indent)minCountx casingName (minimum) (tiered) + * + * @param casingName Name of the Casing. + * @param minCount Minimum needed for valid structure check. + * @param isTiered Flag if this casing accepts multiple tiers (e.g. coils) + * @return Instance this method was called on. + */ + public GT_Multiblock_Tooltip_Builder addCasingInfoMin(String casingName, int minCount, boolean isTiered) { + return addCasingInfoMinColored( + casingName, + EnumChatFormatting.GRAY, + minCount, + EnumChatFormatting.GOLD, + isTiered); + } + + /** + * Add a line of information about the structure:<br> + * (indent)minCountx casingName (minimum) (tiered) + * + * @param casingName Name of the Casing. + * @param minCount Minimum needed for valid structure check. + * @param isTiered Flag if this casing accepts multiple tiers (e.g. coils) + * @param countColor Color of the casing count text + * @param textColor Color of the casing name text + * @return Instance this method was called on. + */ + public GT_Multiblock_Tooltip_Builder addCasingInfoMinColored(String casingName, EnumChatFormatting textColor, + int minCount, EnumChatFormatting countColor, boolean isTiered) { + sLines.add( + countColor + TAB + + minCount + + "x " + + EnumChatFormatting.RESET + + textColor + + casingName + + " " + + TT_minimum + + (isTiered ? " " + TT_tiered : "")); + return this; + } + + /** + * Add a line of information about the structure:<br> + * (indent)minCountx - maxCountx casingName (minimum) (tiered) + * + * @param casingName Name of the Casing. + * @param minCount Minimum needed for valid structure check. + * @param maxCount Maximum needed for valid structure check. + * @param isTiered Flag if this casing accepts multiple tiers (e.g. coils) + * @return Instance this method was called on. + */ + public GT_Multiblock_Tooltip_Builder addCasingInfoRange(String casingName, int minCount, int maxCount, + boolean isTiered) { + return addCasingInfoRangeColored( + casingName, + EnumChatFormatting.GRAY, + minCount, + maxCount, + EnumChatFormatting.GOLD, + isTiered); + } + + /** + * Add a line of information about the structure:<br> + * (indent)minCountx - maxCountx casingName (minimum) (tiered) + * + * @param casingName Name of the Casing. + * @param minCount Minimum needed for valid structure check. + * @param maxCount Maximum needed for valid structure check. + * @param isTiered Flag if this casing accepts multiple tiers (e.g. coils) + * @param countColor Color of the casing count text + * @param textColor Color of the casing name text + * @return Instance this method was called on. + */ + public GT_Multiblock_Tooltip_Builder addCasingInfoRangeColored(String casingName, EnumChatFormatting textColor, + int minCount, int maxCount, EnumChatFormatting countColor, boolean isTiered) { + sLines.add( + countColor + TAB + + minCount + + "x" + + EnumChatFormatting.GRAY + + " - " + + countColor + + maxCount + + "x " + + EnumChatFormatting.RESET + + textColor + + casingName + + (isTiered ? " " + TT_tiered : "")); + return this; + } + + /** + * Use this method to add a structural part that isn't covered by the other methods.<br> + * (indent)name: info + * + * @param name Name of the hatch or other component. + * @param info Positional information. + * @return Instance this method was called on. + */ + public GT_Multiblock_Tooltip_Builder addOtherStructurePart(String name, String info) { + sLines.add(EnumChatFormatting.WHITE + TAB + name + COLON + EnumChatFormatting.GRAY + info); + return this; + } + + /** + * Add a line of information about the structure:<br> + * (indent)Maintenance Hatch: info + * + * @param info Positional information. + * @return Instance this method was called on. + */ + public GT_Multiblock_Tooltip_Builder addMaintenanceHatch(String info) { + sLines.add(EnumChatFormatting.WHITE + TAB + TT_maintenancehatch + COLON + EnumChatFormatting.GRAY + info); + return this; + } + + /** + * Add a line of information about the structure:<br> + * (indent)Muffler Hatch: info + * + * @param info Location where the hatch goes + * @return Instance this method was called on. + */ + public GT_Multiblock_Tooltip_Builder addMufflerHatch(String info) { + sLines.add(EnumChatFormatting.WHITE + TAB + TT_mufflerhatch + COLON + EnumChatFormatting.GRAY + info); + return this; + } + + /** + * Add a line of information about the structure:<br> + * (indent)Energy Hatch: info + * + * @param info Positional information. + * @return Instance this method was called on. + */ + public GT_Multiblock_Tooltip_Builder addEnergyHatch(String info) { + sLines.add(EnumChatFormatting.WHITE + TAB + TT_energyhatch + COLON + EnumChatFormatting.GRAY + info); + return this; + } + + /** + * Add a line of information about the structure:<br> + * (indent)Dynamo Hatch: info + * + * @param info Positional information. + * @return Instance this method was called on. + */ + public GT_Multiblock_Tooltip_Builder addDynamoHatch(String info) { + sLines.add(EnumChatFormatting.WHITE + TAB + TT_dynamohatch + COLON + EnumChatFormatting.GRAY + info); + return this; + } + + /** + * Add a line of information about the structure:<br> + * (indent)Input Bus: info + * + * @param info Location where the bus goes + * @return Instance this method was called on. + */ + public GT_Multiblock_Tooltip_Builder addInputBus(String info) { + sLines.add(EnumChatFormatting.WHITE + TAB + TT_inputbus + COLON + EnumChatFormatting.GRAY + info); + return this; + } + + /** + * Add a line of information about the structure:<br> + * (indent)Input Hatch: info + * + * @param info Location where the hatch goes + * @return Instance this method was called on. + */ + public GT_Multiblock_Tooltip_Builder addInputHatch(String info) { + sLines.add(EnumChatFormatting.WHITE + TAB + TT_inputhatch + COLON + EnumChatFormatting.GRAY + info); + return this; + } + + /** + * Add a line of information about the structure:<br> + * (indent)Output Bus: info + * + * @param info Location where the bus goes + * @return Instance this method was called on. + */ + public GT_Multiblock_Tooltip_Builder addOutputBus(String info) { + sLines.add(EnumChatFormatting.WHITE + TAB + TT_outputbus + COLON + EnumChatFormatting.GRAY + info); + return this; + } + + /** + * Add a line of information about the structure:<br> + * (indent)Output Hatch: info + * + * @param info Location where the bus goes + * @return Instance this method was called on. + */ + public GT_Multiblock_Tooltip_Builder addOutputHatch(String info) { + sLines.add(EnumChatFormatting.WHITE + TAB + TT_outputhatch + COLON + EnumChatFormatting.GRAY + info); + return this; + } + + /** + * Use this method to add a structural part that isn't covered by the other methods.<br> + * (indent)name: info + * + * @param name Name of the hatch or other component. + * @param info Positional information. + * @param dots The valid locations for this part when asked to display hints + * @return Instance this method was called on. + */ + public GT_Multiblock_Tooltip_Builder addOtherStructurePart(String name, String info, int... dots) { + sLines.add(EnumChatFormatting.WHITE + TAB + name + COLON + EnumChatFormatting.GRAY + info); + for (int dot : dots) hBlocks.put(dot, name); + return this; + } + + /** + * Add a line of information about the structure:<br> + * (indent)Maintenance Hatch: info + * + * @param info Positional information. + * @param dots The valid locations for this part when asked to display hints + * @return Instance this method was called on. + */ + public GT_Multiblock_Tooltip_Builder addMaintenanceHatch(String info, int... dots) { + sLines.add(EnumChatFormatting.WHITE + TAB + TT_maintenancehatch + COLON + EnumChatFormatting.GRAY + info); + for (int dot : dots) hBlocks.put(dot, TT_maintenancehatch); + return this; + } + + /** + * Add a line of information about the structure:<br> + * (indent)Muffler Hatch: info + * + * @param info Location where the hatch goes + * @param dots The valid locations for this part when asked to display hints + * @return Instance this method was called on. + */ + public GT_Multiblock_Tooltip_Builder addMufflerHatch(String info, int... dots) { + sLines.add(EnumChatFormatting.WHITE + TAB + TT_mufflerhatch + COLON + EnumChatFormatting.GRAY + info); + for (int dot : dots) hBlocks.put(dot, TT_mufflerhatch); + return this; + } + + /** + * Add a line of information about the structure:<br> + * (indent)Energy Hatch: info + * + * @param info Positional information. + * @param dots The valid locations for this part when asked to display hints + * @return Instance this method was called on. + */ + public GT_Multiblock_Tooltip_Builder addEnergyHatch(String info, int... dots) { + sLines.add(EnumChatFormatting.WHITE + TAB + TT_energyhatch + COLON + EnumChatFormatting.GRAY + info); + for (int dot : dots) hBlocks.put(dot, TT_energyhatch); + return this; + } + + /** + * Add a line of information about the structure:<br> + * (indent)Dynamo Hatch: info + * + * @param info Positional information. + * @param dots The valid locations for this part when asked to display hints + * @return Instance this method was called on. + */ + public GT_Multiblock_Tooltip_Builder addDynamoHatch(String info, int... dots) { + sLines.add(EnumChatFormatting.WHITE + TAB + TT_dynamohatch + COLON + EnumChatFormatting.GRAY + info); + for (int dot : dots) hBlocks.put(dot, TT_dynamohatch); + return this; + } + + /** + * Add a line of information about the structure:<br> + * (indent)Input Bus: info + * + * @param info Location where the bus goes + * @param dots The valid locations for this part when asked to display hints + * @return Instance this method was called on. + */ + public GT_Multiblock_Tooltip_Builder addInputBus(String info, int... dots) { + sLines.add(EnumChatFormatting.WHITE + TAB + TT_inputbus + COLON + EnumChatFormatting.GRAY + info); + for (int dot : dots) hBlocks.put(dot, TT_inputbus); + return this; + } + + /** + * Add a line of information about the structure:<br> + * (indent)Input Hatch: info + * + * @param info Location where the hatch goes + * @param dots The valid locations for this part when asked to display hints + * @return Instance this method was called on. + */ + public GT_Multiblock_Tooltip_Builder addInputHatch(String info, int... dots) { + sLines.add(EnumChatFormatting.WHITE + TAB + TT_inputhatch + COLON + EnumChatFormatting.GRAY + info); + for (int dot : dots) hBlocks.put(dot, TT_inputhatch); + return this; + } + + /** + * Add a line of information about the structure:<br> + * (indent)Output Bus: info + * + * @param info Location where the bus goes + * @param dots The valid locations for this part when asked to display hints + * @return Instance this method was called on. + */ + public GT_Multiblock_Tooltip_Builder addOutputBus(String info, int... dots) { + sLines.add(EnumChatFormatting.WHITE + TAB + TT_outputbus + COLON + EnumChatFormatting.GRAY + info); + for (int dot : dots) hBlocks.put(dot, TT_outputbus); + return this; + } + + /** + * Add a line of information about the structure:<br> + * (indent)Output Hatch: info + * + * @param info Location where the bus goes + * @param dots The valid locations for this part when asked to display hints + * @return Instance this method was called on. + */ + public GT_Multiblock_Tooltip_Builder addOutputHatch(String info, int... dots) { + sLines.add(EnumChatFormatting.WHITE + TAB + TT_outputhatch + COLON + EnumChatFormatting.GRAY + info); + for (int dot : dots) hBlocks.put(dot, TT_outputhatch); + return this; + } + + /** + * Use this method to add non-standard structural info.<br> + * (indent)info + * + * @param info The line to be added. + * @return Instance this method was called on. + */ + public GT_Multiblock_Tooltip_Builder addStructureInfo(String info) { + sLines.add(TAB + info); + return this; + } + + /** + * Use this method to add non-standard structural info.<br> + * (indent)info + * + * @param channel the name of subchannel + * @param purpose the purpose of subchannel + * @return Instance this method was called on. + */ + public GT_Multiblock_Tooltip_Builder addSubChannelUsage(String channel, String purpose) { + sLines.add(TAB + StatCollector.translateToLocalFormatted("GT5U.MBTT.subchannel", channel, purpose)); + return this; + } + + /** + * Use this method to add non-standard structural hint. This info will appear before the standard structural hint. + * + * @param info The line to be added. This should be an entry into minecraft's localization system. + * @return Instance this method was called on. + */ + public GT_Multiblock_Tooltip_Builder addStructureHint(String info) { + hLines.add(StatCollector.translateToLocal(info)); + return this; + } + + /** + * Use this method to add an entry to standard structural hint without creating a corresponding line in structure + * information + * + * @param name The name of block This should be an entry into minecraft's localization system. + * @param dots Possible locations of this block + * @return Instance this method was called on. + */ + public GT_Multiblock_Tooltip_Builder addStructureHint(String name, int... dots) { + for (int dot : dots) hBlocks.put(dot, StatCollector.translateToLocal(name)); + return this; + } + + /** + * Call at the very end.<br> + * Adds a final line with the mod name and information on how to display the structure guidelines.<br> + * Ends the building process. + * + * @param mod Name of the mod that adds this multiblock machine + */ + public void toolTipFinisher(String mod) { + iLines.add( + TT_hold + " " + + EnumChatFormatting.BOLD + + "[LSHIFT]" + + EnumChatFormatting.RESET + + EnumChatFormatting.GRAY + + " " + + TT_todisplay); + iLines.add(TT_mod + COLON + EnumChatFormatting.GREEN + mod + EnumChatFormatting.GRAY); + hLines.add(TT_structurehint); + iArray = iLines.toArray(new String[0]); + sArray = sLines.toArray(new String[0]); + // e.getKey() - 1 because 1 dot is meta 0. + hArray = Stream.concat( + hLines.stream(), + hBlocks.asMap() + .entrySet() + .stream() + .map(e -> TT_dots[e.getKey() - 1] + COLON + String.join(SEPARATOR, e.getValue()))) + .toArray(String[]::new); + } + + public String[] getInformation() { + return iArray; + } + + public String[] getStructureInformation() { + return sArray; + } + + public String[] getStructureHint() { + return hArray; + } +} diff --git a/src/main/java/gregtech/api/util/GT_OreDictUnificator.java b/src/main/java/gregtech/api/util/GT_OreDictUnificator.java new file mode 100644 index 0000000000..7032fc87fc --- /dev/null +++ b/src/main/java/gregtech/api/util/GT_OreDictUnificator.java @@ -0,0 +1,570 @@ +package gregtech.api.util; + +import static gregtech.api.enums.GT_Values.E; +import static gregtech.api.enums.GT_Values.M; +import static gregtech.api.enums.GT_Values.W; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import javax.annotation.Nullable; + +import net.minecraft.init.Items; +import net.minecraft.item.ItemStack; +import net.minecraftforge.oredict.OreDictionary; + +import gregtech.api.GregTech_API; +import gregtech.api.enums.Dyes; +import gregtech.api.enums.Materials; +import gregtech.api.enums.OrePrefixes; +import gregtech.api.enums.SubTag; +import gregtech.api.objects.GT_ItemStack; +import gregtech.api.objects.ItemData; +import gregtech.api.objects.MaterialStack; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap; +import it.unimi.dsi.fastutil.objects.ObjectOpenCustomHashSet; + +/** + * NEVER INCLUDE THIS FILE IN YOUR MOD!!! + * <p/> + * This is the Core of my OreDict Unification Code + * <p/> + * If you just want to use this to unificate your Items, then use the Function in the GregTech_API File + * <p/> + * P.S. It is intended to be named "Unificator" and not "Unifier", because that sounds more awesome. + */ +public class GT_OreDictUnificator { + + private static final Map<String, ItemStack> sName2StackMap = new HashMap<>(); + private static final Map<ItemStack, ItemData> sItemStack2DataMap = new Object2ObjectOpenCustomHashMap<>( + GT_ItemStack.ITEMSTACK_HASH_STRATEGY2); + private static final Map<ItemStack, List<ItemStack>> sUnificationTable = new Object2ObjectOpenCustomHashMap<>( + GT_ItemStack.ITEMSTACK_HASH_STRATEGY2); + private static final Set<ItemStack> sNoUnificationList = new ObjectOpenCustomHashSet<>( + GT_ItemStack.ITEMSTACK_HASH_STRATEGY2); + private static int isRegisteringOre = 0, isAddingOre = 0; + private static boolean mRunThroughTheList = true; + + static { + GregTech_API.sItemStackMappings.add(sItemStack2DataMap); + GregTech_API.sItemStackMappings.add(sUnificationTable); + } + + /** + * The Blacklist just prevents the Item from being unificated into something else. Useful if you have things like + * the Industrial Diamond, which is better than regular Diamond, but also usable in absolutely all Diamond Recipes. + */ + public static void addToBlacklist(ItemStack aStack) { + if (GT_Utility.isStackValid(aStack) && !GT_Utility.isStackInStackSet(aStack, sNoUnificationList)) + sNoUnificationList.add(aStack); + } + + public static boolean isBlacklisted(ItemStack aStack) { + return GT_Utility.isStackInStackSet(aStack, sNoUnificationList); + } + + public static void add(OrePrefixes aPrefix, Materials aMaterial, ItemStack aStack) { + set(aPrefix, aMaterial, aStack, false, false); + } + + public static void set(OrePrefixes aPrefix, Materials aMaterial, ItemStack aStack) { + set(aPrefix, aMaterial, aStack, true, false); + } + + public static void set(OrePrefixes aPrefix, Materials aMaterial, ItemStack aStack, boolean aOverwrite, + boolean aAlreadyRegistered) { + if (aMaterial == null || aPrefix == null + || GT_Utility.isStackInvalid(aStack) + || Items.feather.getDamage(aStack) == W) return; + isAddingOre++; + aStack = GT_Utility.copyAmount(1, aStack); + if (!aAlreadyRegistered) registerOre(aPrefix.get(aMaterial), aStack); + addAssociation(aPrefix, aMaterial, aStack, isBlacklisted(aStack)); + if (aOverwrite || GT_Utility.isStackInvalid( + sName2StackMap.get( + aPrefix.get(aMaterial) + .toString()))) + sName2StackMap.put( + aPrefix.get(aMaterial) + .toString(), + aStack); + isAddingOre--; + } + + public static ItemStack getFirstOre(Object aName, long aAmount) { + if (GT_Utility.isStringInvalid(aName)) return null; + ItemStack tStack = sName2StackMap.get(aName.toString()); + if (GT_Utility.isStackValid(tStack)) return GT_Utility.copyAmount(aAmount, tStack); + return GT_Utility.copyAmount(aAmount, getOresImmutable(aName).toArray()); + } + + public static ItemStack get(Object aName, long aAmount) { + return get(aName, null, aAmount, true, true); + } + + public static ItemStack get(Object aName, ItemStack aReplacement, long aAmount) { + return get(aName, aReplacement, aAmount, true, true); + } + + public static ItemStack get(OrePrefixes aPrefix, Object aMaterial, long aAmount) { + return get(aPrefix, aMaterial, null, aAmount); + } + + public static ItemStack get(OrePrefixes aPrefix, Object aMaterial, ItemStack aReplacement, long aAmount) { + if (OrePrefixes.mPreventableComponents.contains(aPrefix) && aPrefix.mDisabledItems.contains(aMaterial)) + return aReplacement; + return get(aPrefix.get(aMaterial), aReplacement, aAmount, false, true); + } + + public static ItemStack get(OrePrefixes aPrefix, Object aMaterial, long aAmount, boolean aNoInvalidAmounts) { + if (OrePrefixes.mPreventableComponents.contains(aPrefix) && aPrefix.mDisabledItems.contains(aMaterial)) + return null; + return get(aPrefix.get(aMaterial), null, aAmount, false, aNoInvalidAmounts); + } + + public static ItemStack get(Object aName, ItemStack aReplacement, long aAmount, boolean aMentionPossibleTypos, + boolean aNoInvalidAmounts) { + if (aNoInvalidAmounts && aAmount < 1) return null; + final ItemStack stackFromName = sName2StackMap.get(aName.toString()); + if (stackFromName != null) return GT_Utility.copyAmount(aAmount, stackFromName); + if (aMentionPossibleTypos) { + GT_Log.err.println("Unknown Key for Unification, Typo? " + aName); + } + final ItemStack stackFirstOre = getFirstOre(aName, aAmount); + if (stackFirstOre != null) return GT_Utility.copyAmount(aAmount, stackFirstOre); + return GT_Utility.copyAmount(aAmount, aReplacement); + } + + public static ItemStack[] setStackArray(boolean aUseBlackList, ItemStack... aStacks) { + for (int i = 0; i < aStacks.length; i++) aStacks[i] = get(aUseBlackList, GT_Utility.copyOrNull(aStacks[i])); + return aStacks; + } + + public static ItemStack[] getStackArray(boolean aUseBlackList, Object... aStacks) { + ItemStack[] rStacks = new ItemStack[aStacks.length]; + for (int i = 0; i < aStacks.length; i++) { + rStacks[i] = get(aUseBlackList, GT_Utility.copy(aStacks[i]), true); + } + return rStacks; + } + + public static ItemStack setStack(ItemStack aStack) { + return setStack(true, aStack); + } + + public static ItemStack setStack(boolean aUseBlackList, ItemStack aStack) { + if (GT_Utility.isStackInvalid(aStack)) return aStack; + ItemStack tStack = get(aUseBlackList, aStack); + if (GT_Utility.areStacksEqual(aStack, tStack)) return aStack; + aStack.func_150996_a(tStack.getItem()); + Items.feather.setDamage(aStack, Items.feather.getDamage(tStack)); + return aStack; + } + + public static ItemStack get(ItemStack aStack) { + return get(true, aStack); + } + + public static ItemStack get(boolean aUseBlackList, ItemStack aStack) { + return get(aUseBlackList, aStack, false); + } + + /** + * @param unsafe If true, it does not limit stack size by 64. + */ + public static ItemStack get(boolean aUseBlackList, ItemStack aStack, boolean unsafe) { + if (GT_Utility.isStackInvalid(aStack)) return null; + ItemData tPrefixMaterial = getAssociation(aStack); + if (tPrefixMaterial == null || !tPrefixMaterial.hasValidPrefixMaterialData() + || (aUseBlackList && tPrefixMaterial.mBlackListed)) return GT_Utility.copyOrNull(aStack); + if (aUseBlackList && !GregTech_API.sUnificationEntriesRegistered && isBlacklisted(aStack)) { + tPrefixMaterial.mBlackListed = true; + return GT_Utility.copyOrNull(aStack); + } + if (tPrefixMaterial.mUnificationTarget == null) + tPrefixMaterial.mUnificationTarget = sName2StackMap.get(tPrefixMaterial.toString()); + ItemStack rStack = tPrefixMaterial.mUnificationTarget; + if (GT_Utility.isStackInvalid(rStack)) return GT_Utility.copyOrNull(aStack); + ItemStack newStack; + if (unsafe) { + newStack = GT_Utility.copyAmountUnsafe(aStack.stackSize, rStack); + } else { + newStack = GT_Utility.copyAmount(aStack.stackSize, rStack); + } + // NBT is assigned by reference here, so mutating it may have unexpected side effects. + newStack.setTagCompound(aStack.getTagCompound()); + return newStack; + } + + /** + * Doesn't copy the returned stack or set quantity. Be careful and do not mutate it; intended only to optimize + * comparisons + */ + public static ItemStack get_nocopy(ItemStack aStack) { + return get_nocopy(true, aStack); + } + + /** + * Doesn't copy the returned stack or set quantity. Be careful and do not mutate it; intended only to optimize + * comparisons + */ + static ItemStack get_nocopy(boolean aUseBlackList, ItemStack aStack) { + if (GT_Utility.isStackInvalid(aStack)) return null; + ItemData tPrefixMaterial = getAssociation(aStack); + if (tPrefixMaterial == null || !tPrefixMaterial.hasValidPrefixMaterialData() + || (aUseBlackList && tPrefixMaterial.mBlackListed)) return aStack; + if (aUseBlackList && !GregTech_API.sUnificationEntriesRegistered && isBlacklisted(aStack)) { + tPrefixMaterial.mBlackListed = true; + return aStack; + } + if (tPrefixMaterial.mUnificationTarget == null) + tPrefixMaterial.mUnificationTarget = sName2StackMap.get(tPrefixMaterial.toString()); + ItemStack rStack = tPrefixMaterial.mUnificationTarget; + if (GT_Utility.isStackInvalid(rStack)) return aStack; + + // Yes, == and not .equals(). + // This check is primarily intended to optimize for the case where both rStack and aStack + // do not have NBT, and so we would be comparing null == null. + // + // Even if aStack and rStack may have equal NBT, we prefer to do an inexpensive + // new ItemStack() over the potentially expensive NBTTagCompound.equals(). + if (aStack.getTagCompound() == rStack.getTagCompound()) { + // Warning: rStack's stack size may not be equal to aStack's stack size. + return rStack; + } + + // Okay, okay, I lied, we actually do need to make a copy. + // This is to fix a long-standing bug where we were mutating NBT directly on rStack, + // which had unexpected and unpredictable ripple effects. + // + // We will do some custom copying here, to avoid ItemStack.copy(), + // which calls the potentially expensive NBTTagCompound.copy() + // NBT is assigned by reference here, so mutating it may have unexpected side effects. + ItemStack newStack = new ItemStack(rStack.getItem(), aStack.stackSize, Items.feather.getDamage(rStack)); + newStack.setTagCompound(aStack.getTagCompound()); + return newStack; + } + + /** + * Compares the first argument against an already-unificated second argument as if aUseBlackList was both true and + * false. + */ + public static boolean isInputStackEqual(ItemStack aStack, ItemStack unified_tStack) { + boolean alreadyCompared = false; + if (GT_Utility.isStackInvalid(aStack)) return false; + ItemData tPrefixMaterial = getAssociation(aStack); + ItemStack rStack = null; + if (tPrefixMaterial == null || !tPrefixMaterial.hasValidPrefixMaterialData()) + return GT_Utility.areStacksEqual(aStack, unified_tStack, true); + else if (tPrefixMaterial.mBlackListed) { + if (GT_Utility.areStacksEqual(aStack, unified_tStack, true)) return true; + else alreadyCompared = true; + } + if (!alreadyCompared && !GregTech_API.sUnificationEntriesRegistered && isBlacklisted(aStack)) { + tPrefixMaterial.mBlackListed = true; + if (GT_Utility.areStacksEqual(aStack, unified_tStack, true)) return true; + else alreadyCompared = true; + } + if (tPrefixMaterial.mUnificationTarget == null) + tPrefixMaterial.mUnificationTarget = sName2StackMap.get(tPrefixMaterial.toString()); + rStack = tPrefixMaterial.mUnificationTarget; + if (GT_Utility.isStackInvalid(rStack)) + return !alreadyCompared && GT_Utility.areStacksEqual(aStack, unified_tStack, true); + return GT_Utility.areStacksEqual(rStack, unified_tStack, true); + } + + public static List<ItemStack> getNonUnifiedStacks(Object obj) { + if (sUnificationTable.isEmpty() && !sItemStack2DataMap.isEmpty()) { + // use something akin to double check lock. this synchronization overhead is causing lag whenever my + // 5900x tries to do NEI lookup + synchronized (sUnificationTable) { + if (sUnificationTable.isEmpty() && !sItemStack2DataMap.isEmpty()) { + for (ItemStack tGTStack0 : sItemStack2DataMap.keySet()) { + ItemStack tStack0 = GT_ItemStack.internalCopyStack(tGTStack0); + ItemStack tStack1 = get_nocopy(false, tStack0); + if (!GT_Utility.areStacksEqual(tStack0, tStack1)) { + List<ItemStack> list = sUnificationTable.computeIfAbsent(tStack1, k -> new ArrayList<>()); + // greg's original code tries to dedupe the list using List#contains, which won't work + // on vanilla ItemStack. I removed it since it never worked and can be slow. + list.add(tStack0); + } + } + } + } + } + ItemStack[] aStacks = {}; + if (obj instanceof ItemStack) aStacks = new ItemStack[] { (ItemStack) obj }; + else if (obj instanceof ItemStack[]) aStacks = (ItemStack[]) obj; + else if (obj instanceof List) aStacks = (ItemStack[]) ((List<?>) obj).toArray(new ItemStack[0]); + List<ItemStack> rList = new ArrayList<>(); + for (ItemStack aStack : aStacks) { + if (aStack == null) continue; + rList.add(aStack); + List<ItemStack> tList = sUnificationTable.get(aStack); + if (tList != null) { + for (ItemStack tStack : tList) { + ItemStack tStack1 = GT_Utility.copyAmount(aStack.stackSize, tStack); + rList.add(tStack1); + } + } + } + return rList; + } + + public static void addItemData(ItemStack aStack, ItemData aData) { + if (GT_Utility.isStackValid(aStack) && getItemData(aStack) == null && aData != null) setItemData(aStack, aData); + } + + public static void addItemDataFromInputs(ItemStack output, Object... inputs) { + int length = inputs.length; + ItemData[] tData = new ItemData[length]; + for (int i = 0; i < length; i++) { + if (inputs[i] instanceof ItemStack) { + tData[i] = GT_OreDictUnificator.getItemData((ItemStack) inputs[i]); + } else if (inputs[i] instanceof ItemData) { + tData[i] = (ItemData) inputs[i]; + } else { + throw new IllegalArgumentException(); + } + } + if (GT_Utility.arrayContainsNonNull(tData)) { + GT_OreDictUnificator.addItemData(output, new ItemData(tData)); + } + } + + public static void setItemData(ItemStack aStack, ItemData aData) { + if (GT_Utility.isStackInvalid(aStack) || aData == null) return; + ItemData tData = getItemData(aStack); + if (tData == null || !tData.hasValidPrefixMaterialData()) { + if (tData != null) for (Object tObject : tData.mExtraData) + if (!aData.mExtraData.contains(tObject)) aData.mExtraData.add(tObject); + if (aStack.stackSize > 1) { + if (aData.mMaterial != null) aData.mMaterial.mAmount /= aStack.stackSize; + for (MaterialStack tMaterial : aData.mByProducts) tMaterial.mAmount /= aStack.stackSize; + aStack = GT_Utility.copyAmount(1, aStack); + } + sItemStack2DataMap.put(aStack, aData); + if (aData.hasValidMaterialData()) { + long tValidMaterialAmount = aData.mMaterial.mMaterial.contains(SubTag.NO_RECYCLING) ? 0 + : aData.mMaterial.mAmount >= 0 ? aData.mMaterial.mAmount : M; + for (MaterialStack tMaterial : aData.mByProducts) + tValidMaterialAmount += tMaterial.mMaterial.contains(SubTag.NO_RECYCLING) ? 0 + : tMaterial.mAmount >= 0 ? tMaterial.mAmount : M; + if (tValidMaterialAmount < M) GT_ModHandler.addToRecyclerBlackList(aStack); + } + if (mRunThroughTheList) { + if (GregTech_API.sLoadStarted) { + mRunThroughTheList = false; + for (Entry<ItemStack, ItemData> tEntry : sItemStack2DataMap.entrySet()) if (!tEntry.getValue() + .hasValidPrefixData() || tEntry.getValue().mPrefix.mAllowNormalRecycling) + GT_RecipeRegistrator.registerMaterialRecycling( + GT_ItemStack.internalCopyStack(tEntry.getKey()), + tEntry.getValue()); + } + } else { + if (!aData.hasValidPrefixData() || aData.mPrefix.mAllowNormalRecycling) + GT_RecipeRegistrator.registerMaterialRecycling(aStack, aData); + } + } else { + for (Object tObject : aData.mExtraData) + if (!tData.mExtraData.contains(tObject)) tData.mExtraData.add(tObject); + } + } + + public static void removeItemData(ItemStack aStack) { + if (GT_Utility.isStackInvalid(aStack)) { + return; + } + sItemStack2DataMap.remove(aStack); + } + + public static void addAssociation(OrePrefixes aPrefix, Materials aMaterial, ItemStack aStack, + boolean aBlackListed) { + if (aPrefix == null || aMaterial == null || GT_Utility.isStackInvalid(aStack)) return; + if (Items.feather.getDamage(aStack) == W) for (byte i = 0; i < 16; i++) + setItemData(GT_Utility.copyAmountAndMetaData(1, i, aStack), new ItemData(aPrefix, aMaterial, aBlackListed)); + setItemData(aStack, new ItemData(aPrefix, aMaterial, aBlackListed)); + } + + @Nullable + public static ItemData getItemData(ItemStack aStack) { + if (GT_Utility.isStackInvalid(aStack)) return null; + ItemData rData = sItemStack2DataMap.get(aStack); + if (rData == null) { // Try the lookup again but with wildcard damage value + rData = sItemStack2DataMap.get(GT_ItemStack.internalCopyStack(aStack, true)); + } + return rData; + } + + @Nullable + public static ItemData getAssociation(ItemStack aStack) { + ItemData rData = getItemData(aStack); + return rData != null && rData.hasValidPrefixMaterialData() ? rData : null; + } + + public static boolean isItemStackInstanceOf(ItemStack aStack, Object aName) { + if (GT_Utility.isStringInvalid(aName) || GT_Utility.isStackInvalid(aStack)) return false; + for (ItemStack tOreStack : getOresImmutable(aName.toString())) + if (GT_Utility.areStacksEqual(tOreStack, aStack, true)) return true; + return false; + } + + public static boolean isItemStackDye(ItemStack aStack) { + if (GT_Utility.isStackInvalid(aStack)) return false; + + for (Dyes tDye : Dyes.VALUES) if (isItemStackInstanceOf(aStack, tDye.toString())) return true; + + return false; + } + + public static boolean registerOre(OrePrefixes aPrefix, Object aMaterial, ItemStack aStack) { + return registerOre(aPrefix.get(aMaterial), aStack); + } + + public static boolean registerOre(Object aName, ItemStack aStack) { + if (aName == null || GT_Utility.isStackInvalid(aStack)) return false; + + String tName = aName.toString(); + + if (GT_Utility.isStringInvalid(tName)) return false; + + for (ItemStack itemStack : getOresImmutable(tName)) + if (GT_Utility.areStacksEqual(itemStack, aStack, true)) return false; + + isRegisteringOre++; + OreDictionary.registerOre(tName, GT_Utility.copyAmount(1, aStack)); + isRegisteringOre--; + return true; + } + + public static boolean isRegisteringOres() { + return isRegisteringOre > 0; + } + + public static boolean isAddingOres() { + return isAddingOre > 0; + } + + public static void resetUnificationEntries() { + for (ItemData tPrefixMaterial : sItemStack2DataMap.values()) tPrefixMaterial.mUnificationTarget = null; + } + + public static ItemStack getGem(MaterialStack aMaterial) { + return aMaterial == null ? null : getGem(aMaterial.mMaterial, aMaterial.mAmount); + } + + public static ItemStack getGem(Materials aMaterial, OrePrefixes aPrefix) { + return aMaterial == null ? null : getGem(aMaterial, aPrefix.mMaterialAmount); + } + + public static ItemStack getGem(Materials aMaterial, long aMaterialAmount) { + ItemStack rStack = null; + if (((aMaterialAmount >= M))) rStack = get(OrePrefixes.gem, aMaterial, aMaterialAmount / M); + if (rStack == null) { + if ((((aMaterialAmount * 2) % M == 0) || aMaterialAmount >= M * 16)) + rStack = get(OrePrefixes.gemFlawed, aMaterial, (aMaterialAmount * 2) / M); + if ((((aMaterialAmount * 4) >= M))) + rStack = get(OrePrefixes.gemChipped, aMaterial, (aMaterialAmount * 4) / M); + } + return rStack; + } + + public static ItemStack getDust(MaterialStack aMaterial) { + return aMaterial == null ? null : getDust(aMaterial.mMaterial, aMaterial.mAmount); + } + + public static ItemStack getDust(Materials aMaterial, OrePrefixes aPrefix) { + return aMaterial == null ? null : getDust(aMaterial, aPrefix.mMaterialAmount); + } + + public static ItemStack getDust(Materials aMaterial, long aMaterialAmount) { + if (aMaterialAmount <= 0) return null; + ItemStack rStack = null; + if (((aMaterialAmount % M == 0) || aMaterialAmount >= M * 16)) + rStack = get(OrePrefixes.dust, aMaterial, aMaterialAmount / M); + if (rStack == null && (((aMaterialAmount * 4) % M == 0) || aMaterialAmount >= M * 8)) + rStack = get(OrePrefixes.dustSmall, aMaterial, (aMaterialAmount * 4) / M); + if (rStack == null && (((aMaterialAmount * 9) >= M))) + rStack = get(OrePrefixes.dustTiny, aMaterial, (aMaterialAmount * 9) / M); + return rStack; + } + + public static ItemStack getIngot(MaterialStack aMaterial) { + return aMaterial == null ? null : getIngot(aMaterial.mMaterial, aMaterial.mAmount); + } + + public static ItemStack getIngot(Materials aMaterial, OrePrefixes aPrefix) { + return aMaterial == null ? null : getIngot(aMaterial, aPrefix.mMaterialAmount); + } + + public static ItemStack getIngot(Materials aMaterial, long aMaterialAmount) { + if (aMaterialAmount <= 0) return null; + ItemStack rStack = null; + if (((aMaterialAmount % (M * 9) == 0 && aMaterialAmount / (M * 9) > 1) || aMaterialAmount >= M * 72)) + rStack = get(OrePrefixes.block, aMaterial, aMaterialAmount / (M * 9)); + if (rStack == null && ((aMaterialAmount % M == 0) || aMaterialAmount >= M * 8)) + rStack = get(OrePrefixes.ingot, aMaterial, aMaterialAmount / M); + if (rStack == null && (((aMaterialAmount * 9) >= M))) + rStack = get(OrePrefixes.nugget, aMaterial, (aMaterialAmount * 9) / M); + return rStack; + } + + public static ItemStack getIngotOrDust(Materials aMaterial, long aMaterialAmount) { + if (aMaterialAmount <= 0) return null; + ItemStack rStack = getIngot(aMaterial, aMaterialAmount); + if (rStack == null) rStack = getDust(aMaterial, aMaterialAmount); + return rStack; + } + + public static ItemStack getIngotOrDust(MaterialStack aMaterial) { + ItemStack rStack = getIngot(aMaterial); + if (rStack == null) rStack = getDust(aMaterial); + return rStack; + } + + public static ItemStack getDustOrIngot(Materials aMaterial, long aMaterialAmount) { + if (aMaterialAmount <= 0) return null; + ItemStack rStack = getDust(aMaterial, aMaterialAmount); + if (rStack == null) rStack = getIngot(aMaterial, aMaterialAmount); + return rStack; + } + + public static ItemStack getDustOrIngot(MaterialStack aMaterial) { + ItemStack rStack = getDust(aMaterial); + if (rStack == null) rStack = getIngot(aMaterial); + return rStack; + } + + /** + * @return a Copy of the OreDictionary.getOres() List + */ + public static ArrayList<ItemStack> getOres(OrePrefixes aPrefix, Object aMaterial) { + return getOres(aPrefix.get(aMaterial)); + } + + /** + * @return a Copy of the OreDictionary.getOres() List + */ + public static ArrayList<ItemStack> getOres(Object aOreName) { + String aName = aOreName == null ? E : aOreName.toString(); + ArrayList<ItemStack> rList = new ArrayList<>(); + if (GT_Utility.isStringValid(aName)) rList.addAll(OreDictionary.getOres(aName)); + return rList; + } + + /** + * Fast version of {@link #getOres(Object)}, which doesn't call + * {@link System#arraycopy(Object, int, Object, int, int)} in {@link ArrayList#addAll} + */ + public static List<ItemStack> getOresImmutable(@Nullable Object aOreName) { + String aName = aOreName == null ? E : aOreName.toString(); + + return GT_Utility.isStringValid(aName) ? Collections.unmodifiableList(OreDictionary.getOres(aName)) + : Collections.emptyList(); + } +} diff --git a/src/main/java/gregtech/api/util/GT_OverclockCalculator.java b/src/main/java/gregtech/api/util/GT_OverclockCalculator.java new file mode 100644 index 0000000000..609a196e80 --- /dev/null +++ b/src/main/java/gregtech/api/util/GT_OverclockCalculator.java @@ -0,0 +1,631 @@ +package gregtech.api.util; + +import java.util.function.Supplier; + +import javax.annotation.Nonnull; + +import gregtech.api.enums.GT_Values; + +public class GT_OverclockCalculator { + + private static final double LOG2 = Math.log(2); + + /** + * Voltage the recipe will run at + */ + private long recipeVoltage = 0; + /* + * The amount of amps the recipe needs + */ + private long recipeAmperage = 1; + /** + * Voltage of the machine + */ + private long machineVoltage = 0; + /** + * Amperage of the machine + */ + private long machineAmperage = 1; + /** + * Duration of the recipe + */ + private int duration = 0; + /** + * The parallel the machine has when trying to overclock + */ + private int parallel = 1; + + /** + * The min heat required for the recipe + */ + private int recipeHeat = 0; + /** + * The heat the machine has when starting the recipe + */ + private int machineHeat = 0; + /** + * How much the duration should be divided by for each 1800K above recipe heat + */ + private double durationDecreasePerHeatOC = 4; + /** + * Whether to enable overclocking with heat like the EBF every 1800 heat difference + */ + private boolean heatOC; + /** + * Whether to enable heat discounts every 900 heat difference + */ + private boolean heatDiscount; + /** + * The value used for discount final eut per 900 heat + */ + private double heatDiscountExponent = 0.95; + + /** + * Discount for EUt at the beginning of calculating overclocks, like GT++ machines + */ + private double eutDiscount = 1; + /** + * Speeding/Slowing up/down the duration of a recipe at the beginning of calculating overclocks, like GT++ machines + */ + private double speedBoost = 1; + + /** + * How much the energy would be multiplied by per overclock available + */ + private double eutIncreasePerOC = 4; + /** + * How much the duration would be divided by per overclock made that isn't an overclock from HEAT + */ + private double durationDecreasePerOC = 2; + /** + * Whether to give EUt Discount when the duration goes below one tick + */ + private boolean oneTickDiscount; + /** + * Whether the multi should use amperage to overclock with an exponent. Incompatible with amperageOC + */ + private boolean laserOC; + /** + * Laser OC's penalty for using high amp lasers for overclocking. Like what the Adv. Assline is doing + */ + private double laserOCPenalty = 0.3; + /** + * Whether the multi should use amperage to overclock normally. Incompatible with laserOC + */ + private boolean amperageOC; + /** + * If the OC calculator should only do a given amount of overclocks. Mainly used in fusion reactors + */ + private boolean limitOverclocks; + /** + * Maximum amount of overclocks to perform, when limitOverclocks = true + */ + private int maxOverclocks; + /** + * How many overclocks have been performed + */ + private int overclockCount; + /** + * How many overclocks were performed with heat out of the overclocks we had + */ + private int heatOverclockCount; + /** + * A supplier, which is used for machines which have a custom way of calculating duration, like Neutron Activator + */ + private Supplier<Double> durationUnderOneTickSupplier; + /** + * Should we actually try to calculate overclocking + */ + private boolean noOverclock; + /** + * variable to check whether the overclocks have been calculated + */ + private boolean calculated; + + private static final int HEAT_DISCOUNT_THRESHOLD = 900; + private static final int HEAT_PERFECT_OVERCLOCK_THRESHOLD = 1800; + + /** + * Creates calculator that doesn't do OC at all. Will use recipe duration. + */ + public static GT_OverclockCalculator ofNoOverclock(@Nonnull GT_Recipe recipe) { + return ofNoOverclock(recipe.mEUt, recipe.mDuration); + } + + /** + * Creates calculator that doesn't do OC at all, with set duration. + */ + public static GT_OverclockCalculator ofNoOverclock(long eut, int duration) { + return new GT_OverclockCalculator().setRecipeEUt(eut) + .setDuration(duration) + .setEUt(eut) + .setNoOverclock(true); + } + + /** + * An Overclock helper for calculating overclocks in many different situations + */ + public GT_OverclockCalculator() {} + + /** + * @param recipeEUt Sets the Recipe's starting voltage + */ + @Nonnull + public GT_OverclockCalculator setRecipeEUt(long recipeEUt) { + this.recipeVoltage = recipeEUt; + return this; + } + + /** + * @param machineVoltage Sets the EUt that the machine can use. This is the voltage of the machine + */ + @Nonnull + public GT_OverclockCalculator setEUt(long machineVoltage) { + this.machineVoltage = machineVoltage; + return this; + } + + /** + * @param duration Sets the duration of the recipe + */ + @Nonnull + public GT_OverclockCalculator setDuration(int duration) { + this.duration = duration; + return this; + } + + /** + * @param machineAmperage Sets the Amperage that the machine can support + */ + @Nonnull + public GT_OverclockCalculator setAmperage(long machineAmperage) { + this.machineAmperage = machineAmperage; + return this; + } + + /** + * @param recipeAmperage Sets the Amperage of the recipe + */ + @Nonnull + public GT_OverclockCalculator setRecipeAmperage(long recipeAmperage) { + this.recipeAmperage = recipeAmperage; + return this; + } + + /** + * Enables Perfect OC in calculation + */ + @Nonnull + public GT_OverclockCalculator enablePerfectOC() { + this.durationDecreasePerOC = 4; + return this; + } + + /** + * Set if we should be calculating overclocking using EBF's perfectOC + */ + @Nonnull + public GT_OverclockCalculator setHeatOC(boolean heatOC) { + this.heatOC = heatOC; + return this; + } + + /** + * Sets if we should add a heat discount at the end of calculating an overclock, just like the EBF + */ + @Nonnull + public GT_OverclockCalculator setHeatDiscount(boolean heatDiscount) { + this.heatDiscount = heatDiscount; + return this; + } + + /** + * Sets the starting heat of the recipe + */ + @Nonnull + public GT_OverclockCalculator setRecipeHeat(int recipeHeat) { + this.recipeHeat = recipeHeat; + return this; + } + + /** + * Sets the heat of the coils on the machine + */ + @Nonnull + public GT_OverclockCalculator setMachineHeat(int machineHeat) { + this.machineHeat = machineHeat; + return this; + } + + /** + * Sets an EUtDiscount. 0.9 is 10% less energy. 1.1 is 10% more energy + */ + @Nonnull + public GT_OverclockCalculator setEUtDiscount(float aEUtDiscount) { + this.eutDiscount = aEUtDiscount; + return this; + } + + /** + * Sets a Speed Boost for the multiblock. 0.9 is 10% faster. 1.1 is 10% slower + */ + @Nonnull + public GT_OverclockCalculator setSpeedBoost(float aSpeedBoost) { + this.speedBoost = aSpeedBoost; + return this; + } + + /** + * Sets the parallel that the multiblock uses + */ + @Nonnull + public GT_OverclockCalculator setParallel(int aParallel) { + this.parallel = aParallel; + return this; + } + + /** + * Sets the heat discount during OC calculation if HeatOC is used. Default: 0.95 = 5% discount Used like a EU/t + * Discount + */ + @Nonnull + public GT_OverclockCalculator setHeatDiscountMultiplier(float heatDiscountExponent) { + this.heatDiscountExponent = heatDiscountExponent; + return this; + } + + /** + * @deprecated Deprecated in favor of {@link #setHeatPerfectOC(double)}. Calls {@link #setHeatPerfectOC(double)} + * where the given value is 2^heatPerfectOC + */ + @Deprecated + @Nonnull + public GT_OverclockCalculator setHeatPerfectOC(int heatPerfectOC) { + return setHeatPerfectOC(Math.pow(2, heatPerfectOC)); + } + + /** + * Sets the Overclock that should be calculated when a heat OC is applied. + */ + @Nonnull + public GT_OverclockCalculator setHeatPerfectOC(double heatPerfectOC) { + if (heatPerfectOC <= 0) throw new IllegalArgumentException("Heat OC can't be a negative number or zero"); + this.durationDecreasePerHeatOC = heatPerfectOC; + return this; + } + + /** + * @deprecated Deprecated in favor of {@link #setEUtIncreasePerOC(double)}. Calls + * {@link #setEUtIncreasePerOC(double)} where the given value is 2^eutIncreasePerOC + */ + @Deprecated + @Nonnull + public GT_OverclockCalculator setEUtIncreasePerOC(int eutIncreasePerOC) { + return setEUtIncreasePerOC(Math.pow(2, eutIncreasePerOC)); + } + + /** + * Sets the amount that the eut would be multiplied by per overclock. Do not set as 1(ONE) if the duration decrease + * is also 1(ONE)! + */ + @Nonnull + public GT_OverclockCalculator setEUtIncreasePerOC(double eutIncreasePerOC) { + if (eutIncreasePerOC <= 0) + throw new IllegalArgumentException("EUt increase can't be a negative number or zero"); + this.eutIncreasePerOC = eutIncreasePerOC; + return this; + } + + /** + * @deprecated Deprecated in favor of {@link #setDurationDecreasePerOC(double)}. Calls + * {@link #setDurationDecreasePerOC(double)} where the given value is 2^durationDecreasePerOC + */ + @Deprecated + @Nonnull + public GT_OverclockCalculator setDurationDecreasePerOC(int durationDecreasePerOC) { + return setDurationDecreasePerOC(Math.pow(2, durationDecreasePerOC)); + } + + /** + * Sets the amount that the duration would be divided by per overclock. Do not set as 1(ONE) if the eut increase is + * also 1(ONE)! + */ + @Nonnull + public GT_OverclockCalculator setDurationDecreasePerOC(double durationDecreasePerOC) { + if (durationDecreasePerOC <= 0) + throw new IllegalArgumentException("Duration decrease can't be a negative number or zero"); + this.durationDecreasePerOC = durationDecreasePerOC; + return this; + } + + /** + * Set One Tick Discount on EUt based on Duration Decrease Per Overclock. This functions the same as single blocks. + */ + @Nonnull + public GT_OverclockCalculator setOneTickDiscount(boolean oneTickDiscount) { + this.oneTickDiscount = oneTickDiscount; + return this; + } + + /** + * Limit the amount of overclocks that can be performed, regardless of how much power is available. Mainly used for + * fusion reactors. + */ + @Nonnull + public GT_OverclockCalculator limitOverclockCount(int maxOverclocks) { + this.limitOverclocks = true; + this.maxOverclocks = maxOverclocks; + return this; + } + + @Nonnull + public GT_OverclockCalculator setLaserOC(boolean laserOC) { + this.laserOC = laserOC; + return this; + } + + @Nonnull + public GT_OverclockCalculator setAmperageOC(boolean amperageOC) { + this.amperageOC = amperageOC; + return this; + } + + @Nonnull + public GT_OverclockCalculator setLaserOCPenalty(double laserOCPenalty) { + this.laserOCPenalty = laserOCPenalty; + return this; + } + + /** + * Set a supplier for calculating custom duration for when its needed under one tick + */ + @Nonnull + public GT_OverclockCalculator setDurationUnderOneTickSupplier(Supplier<Double> supplier) { + this.durationUnderOneTickSupplier = supplier; + return this; + } + + /** + * Sets if we should do overclocking or not + */ + @Nonnull + public GT_OverclockCalculator setNoOverclock(boolean noOverclock) { + this.noOverclock = noOverclock; + return this; + } + + /** + * Call this when all values have been put it. + */ + @Nonnull + public GT_OverclockCalculator calculate() { + if (calculated) { + throw new IllegalStateException("Tried to calculate overclocks twice"); + } + calculateOverclock(); + calculated = true; + return this; + } + + private void calculateOverclock() { + duration = (int) Math.ceil(duration * speedBoost); + if (noOverclock) { + recipeVoltage = calculateFinalRecipeEUt(calculateHeatDiscountMultiplier()); + return; + } + if (laserOC && amperageOC) { + throw new IllegalStateException("Tried to calculate overclock with both laser and amperage overclocking"); + } + double heatDiscountMultiplier = calculateHeatDiscountMultiplier(); + if (heatOC) { + heatOverclockCount = calculateAmountOfHeatOverclocks(); + } + + double recipePowerTier = calculateRecipePowerTier(heatDiscountMultiplier); + double machinePowerTier = calculateMachinePowerTier(); + + overclockCount = calculateAmountOfNeededOverclocks(machinePowerTier, recipePowerTier); + if (!amperageOC) { + overclockCount = Math.min(overclockCount, calculateRecipeToMachineVoltageDifference()); + } + + // Not just a safeguard. This also means that you can run a 1.2A recipe on a single hatch for a regular gt + // multi. + // This is intended, including the fact that you don't get an OC with a one tier upgrade in that case. + overclockCount = Math.max(overclockCount, 0); + + overclockCount = limitOverclocks ? Math.min(maxOverclocks, overclockCount) : overclockCount; + heatOverclockCount = Math.min(heatOverclockCount, overclockCount); + recipeVoltage = (long) Math.floor(recipeVoltage * Math.pow(eutIncreasePerOC, overclockCount)); + duration = (int) Math.floor(duration / Math.pow(durationDecreasePerOC, overclockCount - heatOverclockCount)); + duration = (int) Math.floor(duration / Math.pow(durationDecreasePerHeatOC, heatOverclockCount)); + if (oneTickDiscount) { + recipeVoltage = (long) Math.floor( + recipeVoltage + / Math.pow(durationDecreasePerOC, ((int) (machinePowerTier - recipePowerTier - overclockCount)))); + if (recipeVoltage < 1) { + recipeVoltage = 1; + } + } + + if (laserOC) { + calculateLaserOC(); + } + + if (duration < 1) { + duration = 1; + } + + recipeVoltage = calculateFinalRecipeEUt(heatDiscountMultiplier); + } + + private double calculateRecipePowerTier(double heatDiscountMultiplier) { + return calculatePowerTier(recipeVoltage * parallel * eutDiscount * heatDiscountMultiplier * recipeAmperage); + } + + private double calculateMachinePowerTier() { + return calculatePowerTier( + machineVoltage * (amperageOC ? machineAmperage : Math.min(machineAmperage, parallel))); + } + + private int calculateRecipeToMachineVoltageDifference() { + return (int) (Math.ceil(calculatePowerTier(machineVoltage)) - Math.ceil(calculatePowerTier(recipeVoltage))); + } + + private double calculatePowerTier(double voltage) { + return 1 + Math.max(0, (Math.log(voltage) / LOG2) - 5) / 2; + } + + private long calculateFinalRecipeEUt(double heatDiscountMultiplier) { + return (long) Math.ceil(recipeVoltage * eutDiscount * heatDiscountMultiplier * parallel * recipeAmperage); + } + + private int calculateAmountOfHeatOverclocks() { + return Math.min( + (machineHeat - recipeHeat) / HEAT_PERFECT_OVERCLOCK_THRESHOLD, + calculateAmountOfOverclocks( + calculateMachinePowerTier(), + calculateRecipePowerTier(calculateHeatDiscountMultiplier()))); + } + + /** + * Calculate maximum possible overclocks ignoring if we are going to go under 1 tick + */ + private int calculateAmountOfOverclocks(double machinePowerTier, double recipePowerTier) { + return (int) (machinePowerTier - recipePowerTier); + } + + /** + * Calculates the amount of overclocks needed to reach 1 ticking + * + * Here we limit "the tier difference overclock" amount to a number of overclocks needed to reach 1 tick duration, + * for example: + * + * recipe initial duration = 250 ticks (12,5 seconds LV(1)) + * we have LCR with IV(5) energy hatch, which overclocks at 4/4 rate + * + * log_4 (250) ~ 3,98 is the number of overclocks needed to reach 1 tick + * + * to calculate log_a(b) we can use the log property: + * log_a(b) = log_c(b) / log_c(a) + * in our case we use natural log base as 'c' + * + * as a final step we apply Math.ceil(), + * otherwise for fractional nums like 3,98 we will never reach 1 tick + */ + private int calculateAmountOfNeededOverclocks(double machinePowerTier, double recipePowerTier) { + return (int) Math.min( + calculateAmountOfOverclocks(machinePowerTier, recipePowerTier), + Math.ceil(Math.log(duration) / Math.log(durationDecreasePerOC))); + } + + private double calculateHeatDiscountMultiplier() { + int heatDiscounts = heatDiscount ? (machineHeat - recipeHeat) / HEAT_DISCOUNT_THRESHOLD : 0; + return Math.pow(heatDiscountExponent, heatDiscounts); + } + + private void calculateLaserOC() { + long inputEut = machineVoltage * machineAmperage; + double currentPenalty = eutIncreasePerOC + laserOCPenalty; + while (inputEut > recipeVoltage * currentPenalty && recipeVoltage * currentPenalty > 0 && duration > 1) { + duration /= durationDecreasePerOC; + recipeVoltage *= currentPenalty; + currentPenalty += laserOCPenalty; + } + } + + /** + * @return The consumption after overclock has been calculated + */ + public long getConsumption() { + if (!calculated) { + throw new IllegalStateException("Tried to get consumption before calculating"); + } + return recipeVoltage; + } + + /** + * @return The duration of the recipe after overclock has been calculated + */ + public int getDuration() { + if (!calculated) { + throw new IllegalStateException("Tried to get duration before calculating"); + } + return duration; + } + + /** + * @return Number of performed overclocks + */ + public int getPerformedOverclocks() { + if (!calculated) { + throw new IllegalStateException("Tried to get performed overclocks before calculating"); + } + return overclockCount; + } + + /** + * Returns duration as a double to show how much it is overclocking too much to determine extra parallel. This + * doesn't count as calculating + */ + public double calculateDurationUnderOneTick() { + if (durationUnderOneTickSupplier != null) return durationUnderOneTickSupplier.get(); + if (noOverclock) return duration; + int normalOverclocks = calculateAmountOfOverclocks( + calculateMachinePowerTier(), + calculateRecipePowerTier(calculateHeatDiscountMultiplier())); + normalOverclocks = limitOverclocks ? Math.min(normalOverclocks, maxOverclocks) : normalOverclocks; + int heatOverclocks = Math.min(calculateAmountOfHeatOverclocks(), normalOverclocks); + return (duration * speedBoost) / (Math.pow(durationDecreasePerOC, normalOverclocks - heatOverclocks) + * Math.pow(durationDecreasePerHeatOC, heatOverclocks)); + } + + /** + * Returns the EUt consumption one would get from overclocking under 1 tick + * This Doesn't count as calculating + * + * @param originalMaxParallel Parallels which are of the actual machine before the overclocking extra ones + */ + public long calculateEUtConsumptionUnderOneTick(int originalMaxParallel, int currentParallel) { + if (noOverclock) return recipeVoltage; + double heatDiscountMultiplier = calculateHeatDiscountMultiplier(); + // So what we need to do here is as follows: + // - First we need to figure out what out parallel multiplier for getting to that OC was + // - Second we need to find how many of those were from heat overclocks + // - Third we need to find how many were from normal overclocking. + // = For that we need to find how much better heat overclocks are compared to normal ones + // = Then remove that many from our normal overclocks + // - Fourth we find how many total overclocks we have + // - Fifth we find how many of those are needed to one tick + // - Finally we calculate the formula + // = The energy increase from our overclocks for parallel + // = The energy increase from our overclock to reach maximum under one tick potential + // =- NOTE: This will always cause machine to use full power no matter what. Otherwise it creates many + // anomalies. + // = Everything else for recipe voltage is also calculated here. + + double parallelMultiplierFromOverclocks = (double) currentParallel / originalMaxParallel; + double amountOfParallelHeatOverclocks = Math.min( + Math.log(parallelMultiplierFromOverclocks) / Math.log(durationDecreasePerHeatOC), + calculateAmountOfHeatOverclocks()); + double amountOfParallelOverclocks = Math.log(parallelMultiplierFromOverclocks) / Math.log(durationDecreasePerOC) + - amountOfParallelHeatOverclocks * (durationDecreasePerHeatOC - durationDecreasePerOC); + double machineTier = calculateMachinePowerTier(); + double recipeTier = calculateRecipePowerTier(heatDiscountMultiplier); + double amountOfTotalOverclocks = calculateAmountOfOverclocks(machineTier, recipeTier); + if (recipeVoltage <= GT_Values.V[0]) { + amountOfTotalOverclocks = Math.min(amountOfTotalOverclocks, calculateRecipeToMachineVoltageDifference()); + } + amountOfTotalOverclocks = limitOverclocks ? Math.min(amountOfTotalOverclocks, maxOverclocks) + : amountOfTotalOverclocks; + return (long) Math.ceil( + recipeVoltage * Math.pow(eutIncreasePerOC, amountOfParallelOverclocks + amountOfParallelHeatOverclocks) + * Math.pow( + eutIncreasePerOC, + amountOfTotalOverclocks - (amountOfParallelOverclocks + amountOfParallelHeatOverclocks)) + * originalMaxParallel + * eutDiscount + * recipeAmperage + * heatDiscountMultiplier); + } +} diff --git a/src/main/java/gregtech/api/util/GT_PCBFactoryManager.java b/src/main/java/gregtech/api/util/GT_PCBFactoryManager.java new file mode 100644 index 0000000000..990e9bd174 --- /dev/null +++ b/src/main/java/gregtech/api/util/GT_PCBFactoryManager.java @@ -0,0 +1,25 @@ +package gregtech.api.util; + +import com.google.common.collect.HashBiMap; + +import gregtech.api.enums.Materials; + +public class GT_PCBFactoryManager { + + private static final HashBiMap<Materials, Integer> mPlasticTiers = HashBiMap.create(); + public static int mTiersOfPlastics = 0; + + public static void addPlasticTier(Materials aMaterial, int aTier) { + mPlasticTiers.put(aMaterial, aTier); + mTiersOfPlastics++; + } + + public static int getPlasticTier(Materials aMaterial) { + return mPlasticTiers.get(aMaterial); + } + + public static Materials getPlasticMaterialFromTier(int aTier) { + return mPlasticTiers.inverse() + .get(aTier); + } +} diff --git a/src/main/java/gregtech/api/util/GT_ParallelHelper.java b/src/main/java/gregtech/api/util/GT_ParallelHelper.java new file mode 100644 index 0000000000..141ea35e9e --- /dev/null +++ b/src/main/java/gregtech/api/util/GT_ParallelHelper.java @@ -0,0 +1,717 @@ +package gregtech.api.util; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Objects; +import java.util.Random; +import java.util.function.Function; + +import javax.annotation.Nonnull; + +import net.minecraft.item.ItemStack; +import net.minecraftforge.fluids.FluidStack; + +import gregtech.api.interfaces.tileentity.IRecipeLockable; +import gregtech.api.interfaces.tileentity.IVoidable; +import gregtech.api.logic.FluidInventoryLogic; +import gregtech.api.logic.ItemInventoryLogic; +import gregtech.api.objects.XSTR; +import gregtech.api.recipe.RecipeMap; +import gregtech.api.recipe.check.CheckRecipeResult; +import gregtech.api.recipe.check.CheckRecipeResultRegistry; +import gregtech.api.recipe.check.SingleRecipeCheck; + +@SuppressWarnings({ "unused", "UnusedReturnValue" }) +public class GT_ParallelHelper { + + private static final double MAX_BATCH_MODE_TICK_TIME = 128; + /** + * Machine used for calculation + */ + private IVoidable machine; + /** + * Machine used for single recipe locking calculation + */ + private IRecipeLockable singleRecipeMachine; + /** + * Is locked to a single recipe? + */ + private boolean isRecipeLocked; + /** + * Recipe used when trying to calculate parallels + */ + private GT_Recipe recipe; + /** + * EUt available to the multiblock (This should be the total eut available) + */ + private long availableEUt; + /** + * The current parallel possible for the multiblock + */ + private int currentParallel = 0; + /** + * The maximum possible parallel possible for the multiblock + */ + private int maxParallel = 1; + /** + * The Batch Modifier applied when batch mode is enabled. 1 does nothing. 2 doubles max possible + * parallel, but also duration + */ + private int batchModifier = 1; + /** + * The inputs of the multiblock for the current recipe check + */ + private ItemStack[] itemInputs; + /** + * The inputs of the machine for current recipe check + */ + private ItemInventoryLogic itemInputInventory; + /** + * The output item inventory of the machine + */ + private ItemInventoryLogic itemOutputInventory; + /** + * The outputs of the recipe with the applied parallel + */ + private ItemStack[] itemOutputs; + /** + * The inputs of the multiblock for the current recipe check + */ + private FluidStack[] fluidInputs; + /** + * The inputs of the machine for the current recipe check + */ + private FluidInventoryLogic fluidInputInventory; + /** + * The output fluid inventory of the machine; + */ + private FluidInventoryLogic fluidOutputInventory; + /** + * The outputs of the recipe with the applied parallel + */ + private FluidStack[] fluidOutputs; + /** + * Does the multi have void protection enabled for items + */ + private boolean protectExcessItem; + /** + * Does the multi have void protection enabled for fluids + */ + private boolean protectExcessFluid; + /** + * Should the Parallel Helper automatically consume for the multi + */ + private boolean consume; + /** + * Is batch mode turned on? + */ + private boolean batchMode; + /** + * Should the Parallel Helper automatically calculate the outputs of the recipe with current parallel? + */ + private boolean calculateOutputs; + /** + * Has the Parallel Helper been built? + */ + private boolean built; + /** + * What is the duration multiplier with batch mode enabled + */ + private double durationMultiplier; + /** + * Modifier which is applied on the recipe eut. Useful for GT++ machines + */ + private float eutModifier = 1; + /** + * Multiplier that is applied on the output chances + */ + private double chanceMultiplier = 1; + /** + * Multiplier by which the output will be multiplied + */ + private int outputMultiplier = 1; + /** + * Method for calculating max parallel from given inputs. + */ + private MaxParallelCalculator maxParallelCalculator = GT_Recipe::maxParallelCalculatedByInputs; + /** + * Method for consuming inputs after determining how many parallels it can execute. + */ + private InputConsumer inputConsumer = GT_Recipe::consumeInput; + + /** + * Calculator to use for overclocking + */ + private GT_OverclockCalculator calculator; + @Nonnull + private CheckRecipeResult result = CheckRecipeResultRegistry.NONE; + + private Function<Integer, ItemStack[]> customItemOutputCalculation; + + private Function<Integer, FluidStack[]> customFluidOutputCalculation; + + /** + * MuTE Mode this is a mode for changing how the GT_ParallelHelper works as Mutes don't use ItemStack and FluidStack + * arrays for inputs + */ + private boolean muteMode = false; + + public GT_ParallelHelper() {} + + /** + * Sets machine, with current configuration for void protection mode. + */ + @Nonnull + public GT_ParallelHelper setMachine(IVoidable machine) { + return setMachine(machine, machine.protectsExcessItem(), machine.protectsExcessFluid()); + } + + /** + * Sets machine, with void protection mode forcibly. + */ + @Nonnull + public GT_ParallelHelper setMachine(IVoidable machine, boolean protectExcessItem, boolean protectExcessFluid) { + this.protectExcessItem = protectExcessItem; + this.protectExcessFluid = protectExcessFluid; + this.machine = machine; + return this; + } + + /** + * Sets the recipe, which will be used for the parallel calculation + */ + @Nonnull + public GT_ParallelHelper setRecipe(@Nonnull GT_Recipe aRecipe) { + recipe = Objects.requireNonNull(aRecipe); + return this; + } + + @Nonnull + public GT_ParallelHelper setRecipeLocked(IRecipeLockable singleRecipeMachine, boolean isRecipeLocked) { + this.singleRecipeMachine = singleRecipeMachine; + this.isRecipeLocked = isRecipeLocked; + return this; + } + + /** + * Sets the items available for the recipe check + */ + @Nonnull + public GT_ParallelHelper setItemInputs(ItemStack... aItemInputs) { + this.itemInputs = aItemInputs; + return this; + } + + /** + * Sets the fluid inputs available for the recipe check + */ + @Nonnull + public GT_ParallelHelper setFluidInputs(FluidStack... aFluidInputs) { + this.fluidInputs = aFluidInputs; + return this; + } + + /** + * Sets the available eut when trying for more parallels + */ + @Nonnull + public GT_ParallelHelper setAvailableEUt(long aAvailableEUt) { + this.availableEUt = aAvailableEUt; + return this; + } + + /** + * Sets the modifier for recipe eut. 1 does nothing 0.9 is 10% less. 1.1 is 10% more + */ + @Nonnull + public GT_ParallelHelper setEUtModifier(float aEUtModifier) { + this.eutModifier = aEUtModifier; + return this; + } + + /** + * Sets the multiplier that is applied on output chances. 1 does nothing. 0.9 is 10% less. 1.1 is 10% more. + * Only useful for item outputs for sure. + */ + @Nonnull + public GT_ParallelHelper setChanceMultiplier(double chanceMultiplier) { + this.chanceMultiplier = chanceMultiplier; + return this; + } + + /** + * Sets the item/fluid output multiplier. 1 does nothing. 2 doubles the item and fluid outputs. + */ + @Nonnull + public GT_ParallelHelper setOutputMultiplier(int outputMultiplier) { + this.outputMultiplier = outputMultiplier; + return this; + } + + @Nonnull + public GT_ParallelHelper setCalculator(GT_OverclockCalculator calculator) { + this.calculator = calculator; + return this; + } + + /** + * Set if we should consume inputs or not when trying for parallels + * + * @param consume Should we consume inputs + */ + @Nonnull + public GT_ParallelHelper setConsumption(boolean consume) { + this.consume = consume; + return this; + } + + /** + * Sets the MaxParallel a multi can handle + */ + @Nonnull + public GT_ParallelHelper setMaxParallel(int maxParallel) { + this.maxParallel = maxParallel; + return this; + } + + /** + * Enables Batch mode. Can do up to an additional processed recipes of mCurrentParallel * mBatchModifier A batch + * modifier of 1 does nothing + */ + @Nonnull + public GT_ParallelHelper enableBatchMode(int batchModifier) { + this.batchMode = batchModifier > 1; + this.batchModifier = batchModifier; + return this; + } + + /** + * Sets if we should calculate outputs with the parallels we found or not + * + * @param calculateOutputs Should we calculate outputs with the helper or not + */ + @Nonnull + public GT_ParallelHelper setOutputCalculation(boolean calculateOutputs) { + this.calculateOutputs = calculateOutputs; + return this; + } + + /** + * Set a custom way to calculate item outputs. You are given the amount of parallels and must return an ItemStack + * array + */ + @Nonnull + public GT_ParallelHelper setCustomItemOutputCalculation(Function<Integer, ItemStack[]> custom) { + customItemOutputCalculation = custom; + return this; + } + + /** + * Set a custom way to calculate item outputs. You are given the amount of parallels and must return a FluidStack + * array + */ + @Nonnull + public GT_ParallelHelper setCustomFluidOutputCalculation(Function<Integer, FluidStack[]> custom) { + customFluidOutputCalculation = custom; + return this; + } + + @Nonnull + public GT_ParallelHelper setMuTEMode(boolean muteMode) { + this.muteMode = muteMode; + return this; + } + + @Nonnull + public GT_ParallelHelper setItemInputInventory(ItemInventoryLogic itemInputInventory) { + this.itemInputInventory = itemInputInventory; + return this; + } + + @Nonnull + public GT_ParallelHelper setFluidInputInventory(FluidInventoryLogic fluidInputInventory) { + this.fluidInputInventory = fluidInputInventory; + return this; + } + + /** + * Sets method for calculating max parallel from given inputs. + */ + public GT_ParallelHelper setMaxParallelCalculator(MaxParallelCalculator maxParallelCalculator) { + this.maxParallelCalculator = maxParallelCalculator; + return this; + } + + /** + * Sets method for consuming inputs after determining how many parallels it can execute. + */ + public GT_ParallelHelper setInputConsumer(InputConsumer inputConsumer) { + this.inputConsumer = inputConsumer; + return this; + } + + @Nonnull + public GT_ParallelHelper setItemOutputInventory(ItemInventoryLogic itemOutputInventory) { + this.itemOutputInventory = itemOutputInventory; + return this; + } + + @Nonnull + public GT_ParallelHelper setFluidOutputInventory(FluidInventoryLogic fluidOutputInventory) { + this.fluidOutputInventory = fluidOutputInventory; + return this; + } + + /** + * Finishes the GT_ParallelHelper. Anything changed after this will not effect anything + */ + @Nonnull + public GT_ParallelHelper build() { + if (built) { + throw new IllegalStateException("Tried to build twice"); + } + if (recipe == null) { + throw new IllegalStateException("Recipe is not set"); + } + built = true; + determineParallel(); + return this; + } + + /** + * @return The current parallels possible by the multiblock + */ + public int getCurrentParallel() { + if (!built) { + throw new IllegalStateException("Tried to get parallels before building"); + } + return currentParallel; + } + + /** + * @return The duration multiplier if batch mode was enabled for the multiblock + */ + public double getDurationMultiplierDouble() { + if (!built) { + throw new IllegalStateException("Tried to get duration multiplier before building"); + } + if (batchMode && durationMultiplier > 0) { + return durationMultiplier; + } + return 1; + } + + /** + * @return The ItemOutputs from the recipe + */ + @Nonnull + public ItemStack[] getItemOutputs() { + if (!built || !calculateOutputs) { + throw new IllegalStateException( + "Tried to get item outputs before building or without enabling calculation of outputs"); + } + return itemOutputs; + } + + /** + * @return The FluidOutputs from the recipe + */ + @Nonnull + public FluidStack[] getFluidOutputs() { + if (!built || !calculateOutputs) { + throw new IllegalStateException( + "Tried to get fluid outputs before building or without enabling calculation of outputs"); + } + return fluidOutputs; + } + + /** + * @return The result of why a recipe could've failed or succeeded + */ + @Nonnull + public CheckRecipeResult getResult() { + if (!built) { + throw new IllegalStateException("Tried to get recipe result before building"); + } + return result; + } + + /** + * Called by build(). Determines the parallels and everything else that needs to be done at build time + */ + protected void determineParallel() { + if (maxParallel <= 0) { + return; + } + if (itemInputs == null) { + itemInputs = new ItemStack[0]; + } + if (fluidInputs == null) { + fluidInputs = new FluidStack[0]; + } + + if (!consume) { + copyInputs(); + } + + if (calculator == null) { + calculator = new GT_OverclockCalculator().setEUt(availableEUt) + .setRecipeEUt(recipe.mEUt) + .setDuration(recipe.mDuration) + .setEUtDiscount(eutModifier); + } + + final int tRecipeEUt = (int) Math.ceil(recipe.mEUt * eutModifier); + if (availableEUt < tRecipeEUt) { + result = CheckRecipeResultRegistry.insufficientPower(tRecipeEUt); + return; + } + + // Save the original max parallel before calculating our overclocking under 1 tick + int originalMaxParallel = maxParallel; + double tickTimeAfterOC = calculator.setParallel(originalMaxParallel) + .calculateDurationUnderOneTick(); + if (tickTimeAfterOC < 1) { + maxParallel = GT_Utility.safeInt((long) (maxParallel / tickTimeAfterOC), 0); + } + + int maxParallelBeforeBatchMode = maxParallel; + if (batchMode) { + maxParallel = GT_Utility.safeInt((long) maxParallel * batchModifier, 0); + } + + final ItemStack[] truncatedItemOutputs = recipe.mOutputs != null + ? Arrays.copyOfRange(recipe.mOutputs, 0, Math.min(machine.getItemOutputLimit(), recipe.mOutputs.length)) + : new ItemStack[0]; + final FluidStack[] truncatedFluidOutputs = recipe.mFluidOutputs != null ? Arrays + .copyOfRange(recipe.mFluidOutputs, 0, Math.min(machine.getFluidOutputLimit(), recipe.mFluidOutputs.length)) + : new FluidStack[0]; + + SingleRecipeCheck recipeCheck = null; + SingleRecipeCheck.Builder tSingleRecipeCheckBuilder = null; + if (isRecipeLocked && singleRecipeMachine != null) { + recipeCheck = singleRecipeMachine.getSingleRecipeCheck(); + if (recipeCheck == null) { + // Machine is configured to lock to a single recipe, but haven't built the recipe checker yet. + // Build the checker on next successful recipe. + RecipeMap<?> recipeMap = singleRecipeMachine.getRecipeMap(); + if (recipeMap != null) { + tSingleRecipeCheckBuilder = SingleRecipeCheck.builder(recipeMap) + .setBefore(itemInputs, fluidInputs); + } + } + } + + // Let's look at how many parallels we can get with void protection + if (protectExcessItem || protectExcessFluid) { + if (machine == null && !muteMode) { + throw new IllegalStateException("Tried to calculate void protection, but machine is not set"); + } + VoidProtectionHelper voidProtectionHelper = new VoidProtectionHelper(); + voidProtectionHelper.setMachine(machine) + .setItemOutputs(truncatedItemOutputs) + .setFluidOutputs(truncatedFluidOutputs) + .setChangeGetter(recipe::getOutputChance) + .setOutputMultiplier(outputMultiplier) + .setChanceMultiplier(chanceMultiplier) + .setMaxParallel(maxParallel) + .setItemOutputInventory(itemOutputInventory) + .setFluidOutputInventory(fluidOutputInventory) + .setMuTEMode(muteMode) + .build(); + maxParallel = Math.min(voidProtectionHelper.getMaxParallel(), maxParallel); + if (voidProtectionHelper.isItemFull()) { + result = CheckRecipeResultRegistry.ITEM_OUTPUT_FULL; + return; + } + if (voidProtectionHelper.isFluidFull()) { + result = CheckRecipeResultRegistry.FLUID_OUTPUT_FULL; + return; + } + } + + maxParallelBeforeBatchMode = Math.min(maxParallel, maxParallelBeforeBatchMode); + + // determine normal parallel + int actualMaxParallel = tRecipeEUt > 0 ? (int) Math.min(maxParallelBeforeBatchMode, availableEUt / tRecipeEUt) + : maxParallelBeforeBatchMode; + if (recipeCheck != null) { + currentParallel = recipeCheck.checkRecipeInputs(true, actualMaxParallel, itemInputs, fluidInputs); + } else { + currentParallel = (int) maxParallelCalculator.calculate(recipe, actualMaxParallel, fluidInputs, itemInputs); + if (currentParallel > 0) { + if (tSingleRecipeCheckBuilder != null) { + // If recipe checker is not built yet, build and set it + inputConsumer.consume(recipe, 1, fluidInputs, itemInputs); + SingleRecipeCheck builtCheck = tSingleRecipeCheckBuilder.setAfter(itemInputs, fluidInputs) + .setRecipe(recipe) + .build(); + singleRecipeMachine.setSingleRecipeCheck(builtCheck); + inputConsumer.consume(recipe, currentParallel - 1, fluidInputs, itemInputs); + } else { + inputConsumer.consume(recipe, currentParallel, fluidInputs, itemInputs); + } + } + } + + if (currentParallel <= 0) { + result = CheckRecipeResultRegistry.INTERNAL_ERROR; + return; + } + + long eutUseAfterOC = calculator.calculateEUtConsumptionUnderOneTick(originalMaxParallel, currentParallel); + calculator.setParallel(Math.min(currentParallel, originalMaxParallel)) + .calculate(); + if (currentParallel > originalMaxParallel) { + calculator.setRecipeEUt(eutUseAfterOC); + } + // If Batch Mode is enabled determine how many extra parallels we can get + if (batchMode && currentParallel > 0 && calculator.getDuration() < MAX_BATCH_MODE_TICK_TIME) { + int tExtraParallels; + double batchMultiplierMax = MAX_BATCH_MODE_TICK_TIME / calculator.getDuration(); + final int maxExtraParallels = (int) Math.floor( + Math.min( + currentParallel * Math.min(batchMultiplierMax - 1, batchModifier - 1), + maxParallel - currentParallel)); + if (recipeCheck != null) { + tExtraParallels = recipeCheck.checkRecipeInputs(true, maxExtraParallels, itemInputs, fluidInputs); + } else { + tExtraParallels = (int) maxParallelCalculator + .calculate(recipe, maxExtraParallels, fluidInputs, itemInputs); + inputConsumer.consume(recipe, tExtraParallels, fluidInputs, itemInputs); + } + durationMultiplier = 1.0f + (float) tExtraParallels / currentParallel; + currentParallel += tExtraParallels; + } + + // If we want to calculate outputs we do it here + if (calculateOutputs && currentParallel > 0) { + calculateItemOutputs(truncatedItemOutputs); + calculateFluidOutputs(truncatedFluidOutputs); + } + result = CheckRecipeResultRegistry.SUCCESSFUL; + } + + protected void copyInputs() { + ItemStack[] itemInputsToUse; + FluidStack[] fluidInputsToUse; + itemInputsToUse = new ItemStack[itemInputs.length]; + for (int i = 0; i < itemInputs.length; i++) { + itemInputsToUse[i] = itemInputs[i].copy(); + } + fluidInputsToUse = new FluidStack[fluidInputs.length]; + for (int i = 0; i < fluidInputs.length; i++) { + fluidInputsToUse[i] = fluidInputs[i].copy(); + } + itemInputs = itemInputsToUse; + fluidInputs = fluidInputsToUse; + } + + private void calculateItemOutputs(ItemStack[] truncatedItemOutputs) { + if (customItemOutputCalculation != null) { + itemOutputs = customItemOutputCalculation.apply(currentParallel); + return; + } + if (truncatedItemOutputs.length == 0) return; + ArrayList<ItemStack> itemOutputsList = new ArrayList<>(); + for (int i = 0; i < truncatedItemOutputs.length; i++) { + if (recipe.getOutput(i) == null) continue; + ItemStack origin = recipe.getOutput(i) + .copy(); + final long itemStackSize = origin.stackSize; + double chancedOutputMultiplier = calculateChancedOutputMultiplier( + (int) (recipe.getOutputChance(i) * chanceMultiplier), + currentParallel); + long items = (long) Math.ceil(itemStackSize * chancedOutputMultiplier * outputMultiplier); + addItemsLong(itemOutputsList, origin, items); + } + itemOutputs = itemOutputsList.toArray(new ItemStack[0]); + } + + private void calculateFluidOutputs(FluidStack[] truncatedFluidOutputs) { + if (customFluidOutputCalculation != null) { + fluidOutputs = customFluidOutputCalculation.apply(currentParallel); + return; + } + if (truncatedFluidOutputs.length == 0) return; + ArrayList<FluidStack> fluidOutputsList = new ArrayList<>(); + for (int i = 0; i < truncatedFluidOutputs.length; i++) { + if (recipe.getFluidOutput(i) == null) continue; + FluidStack origin = recipe.getFluidOutput(i) + .copy(); + long fluids = (long) this.outputMultiplier * origin.amount * currentParallel; + + addFluidsLong(fluidOutputsList, origin, fluids); + } + fluidOutputs = fluidOutputsList.toArray(new FluidStack[0]); + } + + private static final Random rand = new Random(); + + public static double calculateChancedOutputMultiplier(int chanceInt, int parallel) { + // Multiply the integer part of the chance directly with parallel + double multiplier = Math.floorDiv(chanceInt, 10000) * parallel; + int transformedChanceInt = chanceInt % 10000; + if (transformedChanceInt == 0) return multiplier; + // Calculation of the Decimal Part of chance + double chance = transformedChanceInt / 10000.0; + double mean = parallel * chance; + double stdDev = Math.sqrt(parallel * chance * (1 - chance)); + // Check if everything within 3 standard deviations of mean is within the range + // of possible values (0 ~ currentParallel) + boolean isSuitableForFittingWithNormalDistribution = mean - 3 * stdDev >= 0 && mean + 3 * stdDev <= parallel; + if (isSuitableForFittingWithNormalDistribution) { + // Use Normal Distribution to fit Binomial Distribution + double tMultiplier = stdDev * rand.nextGaussian() + mean; + multiplier += Math.max(Math.min(tMultiplier, parallel), 0); + } else { + // Do Binomial Distribution by loop + for (int roll = 0; roll < parallel; roll++) { + if (transformedChanceInt > XSTR.XSTR_INSTANCE.nextInt(10000)) { + multiplier += 1; + } + } + } + return multiplier; + } + + public static void addItemsLong(ArrayList<ItemStack> itemList, ItemStack origin, long amount) { + if (amount > 0) { + while (amount > Integer.MAX_VALUE) { + ItemStack item = origin.copy(); + item.stackSize = Integer.MAX_VALUE; + itemList.add(item); + amount -= Integer.MAX_VALUE; + } + ItemStack item = origin.copy(); + item.stackSize = (int) amount; + itemList.add(item); + } + } + + public static void addFluidsLong(ArrayList<FluidStack> fluidList, FluidStack origin, long amount) { + if (amount > 0) { + while (amount > Integer.MAX_VALUE) { + FluidStack fluid = origin.copy(); + fluid.amount = Integer.MAX_VALUE; + fluidList.add(fluid); + amount -= Integer.MAX_VALUE; + } + FluidStack fluid = origin.copy(); + fluid.amount = (int) amount; + fluidList.add(fluid); + } + } + + @FunctionalInterface + public interface MaxParallelCalculator { + + double calculate(GT_Recipe recipe, int maxParallel, FluidStack[] fluids, ItemStack[] items); + } + + @FunctionalInterface + public interface InputConsumer { + + void consume(GT_Recipe recipe, int amountMultiplier, FluidStack[] aFluidInputs, ItemStack[] aInputs); + } +} diff --git a/src/main/java/gregtech/api/util/GT_PlayedSound.java b/src/main/java/gregtech/api/util/GT_PlayedSound.java new file mode 100644 index 0000000000..05d61e9833 --- /dev/null +++ b/src/main/java/gregtech/api/util/GT_PlayedSound.java @@ -0,0 +1,42 @@ +package gregtech.api.util; + +import static gregtech.api.enums.GT_Values.E; + +import net.minecraft.util.ResourceLocation; + +public class GT_PlayedSound { + + public final String mSoundName; + public final int mX, mY, mZ; + + public GT_PlayedSound(ResourceLocation aSoundResourceLocation, double aX, double aY, double aZ) { + mSoundName = aSoundResourceLocation.toString(); + mX = (int) aX; + mY = (int) aY; + mZ = (int) aZ; + } + + /** + * @inheritDoc + * @deprecated Use {@link GT_PlayedSound(ResourceLocation, double, double, double)} + */ + @Deprecated + public GT_PlayedSound(String aSoundName, double aX, double aY, double aZ) { + this(new ResourceLocation(aSoundName == null ? E : aSoundName), aX, aY, aZ); + } + + @Override + public boolean equals(Object aObject) { + if (aObject instanceof GT_PlayedSound) { + return ((GT_PlayedSound) aObject).mX == mX && ((GT_PlayedSound) aObject).mY == mY + && ((GT_PlayedSound) aObject).mZ == mZ + && ((GT_PlayedSound) aObject).mSoundName.equals(mSoundName); + } + return false; + } + + @Override + public int hashCode() { + return mX + mY + mZ + mSoundName.hashCode(); + } +} diff --git a/src/main/java/gregtech/api/util/GT_ProcessingArray_Manager.java b/src/main/java/gregtech/api/util/GT_ProcessingArray_Manager.java new file mode 100644 index 0000000000..ead9393d0e --- /dev/null +++ b/src/main/java/gregtech/api/util/GT_ProcessingArray_Manager.java @@ -0,0 +1,51 @@ +package gregtech.api.util; + +import java.util.HashMap; + +import net.minecraft.item.ItemStack; + +import gregtech.api.enums.SoundResource; +import gregtech.api.recipe.RecipeMap; + +@Deprecated +public class GT_ProcessingArray_Manager { + + private static final HashMap<String, RecipeMap<?>> mRecipeSaves = new HashMap<>(); + private static final HashMap<String, SoundResource> machineSounds = new HashMap<>(); + + // Adds recipe Maps to the PA using the machines unlocalized name. + // Example: basicmachine.electrolyzer, with its recipe map will add the electrolyzer's recipe map to the PA + public static void addRecipeMapToPA(String aMachineName, RecipeMap<?> aMap) { + if (aMachineName != null) { + mRecipeSaves.put(aMachineName, aMap); + } + } + + // Allows the PA to extract the recipe map for the machine inside it. + public static RecipeMap<?> giveRecipeMap(String aMachineName) { + if (aMachineName != null) { + return mRecipeSaves.get(aMachineName); + } + return null; + } + + public static void addSoundResourceToPA(String machineName, SoundResource soundResource) { + if (machineName != null) { + machineSounds.put(machineName, soundResource); + } + } + + public static SoundResource getSoundResource(String machineName) { + if (machineName != null) { + return machineSounds.get(machineName); + } + return null; + } + + public static String getMachineName(ItemStack stack) { + int length = stack.getUnlocalizedName() + .length(); + return stack.getUnlocalizedName() + .substring(17, length - 8); // trim "gt.blockmachines." and ".tier.xx" + } +} diff --git a/src/main/java/gregtech/api/util/GT_Recipe.java b/src/main/java/gregtech/api/util/GT_Recipe.java new file mode 100644 index 0000000000..04f65a8342 --- /dev/null +++ b/src/main/java/gregtech/api/util/GT_Recipe.java @@ -0,0 +1,1271 @@ +package gregtech.api.util; + +import static gregtech.api.enums.GT_Values.D2; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import net.minecraft.item.ItemStack; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidStack; + +import org.jetbrains.annotations.Contract; + +import cpw.mods.fml.common.Loader; +import cpw.mods.fml.common.ModContainer; +import gregtech.GT_Mod; +import gregtech.api.GregTech_API; +import gregtech.api.enums.ItemList; +import gregtech.api.enums.Materials; +import gregtech.api.logic.FluidInventoryLogic; +import gregtech.api.logic.ItemInventoryLogic; +import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_Input; +import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_InputBus; +import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_MultiInput; +import gregtech.api.objects.GT_ItemStack; +import gregtech.api.recipe.RecipeCategory; +import gregtech.api.recipe.RecipeMap; +import gregtech.api.recipe.RecipeMaps; +import gregtech.api.recipe.RecipeMetadataKey; +import gregtech.api.recipe.metadata.EmptyRecipeMetadataStorage; +import gregtech.api.recipe.metadata.IRecipeMetadataStorage; +import gregtech.api.util.extensions.ArrayExt; +import gregtech.api.util.item.ItemHolder; +import gregtech.common.tileentities.machines.GT_MetaTileEntity_Hatch_InputBus_ME; +import gregtech.common.tileentities.machines.GT_MetaTileEntity_Hatch_Input_ME; +import ic2.core.Ic2Items; +import it.unimi.dsi.fastutil.objects.Object2LongArrayMap; +import it.unimi.dsi.fastutil.objects.Object2LongMap; +import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap; +import it.unimi.dsi.fastutil.objects.Reference2LongArrayMap; +import it.unimi.dsi.fastutil.objects.Reference2LongMap; +import it.unimi.dsi.fastutil.objects.Reference2LongOpenHashMap; + +public class GT_Recipe implements Comparable<GT_Recipe> { + + /** + * If you want to change the Output, feel free to modify or even replace the whole ItemStack Array, for Inputs, + * please add a new Recipe, because of the HashMaps. + */ + public ItemStack[] mInputs, mOutputs; + /** + * If you want to change the Output, feel free to modify or even replace the whole ItemStack Array, for Inputs, + * please add a new Recipe, because of the HashMaps. + */ + public FluidStack[] mFluidInputs, mFluidOutputs; + /** + * If you changed the amount of Array-Items inside the Output Array then the length of this Array must be larger or + * equal to the Output Array. A chance of 10000 equals 100% + */ + public int[] mChances; + /** + * An Item that needs to be inside the Special Slot, like for example the Copy Slot inside the Printer. This is only + * useful for Fake Recipes in NEI, since findRecipe() and containsInput() don't give a shit about this Field. Lists + * are also possible. + */ + public Object mSpecialItems; + + public int mDuration, mEUt, mSpecialValue; + /** + * Use this to just disable a specific Recipe, but the Configuration enables that already for every single Recipe. + */ + public boolean mEnabled = true; + /** + * If this Recipe is hidden from NEI + */ + public boolean mHidden = false; + /** + * If this Recipe is Fake and therefore doesn't get found by the findRecipe Function (It is still in the HashMaps, + * so that containsInput does return T on those fake Inputs) + */ + public boolean mFakeRecipe = false; + /** + * If this Recipe can be stored inside a Machine in order to make Recipe searching more Efficient by trying the + * previously used Recipe first. In case you have a Recipe Map overriding things and returning one time use Recipes, + * you have to set this to F. + */ + public boolean mCanBeBuffered = true; + /** + * If this Recipe needs the Output Slots to be completely empty. Needed in case you have randomised Outputs + */ + public boolean mNeedsEmptyOutput = false; + /** + * If this is set to true, NBT equality is required for recipe check. + */ + public boolean isNBTSensitive = false; + /** + * Used for describing recipes that do not fit the default recipe pattern (for example Large Boiler Fuels) + */ + private String[] neiDesc = null; + /** + * Holds a set of metadata for this recipe. + */ + @Nonnull + private final IRecipeMetadataStorage metadataStorage; + /** + * Category this recipe belongs to. Recipes belonging to recipemap are forced to have non-null category when added, + * otherwise it can be null. + */ + private RecipeCategory recipeCategory; + /** + * Stores which mod added this recipe + */ + public List<ModContainer> owners = new ArrayList<>(); + /** + * Stores stack traces where this recipe was added + */ + // BW wants to overwrite it, so no final + public List<List<String>> stackTraces = new ArrayList<>(); + + private GT_Recipe(GT_Recipe aRecipe, boolean shallow) { + mInputs = shallow ? aRecipe.mInputs : GT_Utility.copyItemArray(aRecipe.mInputs); + mOutputs = shallow ? aRecipe.mOutputs : GT_Utility.copyItemArray(aRecipe.mOutputs); + mSpecialItems = aRecipe.mSpecialItems; + mChances = aRecipe.mChances; + mFluidInputs = shallow ? aRecipe.mFluidInputs : GT_Utility.copyFluidArray(aRecipe.mFluidInputs); + mFluidOutputs = shallow ? aRecipe.mFluidOutputs : GT_Utility.copyFluidArray(aRecipe.mFluidOutputs); + mDuration = aRecipe.mDuration; + mSpecialValue = aRecipe.mSpecialValue; + mEUt = aRecipe.mEUt; + mNeedsEmptyOutput = aRecipe.mNeedsEmptyOutput; + isNBTSensitive = aRecipe.isNBTSensitive; + mCanBeBuffered = aRecipe.mCanBeBuffered; + mFakeRecipe = aRecipe.mFakeRecipe; + mEnabled = aRecipe.mEnabled; + mHidden = aRecipe.mHidden; + metadataStorage = EmptyRecipeMetadataStorage.INSTANCE; + owners = new ArrayList<>(aRecipe.owners); + reloadOwner(); + } + + /** + * Only for {@link GT_RecipeBuilder}. + */ + GT_Recipe(ItemStack[] mInputs, ItemStack[] mOutputs, FluidStack[] mFluidInputs, FluidStack[] mFluidOutputs, + int[] mChances, Object mSpecialItems, int mDuration, int mEUt, int mSpecialValue, boolean mEnabled, + boolean mHidden, boolean mFakeRecipe, boolean mCanBeBuffered, boolean mNeedsEmptyOutput, boolean nbtSensitive, + String[] neiDesc, @Nullable IRecipeMetadataStorage metadataStorage, RecipeCategory recipeCategory) { + this.mInputs = mInputs; + this.mOutputs = mOutputs; + this.mFluidInputs = mFluidInputs; + this.mFluidOutputs = mFluidOutputs; + this.mChances = mChances; + this.mSpecialItems = mSpecialItems; + this.mDuration = mDuration; + this.mEUt = mEUt; + this.mSpecialValue = mSpecialValue; + this.mEnabled = mEnabled; + this.mHidden = mHidden; + this.mFakeRecipe = mFakeRecipe; + this.mCanBeBuffered = mCanBeBuffered; + this.mNeedsEmptyOutput = mNeedsEmptyOutput; + this.isNBTSensitive = nbtSensitive; + this.neiDesc = neiDesc; + this.metadataStorage = metadataStorage == null ? EmptyRecipeMetadataStorage.INSTANCE : metadataStorage.copy(); + this.recipeCategory = recipeCategory; + + reloadOwner(); + } + + public GT_Recipe(boolean aOptimize, ItemStack[] aInputs, ItemStack[] aOutputs, Object aSpecialItems, int[] aChances, + FluidStack[] aFluidInputs, FluidStack[] aFluidOutputs, int aDuration, int aEUt, int aSpecialValue) { + if (aInputs == null) aInputs = new ItemStack[0]; + if (aOutputs == null) aOutputs = new ItemStack[0]; + if (aFluidInputs == null) aFluidInputs = new FluidStack[0]; + if (aFluidOutputs == null) aFluidOutputs = new FluidStack[0]; + if (aChances == null) aChances = new int[aOutputs.length]; + if (aChances.length < aOutputs.length) aChances = Arrays.copyOf(aChances, aOutputs.length); + + aInputs = ArrayExt.withoutTrailingNulls(aInputs, ItemStack[]::new); + aOutputs = ArrayExt.withoutTrailingNulls(aOutputs, ItemStack[]::new); + aFluidInputs = ArrayExt.withoutNulls(aFluidInputs, FluidStack[]::new); + aFluidOutputs = ArrayExt.withoutNulls(aFluidOutputs, FluidStack[]::new); + + GT_OreDictUnificator.setStackArray(true, aInputs); + GT_OreDictUnificator.setStackArray(true, aOutputs); + + for (ItemStack tStack : aOutputs) GT_Utility.updateItemStack(tStack); + + for (int i = 0; i < aChances.length; i++) if (aChances[i] <= 0) aChances[i] = 10000; + for (int i = 0; i < aFluidInputs.length; i++) aFluidInputs[i] = aFluidInputs[i].copy(); + for (int i = 0; i < aFluidOutputs.length; i++) aFluidOutputs[i] = aFluidOutputs[i].copy(); + + if (aOptimize && aDuration >= 32) { + ArrayList<ItemStack> tList = new ArrayList<>(); + tList.addAll(Arrays.asList(aInputs)); + tList.addAll(Arrays.asList(aOutputs)); + for (int i = 0; i < tList.size(); i++) if (tList.get(i) == null) tList.remove(i--); + + for (byte i = (byte) Math.min(64, aDuration / 16); i > 1; i--) if (aDuration / i >= 16) { + boolean temp = true; + for (ItemStack stack : tList) if (stack.stackSize % i != 0) { + temp = false; + break; + } + if (temp) for (FluidStack aFluidInput : aFluidInputs) if (aFluidInput.amount % i != 0) { + temp = false; + break; + } + if (temp) for (FluidStack aFluidOutput : aFluidOutputs) if (aFluidOutput.amount % i != 0) { + temp = false; + break; + } + if (temp) { + for (ItemStack itemStack : tList) itemStack.stackSize /= i; + for (FluidStack aFluidInput : aFluidInputs) aFluidInput.amount /= i; + for (FluidStack aFluidOutput : aFluidOutputs) aFluidOutput.amount /= i; + aDuration /= i; + } + } + } + + mInputs = aInputs; + mOutputs = aOutputs; + mSpecialItems = aSpecialItems; + mChances = aChances; + mFluidInputs = aFluidInputs; + mFluidOutputs = aFluidOutputs; + mDuration = aDuration; + mSpecialValue = aSpecialValue; + mEUt = aEUt; + metadataStorage = EmptyRecipeMetadataStorage.INSTANCE; + // checkCellBalance(); + reloadOwner(); + } + + // aSpecialValue = EU per Liter! If there is no Liquid for this Object, then it gets multiplied with 1000! + public GT_Recipe(ItemStack aInput1, ItemStack aOutput1, ItemStack aOutput2, ItemStack aOutput3, ItemStack aOutput4, + int aSpecialValue, int aType) { + this( + true, + new ItemStack[] { aInput1 }, + new ItemStack[] { aOutput1, aOutput2, aOutput3, aOutput4 }, + null, + null, + null, + null, + 0, + 0, + Math.max(1, aSpecialValue)); + + if (mInputs.length > 0 && aSpecialValue > 0) { + switch (aType) { + // Diesel Generator + case 0 -> { + RecipeMaps.dieselFuels.addRecipe(this); + RecipeMaps.largeBoilerFakeFuels.getBackend() + .addDieselRecipe(this); + } + // Gas Turbine + case 1 -> RecipeMaps.gasTurbineFuels.addRecipe(this); + + // Thermal Generator + case 2 -> RecipeMaps.hotFuels.addRecipe(this); + + // Plasma Generator + case 4 -> RecipeMaps.plasmaFuels.addRecipe(this); + + // Magic Generator + case 5 -> RecipeMaps.magicFuels.addRecipe(this); + + // Fluid Generator. Usually 3. Every wrong Type ends up in the Semifluid Generator + default -> { + RecipeMaps.denseLiquidFuels.addRecipe(this); + RecipeMaps.largeBoilerFakeFuels.getBackend() + .addDenseLiquidRecipe(this); + } + } + } + } + + // Dummy GT_Recipe maker... + public GT_Recipe(ItemStack[] aInputs, ItemStack[] aOutputs, Object aSpecialItems, int[] aChances, + FluidStack[] aFluidInputs, FluidStack[] aFluidOutputs, int aDuration, int aEUt, int aSpecialValue) { + this( + true, + aInputs, + aOutputs, + aSpecialItems, + aChances, + aFluidInputs, + aFluidOutputs, + aDuration, + aEUt, + aSpecialValue); + } + + /** + * Re-unificates all the items present in recipes. + */ + public static void reInit() { + GT_Log.out.println("GT_Mod: Re-Unificating Recipes."); + for (RecipeMap<?> map : RecipeMap.ALL_RECIPE_MAPS.values()) { + map.getBackend() + .reInit(); + } + } + + public ItemStack getRepresentativeInput(int aIndex) { + if (aIndex < 0 || aIndex >= mInputs.length) return null; + return GT_Utility.copyOrNull(mInputs[aIndex]); + } + + public ItemStack getOutput(int aIndex) { + if (aIndex < 0 || aIndex >= mOutputs.length) return null; + return GT_Utility.copyOrNull(mOutputs[aIndex]); + } + + /** + * Dictates the ItemStacks displayed in the output slots of any NEI page handled by the default GT NEI handler. + * Override to make shown items differ from a GT_Recipe's item output array + * + * @see gregtech.nei.GT_NEI_DefaultHandler + * @param i Slot index + * @return ItemStack to be displayed in the slot + */ + public ItemStack getRepresentativeOutput(int i) { + return getOutput(i); + } + + public int getOutputChance(int aIndex) { + if (mChances == null) return 10000; + if (aIndex < 0 || aIndex >= mChances.length) return 10000; + return mChances[aIndex]; + } + + public FluidStack getRepresentativeFluidInput(int aIndex) { + if (aIndex < 0 || aIndex >= mFluidInputs.length || mFluidInputs[aIndex] == null) return null; + return mFluidInputs[aIndex].copy(); + } + + public FluidStack getFluidOutput(int aIndex) { + if (aIndex < 0 || aIndex >= mFluidOutputs.length || mFluidOutputs[aIndex] == null) return null; + return mFluidOutputs[aIndex].copy(); + } + + public void checkCellBalance() { + if (!D2 || mInputs.length < 1) return; + + int tInputAmount = GT_ModHandler.getCapsuleCellContainerCountMultipliedWithStackSize(mInputs); + int tOutputAmount = GT_ModHandler.getCapsuleCellContainerCountMultipliedWithStackSize(mOutputs); + + if (tInputAmount < tOutputAmount) { + if (!Materials.Tin.contains(mInputs)) { + GT_Log.err.println("You get more Cells, than you put in? There must be something wrong."); + new Exception().printStackTrace(GT_Log.err); + } + } else if (tInputAmount > tOutputAmount) { + if (!Materials.Tin.contains(mOutputs)) { + GT_Log.err.println("You get less Cells, than you put in? GT Machines usually don't destroy Cells."); + new Exception().printStackTrace(GT_Log.err); + } + } + } + + public GT_Recipe copy() { + return new GT_Recipe(this, false); + } + + public GT_Recipe copyShallow() { + return new GT_Recipe(this, true); + } + + public boolean isRecipeInputEqual(boolean aDecreaseStacksizeBySuccess, FluidStack[] aFluidInputs, + ItemStack... aInputs) { + return isRecipeInputEqual(aDecreaseStacksizeBySuccess, false, 1, aFluidInputs, aInputs); + } + + // For non-multiplied recipe amount values + public boolean isRecipeInputEqual(boolean aDecreaseStacksizeBySuccess, boolean aDontCheckStackSizes, + FluidStack[] aFluidInputs, ItemStack... aInputs) { + return isRecipeInputEqual(aDecreaseStacksizeBySuccess, aDontCheckStackSizes, 1, aFluidInputs, aInputs); + } + + /** + * Okay, did some code archeology to figure out what's going on here. + * + * <p> + * This variable was added in <a + * href=https://github.com/GTNewHorizons/GT5-Unofficial/commit/9959ab7443982a19ad329bca424ab515493432e9>this + * commit,</a> in order to fix the issues mentioned in <a + * href=https://github.com/GTNewHorizons/GT5-Unofficial/pull/183>the PR</a>. + * + * <p> + * It looks like it controls checking NBT. At this point, since we are still using universal fluid cells which store + * their fluids in NBT, it probably will not be safe to disable the NBT checks in the near future. Data sticks may + * be another case. Anyway, we probably can't get rid of this without some significant changes to clean up recipe + * inputs. + */ + public static boolean GTppRecipeHelper; + + /** + * WARNING: Do not call this method with both {@code aDecreaseStacksizeBySuccess} and {@code aDontCheckStackSizes} + * set to {@code true}! You'll get weird behavior. + */ + public boolean isRecipeInputEqual(boolean aDecreaseStacksizeBySuccess, boolean aDontCheckStackSizes, + int amountMultiplier, FluidStack[] aFluidInputs, ItemStack... aInputs) { + double maxParallel = maxParallelCalculatedByInputs(amountMultiplier, aFluidInputs, aInputs); + if (aDontCheckStackSizes) { + return maxParallel > 0; + } else if (maxParallel >= amountMultiplier) { + if (aDecreaseStacksizeBySuccess) { + consumeInput(amountMultiplier, aFluidInputs, aInputs); + } + return true; + } + return false; + } + + /** + * WARNING: Ensure that item inputs and fluid inputs are enough to be consumed with + * {@link #maxParallelCalculatedByInputs} before calling this method! + */ + public void consumeInput(int amountMultiplier, FluidStack[] aFluidInputs, ItemStack... aInputs) { + if (amountMultiplier <= 0) return; + + long remainingCost; + + if (aFluidInputs != null) { + for (FluidStack recipeFluidCost : mFluidInputs) { + if (recipeFluidCost != null) { + remainingCost = (long) recipeFluidCost.amount * amountMultiplier; + + for (FluidStack providedFluid : aFluidInputs) { + if (providedFluid != null && providedFluid.isFluidEqual(recipeFluidCost)) { + if (providedFluid.amount >= remainingCost) { + providedFluid.amount -= remainingCost; + break; + } else { + remainingCost -= providedFluid.amount; + providedFluid.amount = 0; + } + } + } + } + } + } + + if (aInputs != null) { + for (ItemStack recipeItemCost : mInputs) { + ItemStack unifiedItemCost = GT_OreDictUnificator.get_nocopy(true, recipeItemCost); + if (unifiedItemCost != null) { + remainingCost = (long) recipeItemCost.stackSize * amountMultiplier; + + for (ItemStack providedItem : aInputs) { + if (isNBTSensitive && !GT_Utility.areStacksEqual(providedItem, unifiedItemCost, false)) { + continue; + } else if (!isNBTSensitive + && !GT_OreDictUnificator.isInputStackEqual(providedItem, unifiedItemCost)) { + continue; + } + + if (GTppRecipeHelper) { // Please see JavaDoc on GTppRecipeHelper for why this is here. + if (GT_Utility.areStacksEqual(providedItem, Ic2Items.FluidCell.copy(), true) + || GT_Utility.areStacksEqual(providedItem, ItemList.Tool_DataStick.get(1L), true) + || GT_Utility.areStacksEqual(providedItem, ItemList.Tool_DataOrb.get(1L), true)) { + if (!GT_Utility.areStacksEqual(providedItem, recipeItemCost, false)) continue; + } + } + + if (providedItem.stackSize >= remainingCost) { + providedItem.stackSize -= remainingCost; + break; + } else { + remainingCost -= providedItem.stackSize; + providedItem.stackSize = 0; + } + } + } + } + } + } + + /** + * Returns the number of parallel recipes, or 0 if recipe is not satisfied at all. 0 < number < 1 means that inputs + * are found but not enough. + */ + public double maxParallelCalculatedByInputs(int maxParallel, FluidStack[] aFluidInputs, ItemStack... aInputs) { + if (mInputs.length > 0 && aInputs == null) return 0; + if (mFluidInputs.length > 0 && aFluidInputs == null) return 0; + + double currentParallel = maxParallel; + + // We need to have any fluids inputs, otherwise the code below does nothing. The second check is always true + // because of early exit condition above. + if (mFluidInputs.length > 0 /* && aFluidInputs != null */) { + // Create map for fluid -> stored amount + Reference2LongMap<Fluid> fluidMap = new Reference2LongArrayMap<>(2); + Reference2LongMap<Fluid> fluidCost = new Reference2LongArrayMap<>(2); + for (FluidStack fluidStack : aFluidInputs) { + if (fluidStack == null) continue; + fluidMap.mergeLong(fluidStack.getFluid(), fluidStack.amount, Long::sum); + } + for (FluidStack fluidStack : mFluidInputs) { + if (fluidStack == null) continue; + fluidCost.mergeLong(fluidStack.getFluid(), fluidStack.amount, Long::sum); + } + + // Check how many parallels can it perform for each fluid + for (Reference2LongMap.Entry<Fluid> costEntry : fluidCost.reference2LongEntrySet()) { + if (costEntry.getLongValue() > 0) { + currentParallel = Math.min( + currentParallel, + (double) fluidMap.getOrDefault(costEntry.getKey(), 0L) / costEntry.getLongValue()); + } + if (currentParallel <= 0) { + return 0; + } + } + } + + if (mInputs.length > 0) { + double remainingCost; + long providedAmount; + Object2LongMap<GT_Utility.ItemId> itemCostMap = new Object2LongArrayMap<>(2); + + for (ItemStack itemStack : mInputs) { + if (itemStack == null) continue; + if (shouldCheckNBT(itemStack)) { + GT_Utility.ItemId itemId = GT_Utility.ItemId.createNoCopy(itemStack); + itemCostMap.mergeLong(itemId, itemStack.stackSize, Long::sum); + continue; + } + ItemStack unifiedItem = GT_OreDictUnificator.get_nocopy(true, itemStack); + if (unifiedItem != null) { + GT_Utility.ItemId unifiedId; + if (isNBTSensitive) unifiedId = GT_Utility.ItemId.createNoCopy(unifiedItem); + else unifiedId = GT_Utility.ItemId.createWithoutNBT(unifiedItem); + itemCostMap.mergeLong(unifiedId, itemStack.stackSize, Long::sum); + } + } + + ItemStack unifiedItemCost; + nextRecipeItemCost: for (Map.Entry<GT_Utility.ItemId, Long> costEntry : itemCostMap.entrySet()) { + unifiedItemCost = costEntry.getKey() + .getItemStack(); + if (unifiedItemCost != null) { + remainingCost = costEntry.getValue() * currentParallel; + providedAmount = 0; + + for (ItemStack providedItem : aInputs) { + if (!areInputStackAndRecipeCostMatched(providedItem, unifiedItemCost)) continue; + // for non-consumed input + if (costEntry.getValue() == 0) continue nextRecipeItemCost; + + providedAmount += providedItem.stackSize; + + if (providedAmount >= remainingCost) continue nextRecipeItemCost; + } + if (providedAmount == 0) return 0; + currentParallel = Math.min(currentParallel, (double) providedAmount / costEntry.getValue()); + } + } + } + return currentParallel; + } + + private boolean areInputStackAndRecipeCostMatched(ItemStack providedItem, ItemStack unifiedItemCost) { + if (isNBTSensitive || shouldCheckNBT(providedItem)) { + return GT_Utility.areStacksEqual(providedItem, unifiedItemCost, false); + } else { + return GT_OreDictUnificator.isInputStackEqual(providedItem, unifiedItemCost); + } + } + + /** + * Please see JavaDoc on {@link #GTppRecipeHelper} for why this is here. + */ + private boolean shouldCheckNBT(ItemStack item) { + if (GTppRecipeHelper) { + return GT_Utility.areStacksEqual(item, Ic2Items.FluidCell.copy(), true) + || GT_Utility.areStacksEqual(item, ItemList.Tool_DataStick.get(1L), true) + || GT_Utility.areStacksEqual(item, ItemList.Tool_DataOrb.get(1L), true); + } + return false; + } + + public boolean isRecipePossible(@Nullable ItemInventoryLogic itemInput, @Nullable FluidInventoryLogic fluidInput) { + return getAmountOfRecipesDone(itemInput, fluidInput, 1, true) > 0; + } + + public long getAmountOfRecipesDone(@Nullable ItemInventoryLogic itemInput, @Nullable FluidInventoryLogic fluidInput, + long maxParallel, boolean simulate) { + if (itemInput == null) { + itemInput = new ItemInventoryLogic(0); + } + + if (fluidInput == null) { + fluidInput = new FluidInventoryLogic(0, 0); + } + + itemInput.startRecipeCheck(); + Map<ItemHolder, Long> recipeItems = getItemInputsAsItemMap(); + for (Entry<ItemHolder, Long> entry : recipeItems.entrySet()) { + maxParallel = Math + .min(maxParallel, itemInput.calculateAmountOfTimesItemCanBeTaken(entry.getKey(), entry.getValue())); + } + + for (FluidStack fluid : mFluidInputs) { + if (fluid == null) continue; + maxParallel = Math + .min(maxParallel, fluidInput.calculateAmountOfTimesFluidCanBeTaken(fluid.getFluid(), fluid.amount)); + } + + if (simulate) { + itemInput.stopRecipeCheck(); + return maxParallel; + } + + for (Entry<ItemHolder, Long> entry : recipeItems.entrySet()) { + itemInput.subtractItemAmount(entry.getKey(), entry.getValue() * maxParallel, false); + } + + for (FluidStack fluid : mFluidInputs) { + if (fluid == null) continue; + fluidInput.drain(fluid.getFluid(), fluid.amount * maxParallel, false); + } + itemInput.stopRecipeCheck(); + return maxParallel; + } + + private Map<ItemHolder, Long> getItemInputsAsItemMap() { + Map<ItemHolder, Long> items = new HashMap<>(); + for (ItemStack item : mInputs) { + if (item == null) continue; + ItemHolder itemHolder = new ItemHolder(item); + items.put(itemHolder, items.getOrDefault(itemHolder, 0L) + item.stackSize); + } + return items; + } + + @Override + public int compareTo(GT_Recipe recipe) { + // first lowest tier recipes + // then fastest + // then with lowest special value + // then dry recipes + // then with fewer inputs + if (this.mEUt != recipe.mEUt) { + return this.mEUt - recipe.mEUt; + } else if (this.mDuration != recipe.mDuration) { + return this.mDuration - recipe.mDuration; + } else if (this.mSpecialValue != recipe.mSpecialValue) { + return this.mSpecialValue - recipe.mSpecialValue; + } else if (this.mFluidInputs.length != recipe.mFluidInputs.length) { + return this.mFluidInputs.length - recipe.mFluidInputs.length; + } else if (this.mInputs.length != recipe.mInputs.length) { + return this.mInputs.length - recipe.mInputs.length; + } + return 0; + } + + public String[] getNeiDesc() { + return neiDesc; + } + + /** + * Sets description shown on NEI. <br> + * If you have a large number of recipes for the recipemap, this is not efficient memory wise, so use + * {@link gregtech.api.recipe.RecipeMapBuilder#neiSpecialInfoFormatter} instead. + */ + public void setNeiDesc(String... neiDesc) { + this.neiDesc = neiDesc; + } + + // region metadata + + // Don't try implementing setMetadata, as metadataStorage can be EmptyRecipeMetadataStorage + + /** + * Gets metadata associated with this recipe. Can return null. Use + * {@link #getMetadataOrDefault(RecipeMetadataKey, Object)} + * if you want to specify default value. + */ + @Nullable + public <T> T getMetadata(RecipeMetadataKey<T> key) { + return key.cast(metadataStorage.getMetadata(key)); + } + + /** + * Gets metadata associated with this recipe with default value. Does not return null unless default value is null. + */ + @Contract("_, !null -> !null") + @Nullable + public <T> T getMetadataOrDefault(RecipeMetadataKey<T> key, @Nullable T defaultValue) { + return key.cast(metadataStorage.getMetadataOrDefault(key, defaultValue)); + } + + @Nonnull + public IRecipeMetadataStorage getMetadataStorage() { + return metadataStorage; + } + + // endregion + + public RecipeCategory getRecipeCategory() { + return recipeCategory; + } + + /** + * Exists only for recipe copying from external. For ordinal use case, use {@link GT_RecipeBuilder#recipeCategory}. + */ + public void setRecipeCategory(RecipeCategory recipeCategory) { + this.recipeCategory = recipeCategory; + } + + private static final List<String> excludedStacktraces = Arrays.asList( + "java.lang.Thread", + "gregtech.api.interfaces.IRecipeMap", + "gregtech.api.interfaces.IRecipeMap$1", + "gregtech.api.recipe.RecipeMap", + "gregtech.api.recipe.RecipeMapBackend", + "gregtech.api.recipe.RecipeMapBackendPropertiesBuilder", + "gregtech.api.util.GT_Recipe", + "gregtech.api.util.GT_RecipeBuilder", + "gregtech.api.util.GT_RecipeConstants", + "gregtech.api.util.GT_RecipeMapUtil", + "gregtech.common.GT_RecipeAdder"); + + public void reloadOwner() { + setOwner( + Loader.instance() + .activeModContainer()); + + if (GT_Mod.gregtechproxy.mNEIRecipeOwnerStackTrace) { + List<String> toAdd = new ArrayList<>(); + for (StackTraceElement stackTrace : Thread.currentThread() + .getStackTrace()) { + if (excludedStacktraces.stream() + .noneMatch( + c -> stackTrace.getClassName() + .equals(c))) { + toAdd.add(formatStackTrace(stackTrace)); + } + } + stackTraces.add(toAdd); + } + } + + private static String formatStackTrace(StackTraceElement stackTraceElement) { + String raw = stackTraceElement.toString(); + int startParen = raw.lastIndexOf('('); + int colon = raw.lastIndexOf(':'); + if (colon == -1) { + // native or unknown source + return raw; + } + // strip class name and leave line number, as class name is already shown + return raw.substring(0, startParen + 1) + raw.substring(colon); + } + + public void setOwner(ModContainer newOwner) { + ModContainer oldOwner = !owners.isEmpty() ? this.owners.get(owners.size() - 1) : null; + if (newOwner != null && newOwner != oldOwner) { + owners.add(newOwner); + } + } + + /** + * Use in case {@link Loader#activeModContainer()} isn't helpful + */ + public void setOwner(String modId) { + for (ModContainer mod : Loader.instance() + .getModList()) { + if (mod.getModId() + .equals(modId)) { + setOwner(mod); + return; + } + } + } + + public GT_Recipe setInputs(ItemStack... aInputs) { + // TODO determine if we need this without trailing nulls call + this.mInputs = ArrayExt.withoutTrailingNulls(aInputs, ItemStack[]::new); + return this; + } + + public GT_Recipe setOutputs(ItemStack... aOutputs) { + this.mOutputs = ArrayExt.withoutTrailingNulls(aOutputs, ItemStack[]::new); + return this; + } + + public GT_Recipe setFluidInputs(FluidStack... aInputs) { + this.mFluidInputs = ArrayExt.withoutTrailingNulls(aInputs, FluidStack[]::new); + return this; + } + + public GT_Recipe setFluidOutputs(FluidStack... aOutputs) { + this.mFluidOutputs = ArrayExt.withoutTrailingNulls(aOutputs, FluidStack[]::new); + return this; + } + + public GT_Recipe setDuration(int aDuration) { + this.mDuration = aDuration; + return this; + } + + public GT_Recipe setEUt(int aEUt) { + this.mEUt = aEUt; + return this; + } + + public static class GT_Recipe_AssemblyLine { + + public static final ArrayList<GT_Recipe_AssemblyLine> sAssemblylineRecipes = new ArrayList<>(); + + static { + if (!Boolean.getBoolean("com.gtnh.gt5u.ignore-invalid-assline-recipe")) + GregTech_API.sFirstWorldTick.add(GT_Recipe_AssemblyLine::checkInvalidRecipes); + else GT_Log.out.println("NOT CHECKING INVALID ASSLINE RECIPE."); + } + + private static void checkInvalidRecipes() { + int invalidCount = 0; + GT_Log.out.println("Started assline validation"); + for (GT_Recipe_AssemblyLine recipe : sAssemblylineRecipes) { + if (recipe.getPersistentHash() == 0) { + invalidCount++; + GT_Log.err.printf("Invalid recipe: %s%n", recipe); + } + } + if (invalidCount > 0) throw new RuntimeException( + "There are " + invalidCount + " invalid assembly line recipe(s)! Check GregTech.log for details!"); + } + + public ItemStack mResearchItem; + public int mResearchTime; + public ItemStack[] mInputs; + public FluidStack[] mFluidInputs; + public ItemStack mOutput; + public int mDuration; + public int mEUt; + public ItemStack[][] mOreDictAlt; + private int mPersistentHash; + + /** + * THIS CONSTRUCTOR DOES SET THE PERSISTENT HASH. + * <p> + * if you set one yourself, it will give you one of the RunetimeExceptions! + */ + public GT_Recipe_AssemblyLine(ItemStack aResearchItem, int aResearchTime, ItemStack[] aInputs, + FluidStack[] aFluidInputs, ItemStack aOutput, int aDuration, int aEUt) { + this( + aResearchItem, + aResearchTime, + aInputs, + aFluidInputs, + aOutput, + aDuration, + aEUt, + new ItemStack[aInputs.length][]); + int tPersistentHash = 1; + for (ItemStack tInput : aInputs) + tPersistentHash = tPersistentHash * 31 + GT_Utility.persistentHash(tInput, true, false); + tPersistentHash = tPersistentHash * 31 + GT_Utility.persistentHash(aResearchItem, true, false); + tPersistentHash = tPersistentHash * 31 + GT_Utility.persistentHash(aOutput, true, false); + for (FluidStack tFluidInput : aFluidInputs) + tPersistentHash = tPersistentHash * 31 + GT_Utility.persistentHash(tFluidInput, true, false); + tPersistentHash = tPersistentHash * 31 + aResearchTime; + tPersistentHash = tPersistentHash * 31 + aDuration; + tPersistentHash = tPersistentHash * 31 + aEUt; + setPersistentHash(tPersistentHash); + } + + /** + * THIS CONSTRUCTOR DOES <b>NOT</b> SET THE PERSISTENT HASH. + * <p> + * if you don't set one yourself, it will break a lot of stuff! + */ + public GT_Recipe_AssemblyLine(ItemStack aResearchItem, int aResearchTime, ItemStack[] aInputs, + FluidStack[] aFluidInputs, ItemStack aOutput, int aDuration, int aEUt, ItemStack[][] aAlt) { + mResearchItem = aResearchItem; + mResearchTime = aResearchTime; + mInputs = aInputs; + mFluidInputs = aFluidInputs; + mOutput = aOutput; + mDuration = aDuration; + mEUt = aEUt; + mOreDictAlt = aAlt; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + GT_ItemStack[] thisInputs = new GT_ItemStack[this.mInputs.length]; + int totalInputStackSize = 0; + for (int i = 0; i < this.mInputs.length; i++) { + thisInputs[i] = new GT_ItemStack(this.mInputs[i]); + totalInputStackSize += thisInputs[i].mStackSize; + } + int inputHash = Arrays.deepHashCode(thisInputs); + int inputFluidHash = Arrays.deepHashCode(this.mFluidInputs); + GT_ItemStack thisOutput = new GT_ItemStack(mOutput); + GT_ItemStack thisResearch = new GT_ItemStack(mResearchItem); + int miscRecipeDataHash = Arrays.deepHashCode( + new Object[] { totalInputStackSize, mDuration, mEUt, thisOutput, thisResearch, mResearchTime }); + result = prime * result + inputFluidHash; + result = prime * result + inputHash; + result = prime * result + miscRecipeDataHash; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof GT_Recipe_AssemblyLine other)) { + return false; + } + if (this.mInputs.length != other.mInputs.length) { + return false; + } + if (this.mFluidInputs.length != other.mFluidInputs.length) { + return false; + } + // Check Outputs Match + GT_ItemStack output1 = new GT_ItemStack(this.mOutput); + GT_ItemStack output2 = new GT_ItemStack(other.mOutput); + if (!output1.equals(output2)) { + return false; + } + // Check Scanned Item Match + GT_ItemStack scan1 = new GT_ItemStack(this.mResearchItem); + GT_ItemStack scan2 = new GT_ItemStack(other.mResearchItem); + if (!scan1.equals(scan2)) { + return false; + } + // Check Items Match + GT_ItemStack[] thisInputs = new GT_ItemStack[this.mInputs.length]; + GT_ItemStack[] otherInputs = new GT_ItemStack[other.mInputs.length]; + for (int i = 0; i < thisInputs.length; i++) { + thisInputs[i] = new GT_ItemStack(this.mInputs[i]); + otherInputs[i] = new GT_ItemStack(other.mInputs[i]); + } + for (int i = 0; i < thisInputs.length; i++) { + if (!thisInputs[i].equals(otherInputs[i]) || thisInputs[i].mStackSize != otherInputs[i].mStackSize) { + return false; + } + } + // Check Fluids Match + for (int i = 0; i < this.mFluidInputs.length; i++) { + if (!this.mFluidInputs[i].isFluidStackIdentical(other.mFluidInputs[i])) { + return false; + } + } + + return this.mDuration == other.mDuration && this.mEUt == other.mEUt + && this.mResearchTime == other.mResearchTime; + } + + public int getPersistentHash() { + if (mPersistentHash == 0) + GT_Log.err.println("Assline recipe persistent hash has not been set! Recipe: " + mOutput); + return mPersistentHash; + } + + @Override + public String toString() { + return "GT_Recipe_AssemblyLine{" + "mResearchItem=" + + mResearchItem + + ", mResearchTime=" + + mResearchTime + + ", mInputs=" + + Arrays.toString(mInputs) + + ", mFluidInputs=" + + Arrays.toString(mFluidInputs) + + ", mOutput=" + + mOutput + + ", mDuration=" + + mDuration + + ", mEUt=" + + mEUt + + ", mOreDictAlt=" + + Arrays.toString(mOreDictAlt) + + '}'; + } + + /** + * @param aPersistentHash the persistent hash. it should reflect the exact input used to generate this recipe If + * 0 is passed in, the actual persistent hash will be automatically remapped to 1 + * instead. + * @throws IllegalStateException if the persistent hash has been set already + */ + public void setPersistentHash(int aPersistentHash) { + if (this.mPersistentHash != 0) throw new IllegalStateException("Cannot set persistent hash twice!"); + if (aPersistentHash == 0) this.mPersistentHash = 1; + else this.mPersistentHash = aPersistentHash; + } + + /** + * @param inputBusses List of input busses to check. + * @return An array containing the amount of item to consume from the first slot of every input bus. + * {@code null} if at least one item fails to match the recipe ingredient. + */ + public static int[] getItemConsumptionAmountArray(ArrayList<GT_MetaTileEntity_Hatch_InputBus> inputBusses, + GT_Recipe_AssemblyLine recipe) { + int itemCount = recipe.mInputs.length; + if (itemCount == 0) return null; + int[] tStacks = new int[itemCount]; + for (int i = 0; i < itemCount; i++) { + GT_MetaTileEntity_Hatch_InputBus inputBus = inputBusses.get(i); + if (!inputBus.isValid()) return null; + ItemStack slotStack; + if (inputBus instanceof GT_MetaTileEntity_Hatch_InputBus_ME meBus) { + slotStack = meBus.getShadowItemStack(0); + } else { + slotStack = inputBus.getStackInSlot(0); + } + if (slotStack == null) return null; + + int amount = getMatchedIngredientAmount(slotStack, recipe.mInputs[i], recipe.mOreDictAlt[i]); + if (amount < 0) return null; + + tStacks[i] = amount; + } + return tStacks; + } + + public static int getMatchedIngredientAmount(ItemStack aSlotStack, ItemStack aIngredient, ItemStack[] alts) { + if (alts == null || alts.length == 0) { + if (GT_Utility.areStacksEqual(aSlotStack, aIngredient, true)) { + return aIngredient.stackSize; + } + return -1; + } + for (ItemStack tAltStack : alts) { + if (GT_Utility.areStacksEqual(aSlotStack, tAltStack, true)) { + return tAltStack.stackSize; + } + } + return -1; + } + + /** + * @param inputBusses Input bus list to check. Usually the input bus list of multi. + * @param itemConsumptions Should be generated by {@link GT_Recipe_AssemblyLine#getItemConsumptionAmountArray}. + * @Return The number of parallel recipes, or 0 if recipe is not satisfied at all. 0 < number < 1 means that + * inputs are found but not enough. + */ + public static double maxParallelCalculatedByInputItems(ArrayList<GT_MetaTileEntity_Hatch_InputBus> inputBusses, + int maxParallel, int[] itemConsumptions, Map<GT_Utility.ItemId, ItemStack> inputsFromME) { + // Recipe item matching is done in the generation of itemConsumptions. + + Map<GT_Utility.ItemId, Long> itemConsumptionsFromME = new Object2LongOpenHashMap<>(); + double currentParallel = maxParallel; + + // Calculate the amount of each item to consume from ME + for (int i = 0; i < itemConsumptions.length; i++) { + GT_MetaTileEntity_Hatch_InputBus inputBus = inputBusses.get(i); + if (!inputBus.isValid()) return 0; + if (inputBus instanceof GT_MetaTileEntity_Hatch_InputBus_ME meBus) { + ItemStack item = meBus.getShadowItemStack(0); + if (item == null) return 0; + GT_Utility.ItemId id = GT_Utility.ItemId.createNoCopy(item); + itemConsumptionsFromME.merge(id, (long) itemConsumptions[i], Long::sum); + } + } + // Calculate parallel from ME input busses + for (Entry<GT_Utility.ItemId, Long> entry : itemConsumptionsFromME.entrySet()) { + if (!inputsFromME.containsKey(entry.getKey())) return 0; + long consume = entry.getValue(); + // For non-consumed inputs + if (consume == 0) continue; + currentParallel = Math + .min(currentParallel, (double) inputsFromME.get(entry.getKey()).stackSize / consume); + if (currentParallel <= 0) return 0; + } + + // Calculate parallel from regular input busses + for (int i = 0; i < itemConsumptions.length; i++) { + GT_MetaTileEntity_Hatch_InputBus inputBus = inputBusses.get(i); + if (!inputBus.isValid()) return 0; + if (inputBus instanceof GT_MetaTileEntity_Hatch_InputBus_ME) continue; + + ItemStack item = inputBus.getStackInSlot(0); + if (item == null) return 0; + // For non-consumed inputs + if (itemConsumptions[i] == 0) continue; + currentParallel = Math.min(currentParallel, (double) item.stackSize / itemConsumptions[i]); + if (currentParallel <= 0) return 0; + } + return currentParallel; + } + + /** + * @param inputHatches Input hatch list to check. Usually the input hatch list of multi. + * @param fluidConsumptions Fluid inputs of the recipe. + * @return The number of parallel recipes, or 0 if recipe is not satisfied at all. 0 < number < 1 means that + * fluids are found but not enough. + */ + public static double maxParallelCalculatedByInputFluids(ArrayList<GT_MetaTileEntity_Hatch_Input> inputHatches, + int maxParallel, FluidStack[] fluidConsumptions, Map<Fluid, FluidStack> fluidsFromME) { + Map<Fluid, Long> fluidConsumptionsFromME = new Reference2LongOpenHashMap<>(); + double currentParallel = maxParallel; + + // Calculate the amount of each fluid to consume from ME + for (int i = 0; i < fluidConsumptions.length; i++) { + GT_MetaTileEntity_Hatch_Input inputHatch = inputHatches.get(i); + if (!inputHatch.isValid()) return 0; + if (inputHatch instanceof GT_MetaTileEntity_Hatch_Input_ME meHatch) { + FluidStack fluid = meHatch.getShadowFluidStack(0); + if (fluid == null) return 0; + if (!GT_Utility.areFluidsEqual(fluid, fluidConsumptions[i])) return 0; + fluidConsumptionsFromME.merge(fluid.getFluid(), (long) fluidConsumptions[i].amount, Long::sum); + } + } + // Calculate parallel from ME input hatches + for (Entry<Fluid, Long> entry : fluidConsumptionsFromME.entrySet()) { + Fluid fluid = entry.getKey(); + if (!fluidsFromME.containsKey(fluid)) return 0; + long consume = entry.getValue(); + currentParallel = Math.min(currentParallel, (double) fluidsFromME.get(fluid).amount / consume); + if (currentParallel <= 0) return 0; + } + + // Calculate parallel from regular input hatches + for (int i = 0; i < fluidConsumptions.length; i++) { + GT_MetaTileEntity_Hatch_Input inputHatch = inputHatches.get(i); + if (!inputHatch.isValid()) return 0; + if (inputHatch instanceof GT_MetaTileEntity_Hatch_Input_ME) continue; + + FluidStack fluid; + if (inputHatch instanceof GT_MetaTileEntity_Hatch_MultiInput multiInput) { + fluid = multiInput.getFluid(0); + } else { + fluid = inputHatch.getFillableStack(); + } + if (fluid == null) return 0; + if (!GT_Utility.areFluidsEqual(fluid, fluidConsumptions[i])) return 0; + currentParallel = Math.min(currentParallel, (double) fluid.amount / fluidConsumptions[i].amount); + if (currentParallel <= 0) return 0; + } + return currentParallel; + } + + /** + * WARNING: Ensure that item inputs are enough to be consumed with + * {@link GT_Recipe_AssemblyLine#maxParallelCalculatedByInputItems} before calling this method! + * + * @param inputBusses Input bus list to check. Usually the input bus list of multi. + * @param itemConsumptions Should be generated by {@link GT_Recipe_AssemblyLine#getItemConsumptionAmountArray}. + */ + public static void consumeInputItems(ArrayList<GT_MetaTileEntity_Hatch_InputBus> inputBusses, + int amountMultiplier, int[] itemConsumptions, Map<GT_Utility.ItemId, ItemStack> inputsFromME) { + for (int i = 0; i < itemConsumptions.length; i++) { + GT_MetaTileEntity_Hatch_InputBus inputBus = inputBusses.get(i); + if (!inputBus.isValid()) continue; + ItemStack item; + if (inputBus instanceof GT_MetaTileEntity_Hatch_InputBus_ME meBus) { + item = inputsFromME.get(GT_Utility.ItemId.createNoCopy(meBus.getShadowItemStack(0))); + } else { + item = inputBus.getStackInSlot(0); + } + item.stackSize -= itemConsumptions[i] * amountMultiplier; + } + } + + /** + * WARNING: Ensure that fluid inputs are enough to be consumed with + * {@link GT_Recipe_AssemblyLine#maxParallelCalculatedByInputFluids} before calling this method! + * + * @param inputHatches Input hatch list to check. Usually the input hatch list of multi. + * @param fluidConsumptions Fluid inputs of the recipe. + */ + public static void consumeInputFluids(ArrayList<GT_MetaTileEntity_Hatch_Input> inputHatches, + int amountMultiplier, FluidStack[] fluidConsumptions, Map<Fluid, FluidStack> fluidsFromME) { + for (int i = 0; i < fluidConsumptions.length; i++) { + GT_MetaTileEntity_Hatch_Input inputHatch = inputHatches.get(i); + if (!inputHatch.isValid()) continue; + FluidStack fluid; + if (inputHatch instanceof GT_MetaTileEntity_Hatch_Input_ME meHatch) { + fluid = fluidsFromME.get( + meHatch.getShadowFluidStack(0) + .getFluid()); + } else if (inputHatch instanceof GT_MetaTileEntity_Hatch_MultiInput multiInput) { + fluid = multiInput.getFluid(0); + } else { + fluid = inputHatch.getFillableStack(); + } + fluid.amount -= fluidConsumptions[i].amount * amountMultiplier; + } + } + } + + public static class GT_Recipe_WithAlt extends GT_Recipe { + + public ItemStack[][] mOreDictAlt; + + /** + * Only for {@link GT_RecipeBuilder}. + */ + GT_Recipe_WithAlt(ItemStack[] mInputs, ItemStack[] mOutputs, FluidStack[] mFluidInputs, + FluidStack[] mFluidOutputs, int[] mChances, Object mSpecialItems, int mDuration, int mEUt, + int mSpecialValue, boolean mEnabled, boolean mHidden, boolean mFakeRecipe, boolean mCanBeBuffered, + boolean mNeedsEmptyOutput, boolean nbtSensitive, String[] neiDesc, + @Nullable IRecipeMetadataStorage metadataStorage, RecipeCategory recipeCategory, + ItemStack[][] mOreDictAlt) { + super( + mInputs, + mOutputs, + mFluidInputs, + mFluidOutputs, + mChances, + mSpecialItems, + mDuration, + mEUt, + mSpecialValue, + mEnabled, + mHidden, + mFakeRecipe, + mCanBeBuffered, + mNeedsEmptyOutput, + nbtSensitive, + neiDesc, + metadataStorage, + recipeCategory); + this.mOreDictAlt = mOreDictAlt; + } + + public GT_Recipe_WithAlt(boolean aOptimize, ItemStack[] aInputs, ItemStack[] aOutputs, Object aSpecialItems, + int[] aChances, FluidStack[] aFluidInputs, FluidStack[] aFluidOutputs, int aDuration, int aEUt, + int aSpecialValue, ItemStack[][] aAlt) { + super( + aOptimize, + aInputs, + aOutputs, + aSpecialItems, + aChances, + aFluidInputs, + aFluidOutputs, + aDuration, + aEUt, + aSpecialValue); + mOreDictAlt = aAlt; + } + + public Object getAltRepresentativeInput(int aIndex) { + if (aIndex < 0) return null; + if (aIndex < mOreDictAlt.length) { + if (mOreDictAlt[aIndex] != null && mOreDictAlt[aIndex].length > 0) { + ItemStack[] rStacks = new ItemStack[mOreDictAlt[aIndex].length]; + for (int i = 0; i < mOreDictAlt[aIndex].length; i++) { + rStacks[i] = GT_Utility.copyOrNull(mOreDictAlt[aIndex][i]); + } + return rStacks; + } + } + if (aIndex >= mInputs.length) return null; + return GT_Utility.copyOrNull(mInputs[aIndex]); + } + } +} diff --git a/src/main/java/gregtech/api/util/GT_RecipeBuilder.java b/src/main/java/gregtech/api/util/GT_RecipeBuilder.java new file mode 100644 index 0000000000..6f7c9a81bb --- /dev/null +++ b/src/main/java/gregtech/api/util/GT_RecipeBuilder.java @@ -0,0 +1,933 @@ +package gregtech.api.util; + +import static gregtech.api.util.GT_RecipeMapUtil.SPECIAL_VALUE_ALIASES; +import static gregtech.api.util.GT_Utility.copyFluidArray; +import static gregtech.api.util.GT_Utility.copyItemArray; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Stream; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import net.minecraft.item.ItemStack; +import net.minecraft.launchwrapper.Launch; +import net.minecraftforge.fluids.FluidStack; + +import org.jetbrains.annotations.Contract; + +import gregtech.GT_Mod; +import gregtech.api.enums.Mods; +import gregtech.api.interfaces.IRecipeMap; +import gregtech.api.recipe.RecipeCategory; +import gregtech.api.recipe.RecipeMetadataKey; +import gregtech.api.recipe.metadata.IRecipeMetadataStorage; +import gregtech.api.recipe.metadata.RecipeMetadataStorage; +import gregtech.api.util.extensions.ArrayExt; + +@SuppressWarnings({ "unused", "UnusedReturnValue" }) +public class GT_RecipeBuilder { + + // debug mode expose problems. panic mode help you check nothing is wrong-ish without you actively monitoring + private static final boolean DEBUG_MODE_NULL; + private static boolean PANIC_MODE_NULL; + private static final boolean DEBUG_MODE_INVALID; + private static final boolean PANIC_MODE_INVALID; + private static final boolean DEBUG_MODE_COLLISION; + private static final boolean PANIC_MODE_COLLISION; + + public static final int WILDCARD = 32767; + + // time units + public static final int HOURS = 20 * 60 * 60; + public static final int MINUTES = 20 * 60; + public static final int SECONDS = 20; + public static final int TICKS = 1; + + // fluid units + public static final int INGOTS = 144; + public static final int HALF_INGOT = 72; + public static final int QUARTER_INGOT = 36; + public static final int EIGHTH_INGOT = 18; + public static final int NUGGETS = 16; + public static final int BUCKETS = 1000; + + static { + final boolean debugAll; + if (System.getProperties() + .containsKey("gt.recipebuilder.debug")) { + debugAll = Boolean.getBoolean("gt.recipebuilder.debug"); + } else { + // turn on debug by default in dev mode + debugAll = (boolean) Launch.blackboard.get("fml.deobfuscatedEnvironment"); + } + DEBUG_MODE_NULL = debugAll || Boolean.getBoolean("gt.recipebuilder.debug.null"); + DEBUG_MODE_INVALID = debugAll || Boolean.getBoolean("gt.recipebuilder.debug.invalid"); + DEBUG_MODE_COLLISION = debugAll || Boolean.getBoolean("gt.recipebuilder.debug.collision"); + + final boolean panicAll = Boolean.getBoolean("gt.recipebuilder.panic"); + PANIC_MODE_NULL = panicAll || Boolean.getBoolean("gt.recipebuilder.panic.null"); + PANIC_MODE_INVALID = panicAll || Boolean.getBoolean("gt.recipebuilder.panic.invalid"); + PANIC_MODE_COLLISION = panicAll || Boolean.getBoolean("gt.recipebuilder.panic.collision"); + } + + protected ItemStack[] inputsBasic = new ItemStack[0]; + protected Object[] inputsOreDict; + protected ItemStack[] outputs = new ItemStack[0]; + protected ItemStack[][] alts; + protected FluidStack[] fluidInputs = new FluidStack[0]; + protected FluidStack[] fluidOutputs = new FluidStack[0]; + protected int[] chances; + protected Object special; + protected int duration = -1; + protected int eut = -1; + protected int specialValue; + protected boolean enabled = true; + protected boolean hidden = false; + protected boolean fakeRecipe = false; + protected boolean mCanBeBuffered = true; + protected boolean mNeedsEmptyOutput = false; + protected boolean nbtSensitive = false; + protected String[] neiDesc; + protected RecipeCategory recipeCategory; + protected boolean optimize = true; + @Nullable + protected IRecipeMetadataStorage metadataStorage; + protected boolean checkForCollision = true; + /** + * If recipe addition should be skipped. + */ + protected boolean skip = false; + protected boolean valid = true; + + GT_RecipeBuilder() {} + + private GT_RecipeBuilder(ItemStack[] inputsBasic, Object[] inputsOreDict, ItemStack[] outputs, ItemStack[][] alts, + FluidStack[] fluidInputs, FluidStack[] fluidOutputs, int[] chances, Object special, int duration, int eut, + int specialValue, boolean enabled, boolean hidden, boolean fakeRecipe, boolean mCanBeBuffered, + boolean mNeedsEmptyOutput, boolean nbtSensitive, String[] neiDesc, RecipeCategory recipeCategory, + boolean optimize, @Nullable IRecipeMetadataStorage metadataStorage, boolean checkForCollision, boolean skip, + boolean valid) { + this.inputsBasic = inputsBasic; + this.inputsOreDict = inputsOreDict; + this.outputs = outputs; + this.alts = alts; + this.fluidInputs = fluidInputs; + this.fluidOutputs = fluidOutputs; + this.chances = chances; + this.special = special; + this.duration = duration; + this.eut = eut; + this.specialValue = specialValue; + this.enabled = enabled; + this.hidden = hidden; + this.fakeRecipe = fakeRecipe; + this.mCanBeBuffered = mCanBeBuffered; + this.mNeedsEmptyOutput = mNeedsEmptyOutput; + this.nbtSensitive = nbtSensitive; + this.neiDesc = neiDesc; + this.recipeCategory = recipeCategory; + this.optimize = optimize; + this.metadataStorage = metadataStorage; + if (this.metadataStorage != null) { + this.metadataStorage = this.metadataStorage.copy(); + } + this.checkForCollision = checkForCollision; + this.skip = skip; + this.valid = valid; + } + + // region helper methods + + private static FluidStack[] fix(FluidStack[] fluidInputs) { + return Arrays.stream(fluidInputs) + .filter(Objects::nonNull) + .map(FluidStack::copy) + .toArray(FluidStack[]::new); + } + + private static ItemStack[] fix(ItemStack[] inputs) { + return GT_OreDictUnificator.setStackArray(true, ArrayExt.withoutTrailingNulls(inputs, ItemStack[]::new)); + } + + public static GT_RecipeBuilder builder() { + return new GT_RecipeBuilder(); + } + + /** + * Creates empty builder where only duration and EU/t are set to 0. + */ + public static GT_RecipeBuilder empty() { + return new GT_RecipeBuilder().duration(0) + .eut(0); + } + + private static boolean containsNull(Object[] arr) { + return arr == null || Arrays.stream(arr) + .anyMatch(Objects::isNull); + } + + private static void handleNullRecipeComponents(String componentType) { + // place a breakpoint here to catch all these issues + GT_Log.err.print("null detected in "); + GT_Log.err.println(componentType); + new NullPointerException().printStackTrace(GT_Log.err); + if (PANIC_MODE_NULL) { + throw new IllegalArgumentException("null in argument"); + } + } + + private static boolean debugNull() { + return DEBUG_MODE_NULL || PANIC_MODE_NULL; + } + + public static void handleInvalidRecipe() { + if (!DEBUG_MODE_INVALID && !PANIC_MODE_INVALID) { + return; + } + // place a breakpoint here to catch all these issues + GT_Log.err.print("invalid recipe"); + new IllegalArgumentException().printStackTrace(GT_Log.err); + if (PANIC_MODE_INVALID) { + throw new IllegalArgumentException("invalid recipe"); + } + } + + public static void handleRecipeCollision(String details) { + if (!DEBUG_MODE_COLLISION && !PANIC_MODE_COLLISION) { + return; + } + GT_Log.err.print("Recipe collision resulting in recipe loss detected with "); + GT_Log.err.println(details); + if (PANIC_MODE_COLLISION) { + throw new IllegalArgumentException("Recipe Collision"); + } else { + // place a breakpoint here to catch all these issues + new IllegalArgumentException().printStackTrace(GT_Log.err); + } + } + + public static void onConfigLoad() { + PANIC_MODE_NULL |= GT_Mod.gregtechproxy.crashOnNullRecipeInput; + } + + // endregion + + // region setter + + /** + * Non-OreDicted item inputs. Assumes input is unified. + */ + public GT_RecipeBuilder itemInputsUnified(ItemStack... inputs) { + if (skip) return this; + if (debugNull() && containsNull(inputs)) handleNullRecipeComponents("itemInputUnified"); + inputsBasic = ArrayExt.withoutTrailingNulls(inputs, ItemStack[]::new); + inputsOreDict = null; + alts = null; + return this; + } + + /** + * Non-OreDicted item inputs. Assumes input is not unified. + */ + public GT_RecipeBuilder itemInputs(ItemStack... inputs) { + if (skip) return this; + if (debugNull() && containsNull(inputs)) handleNullRecipeComponents("itemInputs"); + inputsBasic = fix(inputs); + inputsOreDict = null; + alts = null; + return this; + } + + /** + * OreDicted item inputs. Currently only used for assline recipes adder. + */ + public GT_RecipeBuilder itemInputs(Object... inputs) { + if (skip) return this; + inputsOreDict = inputs; + alts = new ItemStack[inputs.length][]; + for (int i = 0, inputsLength = inputs.length; i < inputsLength; i++) { + Object input = inputs[i]; + if (input instanceof ItemStack) { + alts[i] = new ItemStack[] { (ItemStack) input }; + } else if (input instanceof ItemStack[]) { + alts[i] = ((ItemStack[]) input).clone(); + } else if (input instanceof Object[]arr) { + if (arr.length != 2) continue; + List<ItemStack> ores = GT_OreDictUnificator.getOres(arr[0]); + if (ores.isEmpty()) continue; + int size = ((Number) arr[1]).intValue(); + alts[i] = ores.stream() + .map(s -> GT_Utility.copyAmount(size, s)) + .filter(GT_Utility::isStackValid) + .toArray(ItemStack[]::new); + } else if (input == null) { + handleNullRecipeComponents("recipe oredict input"); + alts[i] = new ItemStack[0]; + } else { + throw new IllegalArgumentException("index " + i + ", unexpected type: " + input.getClass()); + } + } + inputsBasic = Arrays.stream(alts) + .map(ss -> ss.length > 0 ? ss[0] : null) + .toArray(ItemStack[]::new); + // optimize cannot handle recipes with alts + return noOptimize(); + } + + public GT_RecipeBuilder itemOutputs(ItemStack... outputs) { + if (skip) return this; + if (debugNull() && containsNull(outputs)) handleNullRecipeComponents("itemOutputs"); + this.outputs = outputs; + if (chances != null && chances.length != outputs.length) { + throw new IllegalArgumentException("Output chances array and items array length differs"); + } + return this; + } + + /** + * Not intended to be used by recipe authors. + * Intended for recipe rewrite middlewares. + */ + public GT_RecipeBuilder itemOutputs(ItemStack[] outputs, int[] chances) { + if (skip) return this; + if (debugNull() && containsNull(outputs)) handleNullRecipeComponents("itemOutputs"); + this.outputs = outputs; + this.chances = chances; + if (chances != null && chances.length != outputs.length) { + throw new IllegalArgumentException("Output chances array and items array length differs"); + } + return this; + } + + public GT_RecipeBuilder fluidInputs(FluidStack... fluidInputs) { + if (skip) return this; + if (debugNull() && containsNull(fluidInputs)) handleNullRecipeComponents("fluidInputs"); + this.fluidInputs = fix(fluidInputs); + return this; + } + + public GT_RecipeBuilder fluidOutputs(FluidStack... fluidOutputs) { + if (skip) return this; + if (debugNull() && containsNull(fluidOutputs)) handleNullRecipeComponents("fluidOutputs"); + this.fluidOutputs = fix(fluidOutputs); + return this; + } + + public GT_RecipeBuilder outputChances(int... chances) { + if (skip) return this; + if (outputs != null && chances.length != outputs.length) { + throw new IllegalArgumentException("Output chances array and items array length differs"); + } + this.chances = chances; + return this; + } + + public GT_RecipeBuilder special(Object special) { + this.special = special; + return this; + } + + /** + * Really just {@link #special(Object)}, but with a different signature to make it less confusing. WARNING: only for + * legacy recipe map. do not abuse. + */ + public GT_RecipeBuilder specialItem(ItemStack specialItem) { + return special(specialItem); + } + + public GT_RecipeBuilder duration(int duration) { + this.duration = duration; + return this; + } + + public GT_RecipeBuilder duration(long duration) { + this.duration = (int) duration; + return this; + } + + public GT_RecipeBuilder eut(int eut) { + this.eut = eut; + return this; + } + + public GT_RecipeBuilder eut(long eut) { + this.eut = (int) eut; + return this; + } + + /** + * prefer to use metadata over this. should only use when the target recipe map does not yet support metadata + * system, or it's to bridge legacy code and modern code. + */ + public GT_RecipeBuilder specialValue(int specialValue) { + this.specialValue = specialValue; + return this; + } + + // I don't expect anyone to actually call this... + public GT_RecipeBuilder disabled() { + this.enabled = false; + return this; + } + + public GT_RecipeBuilder hidden() { + this.hidden = true; + return this; + } + + public GT_RecipeBuilder fake() { + this.fakeRecipe = true; + return this; + } + + public GT_RecipeBuilder noBuffer() { + this.mCanBeBuffered = false; + return this; + } + + public GT_RecipeBuilder needsEmptyOutput() { + this.mNeedsEmptyOutput = true; + return this; + } + + public GT_RecipeBuilder nbtSensitive() { + this.nbtSensitive = true; + return this; + } + + public GT_RecipeBuilder setNEIDesc(String... neiDesc) { + this.neiDesc = neiDesc; + return this; + } + + public GT_RecipeBuilder recipeCategory(RecipeCategory recipeCategory) { + this.recipeCategory = recipeCategory; + return this; + } + + /** + * Prevent the resulting recipe from optimizing recipe, which is a process that reduce recipe batch size. + */ + public GT_RecipeBuilder noOptimize() { + this.optimize = false; + return this; + } + + /** + * Prevents checking collision with existing recipes when adding the built recipe. + */ + public GT_RecipeBuilder ignoreCollision() { + this.checkForCollision = false; + return this; + } + + /** + * Sets metadata of the recipe. It can be used for recipe emitter to do special things, or for being stored in the + * built recipe and used for actual recipe processing. + * <p> + * {@link GT_RecipeConstants} has a series of metadata keys. Or you can create one by yourself. + */ + public <T> GT_RecipeBuilder metadata(RecipeMetadataKey<T> key, T value) { + if (skip) return this; + if (metadataStorage == null) { + metadataStorage = new RecipeMetadataStorage(); + } + metadataStorage.store(key, value); + return this; + } + + /** + * Gets metadata already set for this builder. Can return null. Use + * {@link #getMetadataOrDefault(RecipeMetadataKey, Object)} + * if you want to specify default value. + */ + @Nullable + public <T> T getMetadata(RecipeMetadataKey<T> key) { + if (metadataStorage == null) { + return null; + } + return key.cast(metadataStorage.getMetadata(key)); + } + + /** + * Gets metadata already set for this builder with default value. Does not return null unless default value is null. + */ + @Contract("_, !null -> !null") + @Nullable + public <T> T getMetadataOrDefault(RecipeMetadataKey<T> key, T defaultValue) { + if (metadataStorage == null) { + return defaultValue; + } + return key.cast(metadataStorage.getMetadataOrDefault(key, defaultValue)); + } + + /** + * Specifies mods required to add the recipe. If any of the mods is not loaded, all the operations for this builder + * will be ignored. + * + * @param mods Mod(s) required for the recipe. + */ + public GT_RecipeBuilder requireMods(Mods... mods) { + skip = Stream.of(mods) + .anyMatch(mod -> !mod.isModLoaded()); + return this; + } + + public GT_RecipeBuilder requiresCleanRoom() { + return metadata(GT_RecipeConstants.CLEANROOM, true); + } + + public GT_RecipeBuilder requiresLowGravity() { + return metadata(GT_RecipeConstants.LOW_GRAVITY, true); + } + + // endregion + + private static <T> T[] copy(T[] arr) { + return arr == null ? null : arr.clone(); + } + + private static int[] copy(int[] arr) { + return arr == null ? null : arr.clone(); + } + + /** + * produce a deep copy of current values. anything unset will remain unset. IMPORTANT: If metadata contains mutable + * value, they will not be cloned! + * <p> + * checkout docs/RecipeBuilder.md for more info on whether to copy or not. + */ + public GT_RecipeBuilder copy() { + return new GT_RecipeBuilder( + copyItemArray(inputsBasic), + copy(inputsOreDict), + copyItemArray(outputs), + copy(alts), + copyFluidArray(fluidInputs), + copyFluidArray(fluidOutputs), + copy(chances), + special, + duration, + eut, + specialValue, + enabled, + hidden, + fakeRecipe, + mCanBeBuffered, + mNeedsEmptyOutput, + nbtSensitive, + copy(neiDesc), + recipeCategory, + optimize, + metadataStorage, + checkForCollision, + skip, + valid); + } + + /** + * produce a deep copy of current values. anything unset will remain unset. discard all existing metadata + */ + public GT_RecipeBuilder copyNoMetadata() { + return new GT_RecipeBuilder( + copyItemArray(inputsBasic), + copy(inputsOreDict), + copyItemArray(outputs), + copy(alts), + copyFluidArray(fluidInputs), + copyFluidArray(fluidOutputs), + copy(chances), + special, + duration, + eut, + specialValue, + enabled, + hidden, + fakeRecipe, + mCanBeBuffered, + mNeedsEmptyOutput, + nbtSensitive, + copy(neiDesc), + recipeCategory, + optimize, + null, + checkForCollision, + skip, + valid); + } + + // region getter + + public ItemStack getItemInputBasic(int index) { + return index < inputsBasic.length ? inputsBasic[index] : null; + } + + public Object getItemInputOreDict(int index) { + return index < inputsOreDict.length ? inputsOreDict[index] : null; + } + + public ItemStack getItemOutput(int index) { + return index < outputs.length ? outputs[index] : null; + } + + public FluidStack getFluidInput(int index) { + return index < fluidInputs.length ? fluidInputs[index] : null; + } + + public FluidStack getFluidOutput(int index) { + return index < fluidOutputs.length ? fluidOutputs[index] : null; + } + + public ItemStack[] getItemInputsBasic() { + return inputsBasic; + } + + public Object[] getItemInputsOreDict() { + return inputsOreDict; + } + + public ItemStack[] getItemOutputs() { + return outputs; + } + + public FluidStack[] getFluidInputs() { + return fluidInputs; + } + + public FluidStack[] getFluidOutputs() { + return fluidOutputs; + } + + public int getDuration() { + return duration; + } + + public int[] getChances() { + return chances; + } + + public int getEUt() { + return eut; + } + + public RecipeCategory getRecipeCategory() { + return recipeCategory; + } + + public boolean isOptimize() { + return optimize; + } + + public boolean isCheckForCollision() { + return checkForCollision; + } + + // endregion + + // region validator + + public GT_RecipeBuilder clearInvalid() { + valid = true; + return this; + } + + public GT_RecipeBuilder invalidate() { + valid = false; + return this; + } + + public boolean isValid() { + return valid; + } + + private static boolean isArrayValid(@Nonnull Object[] arr, int min, int max) { + int count = 0; + for (Object o : arr) { + if (o != null) count += 1; + } + return min <= count && max >= count; + } + + /** + * Validate if input item match requirement. Return as invalidated if fails prereq. Specify -1 as min to allow + * unset. Both bound inclusive. Only supposed to be called by IRecipeMap and not client code. + */ + public GT_RecipeBuilder validateNoInput() { + if (skip) return this; + return GT_Utility.isArrayEmptyOrNull(inputsBasic) ? this : invalidate(); + } + + /** + * Validate if input fluid match requirement. Return as invalidated if fails prereq. Specify -1 as min to allow + * unset. Both bound inclusive. Only supposed to be called by IRecipeMap and not client code. + */ + public GT_RecipeBuilder validateNoInputFluid() { + if (skip) return this; + return GT_Utility.isArrayEmptyOrNull(fluidInputs) ? this : invalidate(); + } + + /** + * Validate if output item match requirement. Return as invalidated if fails prereq. Specify -1 as min to allow + * unset. Both bound inclusive. Only supposed to be called by IRecipeMap and not client code. + */ + public GT_RecipeBuilder validateNoOutput() { + if (skip) return this; + return GT_Utility.isArrayEmptyOrNull(outputs) ? this : invalidate(); + } + + /** + * Validate if output fluid match requirement. Return as invalidated if fails prereq. Specify -1 as min to allow + * unset. Both bound inclusive. Only supposed to be called by IRecipeMap and not client code. + */ + public GT_RecipeBuilder validateNoOutputFluid() { + if (skip) return this; + return GT_Utility.isArrayEmptyOrNull(fluidOutputs) ? this : invalidate(); + } + + /** + * Validate if input item match requirement. Return as invalidated if fails prereq. Specify -1 as min to allow + * unset. Both bound inclusive. Only supposed to be called by IRecipeMap and not client code. + */ + public GT_RecipeBuilder validateInputCount(int min, int max) { + if (skip) return this; + if (inputsBasic == null) return min < 0 ? this : invalidate(); + return isArrayValid(inputsBasic, min, max) ? this : invalidate(); + } + + /** + * Validate if input fluid match requirement. Return as invalidated if fails prereq. Specify -1 as min to allow + * unset. Both bound inclusive. Only supposed to be called by IRecipeMap and not client code. + */ + public GT_RecipeBuilder validateInputFluidCount(int min, int max) { + if (skip) return this; + if (fluidInputs == null) return min < 0 ? this : invalidate(); + return isArrayValid(fluidInputs, min, max) ? this : invalidate(); + } + + /** + * Validate if output item match requirement. Return as invalidated if fails prereq. Specify -1 as min to allow + * unset. Both bound inclusive. Only supposed to be called by IRecipeMap and not client code. + */ + public GT_RecipeBuilder validateOutputCount(int min, int max) { + if (skip) return this; + if (outputs == null) return min < 0 ? this : invalidate(); + return isArrayValid(outputs, min, max) ? this : invalidate(); + } + + /** + * Validate if output fluid match requirement. Return as invalidated if fails prereq. Specify -1 as min to allow + * unset. Both bound inclusive. Only supposed to be called by IRecipeMap and not client code. + */ + public GT_RecipeBuilder validateOutputFluidCount(int min, int max) { + if (skip) return this; + if (fluidOutputs == null) return min < 0 ? this : invalidate(); + return isArrayValid(fluidOutputs, min, max) ? this : invalidate(); + } + + public GT_RecipeBuilder validateAnyInput() { + if (skip) return this; + if (fluidInputs != null && isArrayValid(fluidInputs, 1, Integer.MAX_VALUE)) { + return this; + } + if (inputsBasic != null && isArrayValid(inputsBasic, 1, Integer.MAX_VALUE)) { + return this; + } + return invalidate(); + } + + public GT_RecipeBuilder validateAnyOutput() { + if (skip) return this; + if (fluidOutputs != null && isArrayValid(fluidOutputs, 1, Integer.MAX_VALUE)) { + return this; + } + if (outputs != null && isArrayValid(outputs, 1, Integer.MAX_VALUE)) { + return this; + } + return invalidate(); + } + + // endregion + + /** + * Builds new recipe, without custom behavior of recipemaps. For adding recipe to recipemap, + * use {@link #addTo} instead. + * + * @return Built recipe. Returns empty if failed to build. + */ + public Optional<GT_Recipe> build() { + if (skip) { + return Optional.empty(); + } + if (!valid) { + handleInvalidRecipe(); + return Optional.empty(); + } + preBuildChecks(); + optimize(); + return Optional.of( + decorate( + new GT_Recipe( + inputsBasic, + outputs, + fluidInputs, + fluidOutputs, + chances, + special, + duration, + eut, + specialValue, + enabled, + hidden, + fakeRecipe, + mCanBeBuffered, + mNeedsEmptyOutput, + nbtSensitive, + neiDesc, + metadataStorage, + recipeCategory))); + } + + public GT_RecipeBuilder forceOreDictInput() { + if (inputsOreDict != null || inputsBasic == null) return this; + return itemInputs((Object[]) inputsBasic); + } + + public Optional<GT_Recipe.GT_Recipe_WithAlt> buildWithAlt() { + if (skip) { + return Optional.empty(); + } + if (inputsOreDict == null) { + throw new UnsupportedOperationException(); + } + if (!valid) { + handleInvalidRecipe(); + return Optional.empty(); + } + preBuildChecks(); + // no optimize. + return Optional.of( + decorate( + new GT_Recipe.GT_Recipe_WithAlt( + inputsBasic, + outputs, + fluidInputs, + fluidOutputs, + chances, + special, + duration, + eut, + specialValue, + enabled, + hidden, + fakeRecipe, + mCanBeBuffered, + mNeedsEmptyOutput, + nbtSensitive, + neiDesc, + metadataStorage, + recipeCategory, + alts))); + } + + private void preBuildChecks() { + if (duration == -1) throw new IllegalStateException("no duration"); + if (eut == -1) throw new IllegalStateException("no eut"); + } + + private void optimize() { + if (optimize) { + ArrayList<ItemStack> l = new ArrayList<>(); + l.addAll(Arrays.asList(inputsBasic)); + l.addAll(Arrays.asList(outputs)); + for (int i = 0; i < l.size(); i++) if (l.get(i) == null) l.remove(i--); + + outer: for (byte i = (byte) Math.min(64, duration / 16); i > 1; i--) { + if (duration / i >= 16) { + for (ItemStack stack : l) { + if (stack.stackSize % i != 0) continue outer; + } + for (FluidStack fluidInput : fluidInputs) { + if (fluidInput.amount % i != 0) continue outer; + } + for (FluidStack fluidOutput : fluidOutputs) { + if (fluidOutput.amount % i != 0) continue outer; + } + for (ItemStack itemStack : l) itemStack.stackSize /= i; + for (FluidStack fluidInput : fluidInputs) fluidInput.amount /= i; + for (FluidStack fluidOutput : fluidOutputs) fluidOutput.amount /= i; + duration /= i; + } + } + optimize = false; + } + } + + private <T extends GT_Recipe> T decorate(T r) { + r.mHidden = hidden; + r.mCanBeBuffered = mCanBeBuffered; + r.mNeedsEmptyOutput = mNeedsEmptyOutput; + r.isNBTSensitive = nbtSensitive; + r.mFakeRecipe = fakeRecipe; + r.mEnabled = enabled; + if (neiDesc != null) r.setNeiDesc(neiDesc); + applyDefaultSpecialValues(r); + return r; + } + + private void applyDefaultSpecialValues(GT_Recipe recipe) { + if (recipe.mSpecialValue != 0) return; + + int specialValue = 0; + if (getMetadataOrDefault(GT_RecipeConstants.LOW_GRAVITY, false)) specialValue -= 100; + if (getMetadataOrDefault(GT_RecipeConstants.CLEANROOM, false)) specialValue -= 200; + for (RecipeMetadataKey<Integer> ident : SPECIAL_VALUE_ALIASES) { + Integer metadata = getMetadataOrDefault(ident, null); + if (metadata != null) { + specialValue = metadata; + break; + } + } + recipe.mSpecialValue = specialValue; + } + + public Collection<GT_Recipe> addTo(IRecipeMap recipeMap) { + if (skip) { + return Collections.emptyList(); + } + return recipeMap.doAdd(this); + } + + public GT_RecipeBuilder reset() { + metadataStorage = null; + alts = null; + chances = null; + duration = -1; + enabled = true; + eut = -1; + fakeRecipe = false; + fluidInputs = null; + fluidOutputs = null; + hidden = false; + inputsBasic = null; + inputsOreDict = null; + mCanBeBuffered = true; + mNeedsEmptyOutput = false; + nbtSensitive = false; + neiDesc = null; + recipeCategory = null; + optimize = true; + outputs = null; + special = null; + specialValue = 0; + skip = false; + valid = true; + return this; + } +} diff --git a/src/main/java/gregtech/api/util/GT_RecipeConstants.java b/src/main/java/gregtech/api/util/GT_RecipeConstants.java new file mode 100644 index 0000000000..d9d6c7d7d2 --- /dev/null +++ b/src/main/java/gregtech/api/util/GT_RecipeConstants.java @@ -0,0 +1,332 @@ +package gregtech.api.util; + +import static gregtech.api.util.GT_RecipeMapUtil.convertCellToFluid; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Optional; + +import net.minecraft.init.Items; +import net.minecraft.item.ItemStack; +import net.minecraftforge.fluids.FluidStack; + +import cpw.mods.fml.common.registry.GameRegistry; +import gregtech.api.enums.ItemList; +import gregtech.api.enums.Materials; +import gregtech.api.interfaces.IRecipeMap; +import gregtech.api.recipe.RecipeCategories; +import gregtech.api.recipe.RecipeMaps; +import gregtech.api.recipe.RecipeMetadataKey; +import gregtech.api.recipe.metadata.SimpleRecipeMetadataKey; + +// this class is intended to be import-static-ed on every recipe script +// so take care to not put unrelated stuff here! +public class GT_RecipeConstants { + + /** + * Set to true to signal the recipe require low gravity. do nothing if recipe set specialValue explicitly. Can + * coexist with CLEANROOM just fine + */ + public static final RecipeMetadataKey<Boolean> LOW_GRAVITY = SimpleRecipeMetadataKey + .create(Boolean.class, "low_gravity"); + /** + * Set to true to signal the recipe require cleanroom. do nothing if recipe set specialValue explicitly. Can coexist + * with LOW_GRAVITY just fine + */ + public static final RecipeMetadataKey<Boolean> CLEANROOM = SimpleRecipeMetadataKey + .create(Boolean.class, "cleanroom"); + /** + * Common additive to use in recipe, e.g. for PBF, this is coal amount. + */ + public static final RecipeMetadataKey<Integer> ADDITIVE_AMOUNT = SimpleRecipeMetadataKey + .create(Integer.class, "additives"); + /** + * Used for fusion reactor. Denotes ignition threshold. + */ + public static final RecipeMetadataKey<Integer> FUSION_THRESHOLD = SimpleRecipeMetadataKey + .create(Integer.class, "fusion_threshold"); + /** + * Research time in a scanner used in ticks. + */ + public static final RecipeMetadataKey<Integer> RESEARCH_TIME = SimpleRecipeMetadataKey + .create(Integer.class, "research_time"); + /** + * Fuel type. TODO should we use enum directly? + */ + public static final RecipeMetadataKey<Integer> FUEL_TYPE = SimpleRecipeMetadataKey + .create(Integer.class, "fuel_type"); + /** + * Fuel value. + */ + public static final RecipeMetadataKey<Integer> FUEL_VALUE = SimpleRecipeMetadataKey + .create(Integer.class, "fuel_value"); + /** + * Required heat for heating coil (Kelvin). + */ + public static final RecipeMetadataKey<Integer> COIL_HEAT = SimpleRecipeMetadataKey + .create(Integer.class, "coil_heat"); + /** + * Research item used by assline recipes. + */ + public static final RecipeMetadataKey<ItemStack> RESEARCH_ITEM = SimpleRecipeMetadataKey + .create(ItemStack.class, "research_item"); + /** + * For assembler. It accepts a single item as oredict. It looks like no one uses this anyway... + */ + public static final RecipeMetadataKey<Object> OREDICT_INPUT = SimpleRecipeMetadataKey + .create(Object.class, "oredict_input"); + /** + * Replicator output material. + */ + public static final RecipeMetadataKey<Materials> MATERIAL = SimpleRecipeMetadataKey + .create(Materials.class, "material"); + /** + * Marker for {@link #UniversalArcFurnace} to tell that the recipe belongs to recycling category. + */ + public static final RecipeMetadataKey<Boolean> RECYCLE = SimpleRecipeMetadataKey.create(Boolean.class, "recycle"); + /** + * For Microwave. + */ + public static final RecipeMetadataKey<Boolean> EXPLODE = SimpleRecipeMetadataKey.create(Boolean.class, "explode"); + /** + * For Microwave. + */ + public static final RecipeMetadataKey<Boolean> ON_FIRE = SimpleRecipeMetadataKey.create(Boolean.class, "on_fire"); + + /** + * Add a arc furnace recipe. Adds to both normal arc furnace and plasma arc furnace. + * Will override the fluid input with oxygen/plasma for the respective recipe maps, so there is no point setting it. + */ + public static final IRecipeMap UniversalArcFurnace = IRecipeMap.newRecipeMap(builder -> { + if (!GT_Utility.isArrayOfLength(builder.getItemInputsBasic(), 1) + || GT_Utility.isArrayEmptyOrNull(builder.getItemOutputs())) return Collections.emptyList(); + int aDuration = builder.getDuration(); + if (aDuration <= 0) { + return Collections.emptyList(); + } + builder.duration(aDuration); + boolean recycle = builder.getMetadataOrDefault(RECYCLE, false); + Collection<GT_Recipe> ret = new ArrayList<>(); + for (Materials mat : new Materials[] { Materials.Argon, Materials.Nitrogen }) { + int tPlasmaAmount = (int) Math.max(1L, aDuration / (mat.getMass() * 16L)); + GT_RecipeBuilder plasmaBuilder = builder.copy() + .fluidInputs(mat.getPlasma(tPlasmaAmount)) + .fluidOutputs(mat.getGas(tPlasmaAmount)); + if (recycle) { + plasmaBuilder.recipeCategory(RecipeCategories.plasmaArcFurnaceRecycling); + } + ret.addAll(RecipeMaps.plasmaArcFurnaceRecipes.doAdd(plasmaBuilder)); + } + GT_RecipeBuilder arcBuilder = builder.copy() + .fluidInputs(Materials.Oxygen.getGas(aDuration)); + if (recycle) { + arcBuilder.recipeCategory(RecipeCategories.arcFurnaceRecycling); + } + ret.addAll(RecipeMaps.arcFurnaceRecipes.doAdd(arcBuilder)); + return ret; + }); + + /** + * Add a chemical reactor recipe to both LCR and singleblocks. + */ + public static final IRecipeMap UniversalChemical = IRecipeMap.newRecipeMap(builder -> { + for (ItemStack input : builder.getItemInputsBasic()) { + // config >= 10 -> this is a special chemical recipe that output fluid/canned fluid variant. + // it doesn't belong to multiblocks + if (GT_Utility.isAnyIntegratedCircuit(input) && input.getItemDamage() >= 10) { + return builder.addTo(RecipeMaps.chemicalReactorRecipes); + } + } + return GT_Utility.concat( + builder.copy() + .addTo(RecipeMaps.chemicalReactorRecipes), + convertCellToFluid(builder, false) + // LCR does not need cleanroom. + .metadata(CLEANROOM, false) + .addTo(RecipeMaps.multiblockChemicalReactorRecipes)); + }); + + /** + * The one and only :tm: assline recipe adder. + * Uses {@link #RESEARCH_ITEM} metadata as research item, and {@link #RESEARCH_TIME} metadata as research time, unit + * in ticks. + */ + public static final IRecipeMap AssemblyLine = IRecipeMap.newRecipeMap(builder -> { + Optional<GT_Recipe.GT_Recipe_WithAlt> rr = builder.forceOreDictInput() + .validateInputCount(4, 16) + .validateOutputCount(1, 1) + .validateOutputFluidCount(-1, 0) + .validateInputFluidCount(0, 4) + .buildWithAlt(); + // noinspection SimplifyOptionalCallChains + if (!rr.isPresent()) return Collections.emptyList(); + GT_Recipe.GT_Recipe_WithAlt r = rr.get(); + ItemStack[][] mOreDictAlt = r.mOreDictAlt; + Object[] inputs = builder.getItemInputsOreDict(); + ItemStack aResearchItem = builder.getMetadata(RESEARCH_ITEM); + if (aResearchItem == null) { + return Collections.emptyList(); + } + ItemStack aOutput = r.mOutputs[0]; + int tPersistentHash = 1; + for (int i = 0, mOreDictAltLength = mOreDictAlt.length; i < mOreDictAltLength; i++) { + ItemStack[] alts = mOreDictAlt[i]; + Object input = inputs[i]; + if (input == null) { + GT_Log.err.println( + "addAssemblingLineRecipe " + aResearchItem.getDisplayName() + + " --> " + + aOutput.getUnlocalizedName() + + " there is some null item in that recipe"); + } + if (input instanceof ItemStack) { + tPersistentHash = tPersistentHash * 31 + GT_Utility.persistentHash((ItemStack) input, true, false); + } else if (input instanceof ItemStack[]) { + for (ItemStack alt : ((ItemStack[]) input)) { + tPersistentHash = tPersistentHash * 31 + GT_Utility.persistentHash(alt, true, false); + if (alt == null) { + GT_Log.err.println( + "addAssemblingLineRecipe " + aResearchItem.getDisplayName() + + " --> " + + aOutput.getUnlocalizedName() + + " there is some null alt item in that recipe"); + } + } + tPersistentHash *= 31; + } else if (input instanceof Object[]objs) { + Arrays.sort( + alts, + Comparator + .<ItemStack, String>comparing(s -> GameRegistry.findUniqueIdentifierFor(s.getItem()).modId) + .thenComparing(s -> GameRegistry.findUniqueIdentifierFor(s.getItem()).name) + .thenComparingInt(Items.feather::getDamage) + .thenComparingInt(s -> s.stackSize)); + + tPersistentHash = tPersistentHash * 31 + (objs[0] == null ? "" : objs[0].toString()).hashCode(); + tPersistentHash = tPersistentHash * 31 + ((Number) objs[1]).intValue(); + } + } + tPersistentHash = tPersistentHash * 31 + GT_Utility.persistentHash(aResearchItem, true, false); + tPersistentHash = tPersistentHash * 31 + GT_Utility.persistentHash(aOutput, true, false); + for (FluidStack fluidInput : r.mFluidInputs) { + if (fluidInput == null) continue; + tPersistentHash = tPersistentHash * 31 + GT_Utility.persistentHash(fluidInput, true, false); + } + int aResearchTime = builder.getMetadataOrDefault(RESEARCH_TIME, 0); + tPersistentHash = tPersistentHash * 31 + aResearchTime; + tPersistentHash = tPersistentHash * 31 + r.mDuration; + tPersistentHash = tPersistentHash * 31 + r.mEUt; + Collection<GT_Recipe> ret = new ArrayList<>(3); + ret.add( + RecipeMaps.scannerFakeRecipes.addFakeRecipe( + false, + new ItemStack[] { aResearchItem }, + new ItemStack[] { aOutput }, + new ItemStack[] { ItemList.Tool_DataStick.getWithName(1L, "Writes Research result") }, + null, + null, + aResearchTime, + 30, + -201)); // means it's scanned + + ret.add( + RecipeMaps.assemblylineVisualRecipes.addFakeRecipe( + false, + r.mInputs, + new ItemStack[] { aOutput }, + new ItemStack[] { ItemList.Tool_DataStick.getWithName(1L, "Reads Research result") }, + r.mFluidInputs, + null, + r.mDuration, + r.mEUt, + 0, + r.mOreDictAlt, + false)); + + GT_Recipe.GT_Recipe_AssemblyLine tRecipe = new GT_Recipe.GT_Recipe_AssemblyLine( + aResearchItem, + aResearchTime, + r.mInputs, + r.mFluidInputs, + aOutput, + r.mDuration, + r.mEUt, + r.mOreDictAlt); + tRecipe.setPersistentHash(tPersistentHash); + GT_Recipe.GT_Recipe_AssemblyLine.sAssemblylineRecipes.add(tRecipe); + GT_AssemblyLineUtils.addRecipeToCache(tRecipe); + return ret; + }); + + /** + * Just like any normal assembler recipe, however it accepts one input item to be oredicted. Pass in the item to + * oredict via {@link #OREDICT_INPUT}. It will be used along all other item inputs as input of this recipe. + */ + public static IRecipeMap AssemblerOD = IRecipeMap.newRecipeMap(builder -> { + Collection<GT_Recipe> ret = new ArrayList<>(); + for (ItemStack input : GT_OreDictUnificator.getOresImmutable(builder.getMetadata(OREDICT_INPUT))) { + ret.addAll( + builder.copy() + .itemInputs(GT_RecipeMapUtil.appendArray(builder.getItemInputsBasic(), input)) + .addTo(RecipeMaps.assemblerRecipes)); + } + return ret; + }); + + /** + * A universal fuel adder. It's actually just a dispatcher towards all actual fuel recipe maps. + * Dispatch based on {@link #FUEL_TYPE}. Uses {@link #FUEL_VALUE} as fuel value. + * Can use {@link FuelType#ordinal()} as a human-readable form of what FUEL_TYPE should be. + * You can bypass this and add to relevant fuel maps directly if you wish. + */ + public static IRecipeMap Fuel = IRecipeMap.newRecipeMap(builder -> { + builder.validateInputCount(1, 1) + .validateNoInputFluid() + .validateOutputCount(-1, 1) + .validateNoOutputFluid(); + if (!builder.isValid()) return Collections.emptyList(); + Integer fuelType = builder.getMetadata(FUEL_TYPE); + if (fuelType == null) return Collections.emptyList(); + builder.metadata(FUEL_VALUE, builder.getMetadataOrDefault(FUEL_VALUE, 0)); + return FuelType.get(fuelType) + .getTarget() + .doAdd(builder); + }); + + public enum FuelType { + + // ORDER MATTERS. DO NOT INSERT ELEMENT BETWEEN EXISTING ONES + DieselFuel(RecipeMaps.dieselFuels), + GasTurbine(RecipeMaps.gasTurbineFuels), + // appears unused + HotFuel(RecipeMaps.hotFuels), + SemiFluid(RecipeMaps.denseLiquidFuels), + PlasmaTurbine(RecipeMaps.plasmaFuels), + Magic(RecipeMaps.magicFuels),; + + private static final FuelType[] VALUES = values(); + private final IRecipeMap target; + + FuelType(IRecipeMap target) { + this.target = target; + } + + public static FuelType get(int fuelType) { + if (fuelType < 0 || fuelType >= VALUES.length) return SemiFluid; + return VALUES[fuelType]; + } + + public IRecipeMap getTarget() { + return target; + } + } + + static { + GT_RecipeMapUtil.SPECIAL_VALUE_ALIASES.add(COIL_HEAT); + GT_RecipeMapUtil.SPECIAL_VALUE_ALIASES.add(FUSION_THRESHOLD); + GT_RecipeMapUtil.SPECIAL_VALUE_ALIASES.add(FUEL_VALUE); + } +} diff --git a/src/main/java/gregtech/api/util/GT_RecipeMapUtil.java b/src/main/java/gregtech/api/util/GT_RecipeMapUtil.java new file mode 100644 index 0000000000..3e97b56f84 --- /dev/null +++ b/src/main/java/gregtech/api/util/GT_RecipeMapUtil.java @@ -0,0 +1,226 @@ +package gregtech.api.util; + +import static gregtech.api.enums.Mods.GregTech; +import static gregtech.api.util.GT_Config.getStackConfigName; +import static gregtech.api.util.GT_Utility.isArrayEmptyOrNull; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.IdentityHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.function.Consumer; +import java.util.function.Function; + +import net.minecraft.item.ItemStack; +import net.minecraftforge.fluids.FluidStack; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; + +import cpw.mods.fml.common.Loader; +import gnu.trove.list.TIntList; +import gnu.trove.list.array.TIntArrayList; +import gregtech.api.interfaces.IRecipeMap; +import gregtech.api.recipe.RecipeMetadataKey; + +/** + * Define helpers useful in the creation of recipe maps. + */ +public class GT_RecipeMapUtil { + + public static final Function<GT_Recipe, GT_Recipe> ALL_FAKE_RECIPE = r -> { + r.mFakeRecipe = true; + return r; + }; + + public static final Function<GT_Recipe, String> FIRST_FLUID_INPUT = r -> isArrayEmptyOrNull(r.mFluidInputs) ? null + : r.mFluidInputs[0].getFluid() + .getName(); + public static final Function<GT_Recipe, String> FIRST_FLUID_OUTPUT = r -> isArrayEmptyOrNull(r.mFluidInputs) ? null + : r.mFluidOutputs[0].getFluid() + .getName(); + public static final Function<GT_Recipe, String> FIRST_FLUIDSTACK_INPUT = r -> isArrayEmptyOrNull(r.mFluidInputs) + ? null + : r.mFluidInputs[0].getUnlocalizedName(); + public static final Function<GT_Recipe, String> FIRST_FLUIDSTACK_OUTPUT = r -> isArrayEmptyOrNull(r.mFluidOutputs) + ? null + : r.mFluidOutputs[0].getUnlocalizedName(); + public static final Function<GT_Recipe, String> FIRST_ITEM_INPUT = r -> isArrayEmptyOrNull(r.mInputs) ? null + : getStackConfigName(r.mInputs[0]); + public static final Function<GT_Recipe, String> FIRST_ITEM_OUTPUT = r -> isArrayEmptyOrNull(r.mOutputs) ? null + : getStackConfigName(r.mOutputs[0]); + public static final Function<GT_Recipe, String> FIRST_ITEM_OR_FLUID_INPUT = r -> isArrayEmptyOrNull(r.mInputs) + ? isArrayEmptyOrNull(r.mFluidInputs) ? null + : r.mFluidInputs[0].getFluid() + .getName() + : getStackConfigName(r.mInputs[0]); + public static final Function<GT_Recipe, String> FIRST_ITEM_OR_FLUID_OUTPUT = r -> isArrayEmptyOrNull(r.mOutputs) + ? isArrayEmptyOrNull(r.mFluidOutputs) ? null + : r.mFluidOutputs[0].getFluid() + .getName() + : getStackConfigName(r.mOutputs[0]); + private static final Map<String, IRecipeMap> addonRecipeMaps = new HashMap<>(); + private static final Multimap<String, Consumer<IRecipeMap>> delayedActions = ArrayListMultimap.create(); + + /** + * Set of metadata that work as alias for special values. + */ + public static final Set<RecipeMetadataKey<Integer>> SPECIAL_VALUE_ALIASES = new HashSet<>(); + + public static <T> T[] appendArray(T[] arr, T val) { + T[] newArr = Arrays.copyOf(arr, arr.length + 1); + newArr[arr.length] = val; + return newArr; + } + + public static GT_RecipeTemplate asTemplate(GT_Recipe r) { + return asTemplate(r, false); + } + + public static GT_RecipeTemplate asTemplate(GT_Recipe r, boolean includeTemplate) { + return new GT_RecipeTemplate(r, includeTemplate); + } + + public static List<GT_Recipe> buildRecipeForMultiblock(GT_RecipeBuilder b) { + return buildOrEmpty(convertCellToFluid(b, true)); + + } + + public static List<GT_Recipe> buildRecipeForMultiblockNoCircuit(GT_RecipeBuilder b) { + return buildOrEmpty(convertCellToFluid(b, false)); + } + + public static GT_RecipeBuilder convertCellToFluid(GT_RecipeBuilder b, boolean removeIntegratedCircuit) { + List<ItemStack> itemInputs = new ArrayList<>(Arrays.asList(b.getItemInputsBasic())); + List<ItemStack> itemOutputs = new ArrayList<>(Arrays.asList(b.getItemOutputs())); + List<FluidStack> fluidInputs = new ArrayList<>(Arrays.asList(b.getFluidInputs())); + List<FluidStack> fluidOutputs = new ArrayList<>(Arrays.asList(b.getFluidOutputs())); + TIntList chances = b.getChances() != null ? new TIntArrayList(b.getChances()) : null; + cellToFluid(itemInputs, fluidInputs, removeIntegratedCircuit, null); + cellToFluid(itemOutputs, fluidOutputs, removeIntegratedCircuit, chances); + itemInputs.removeIf(Objects::isNull); + if (chances == null) { + itemOutputs.removeIf(Objects::isNull); + } + fluidInputs.removeIf(Objects::isNull); + fluidOutputs.removeIf(Objects::isNull); + b.itemInputs(itemInputs.toArray(new ItemStack[0])); + b.itemOutputs(itemOutputs.toArray(new ItemStack[0]), chances != null ? chances.toArray() : null); + b.fluidInputs(fluidInputs.toArray(new FluidStack[0])); + b.fluidOutputs(fluidOutputs.toArray(new FluidStack[0])); + return b; + } + + private static void cellToFluid(List<ItemStack> items, List<FluidStack> fluids, boolean removeIntegratedCircuit, + TIntList chances) { + for (int i = items.size() - 1; i >= 0; i--) { + ItemStack item = items.get(i); + if (GT_Utility.getFluidForFilledItem(item, true) != null || GT_Utility.isCellEmpty(item) + || (removeIntegratedCircuit && GT_Utility.isAnyIntegratedCircuit(item))) { + fluids.add(GT_Utility.convertCellToFluid(item)); + items.remove(i); + if (chances != null) chances.removeAt(i); + } + } + } + + public static List<GT_Recipe> buildOrEmpty(GT_RecipeBuilder builder) { + return builder.build() + .map(Collections::singletonList) + .orElse(Collections.emptyList()); + } + + /** + * Register a recipe map as part of your mod's public API under your modID and your given identifier. + * + * @param identifier map name + * @param recipeMap the map to register + * @param dependencies fully qualified identifier of dependent recipe maps. scheduler will only add recipes to one + * of the dependent recipe maps and this recipe map concurrently, guaranteeing thread safety. + * Currently unused, but you are advised to fill them, so that when The Day (tm) comes we don't + * end up with a bunch of weird concurrency bugs. + */ + public static void registerRecipeMap(String identifier, IRecipeMap recipeMap, RecipeMapDependency... dependencies) { + String modId = Loader.instance() + .activeModContainer() + .getModId(); + if (GregTech.ID.equals(modId)) throw new IllegalStateException( + "do not register recipe map under the name of gregtech! do it in your own preinit!"); + String id = modId + "@" + identifier; + addonRecipeMaps.put(id, recipeMap); + for (Consumer<IRecipeMap> action : delayedActions.get(id)) { + action.accept(recipeMap); + } + } + + /** + * Use this to register recipes for a recipe map in addon not present at compile time. + * <p> + * Do not use this for recipes maps already in {@link GT_RecipeConstants}. None of them will be available via this + * interface! + * + * @param identifier recipe map id + * @param registerAction DO NOT ADD RECIPES TO MAPS OTHER THAN THE ONE PASSED TO YOU. DO NOT DO ANYTHING OTHER THAN + * ADDING RECIPES TO THIS R + */ + public static void registerRecipesFor(String modid, String identifier, Consumer<IRecipeMap> registerAction) { + String id = modid + "@" + identifier; + IRecipeMap map = addonRecipeMaps.get(id); + if (map == null) delayedActions.put(id, registerAction); + else registerAction.accept(map); + } + + public static final class GT_RecipeTemplate { + + private final GT_Recipe template; + private final List<GT_Recipe> derivatives = new ArrayList<>(); + + private GT_RecipeTemplate(GT_Recipe template, boolean includeTemplate) { + this.template = template; + if (includeTemplate) derivatives.add(template); + } + + public GT_Recipe derive() { + GT_Recipe derived = template.copyShallow(); + derivatives.add(derived); + return derived; + } + + public List<GT_Recipe> getAll() { + // fix shallow references + Set<Object> references = Collections.newSetFromMap(new IdentityHashMap<>()); + for (GT_Recipe r : derivatives) { + if (!references.add(r.mInputs)) r.mInputs = r.mInputs.clone(); + if (!references.add(r.mOutputs)) r.mOutputs = r.mOutputs.clone(); + if (!references.add(r.mFluidInputs)) r.mFluidInputs = r.mFluidInputs.clone(); + if (!references.add(r.mFluidOutputs)) r.mFluidOutputs = r.mFluidOutputs.clone(); + } + return derivatives; + } + } + + public static final class RecipeMapDependency { + + private final IRecipeMap obj; + private final String id; + + public RecipeMapDependency(IRecipeMap obj, String id) { + this.obj = obj; + this.id = id; + } + + public static RecipeMapDependency create(String id) { + return new RecipeMapDependency(null, id); + } + + public static RecipeMapDependency create(IRecipeMap obj) { + return new RecipeMapDependency(obj, null); + } + } +} diff --git a/src/main/java/gregtech/api/util/GT_RecipeRegistrator.java b/src/main/java/gregtech/api/util/GT_RecipeRegistrator.java new file mode 100644 index 0000000000..f4490b59b0 --- /dev/null +++ b/src/main/java/gregtech/api/util/GT_RecipeRegistrator.java @@ -0,0 +1,868 @@ +package gregtech.api.util; + +import static gregtech.api.enums.GT_Values.L; +import static gregtech.api.enums.GT_Values.M; +import static gregtech.api.enums.GT_Values.RA; +import static gregtech.api.enums.Materials.Bronze; +import static gregtech.api.enums.Materials.Cobalt; +import static gregtech.api.enums.Materials.DarkSteel; +import static gregtech.api.enums.Materials.Diamond; +import static gregtech.api.enums.Materials.FierySteel; +import static gregtech.api.enums.Materials.Gold; +import static gregtech.api.enums.Materials.Iron; +import static gregtech.api.enums.Materials.IronWood; +import static gregtech.api.enums.Materials.Knightmetal; +import static gregtech.api.enums.Materials.Lead; +import static gregtech.api.enums.Materials.Ruby; +import static gregtech.api.enums.Materials.Sapphire; +import static gregtech.api.enums.Materials.Steel; +import static gregtech.api.enums.Materials.Steeleaf; +import static gregtech.api.enums.Materials.Thaumium; +import static gregtech.api.enums.Materials.Void; +import static gregtech.api.recipe.RecipeMaps.fluidExtractionRecipes; +import static gregtech.api.recipe.RecipeMaps.hammerRecipes; +import static gregtech.api.recipe.RecipeMaps.maceratorRecipes; +import static gregtech.api.recipe.RecipeMaps.wiremillRecipes; +import static gregtech.api.util.GT_RecipeBuilder.SECONDS; +import static gregtech.api.util.GT_RecipeBuilder.TICKS; +import static gregtech.api.util.GT_RecipeConstants.RECYCLE; +import static gregtech.api.util.GT_RecipeConstants.UniversalArcFurnace; +import static gregtech.api.util.GT_Utility.calculateRecipeEU; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.IdentityHashMap; +import java.util.List; +import java.util.Map; + +import net.minecraft.init.Blocks; +import net.minecraft.init.Items; +import net.minecraft.item.ItemBlock; +import net.minecraft.item.ItemStack; +import net.minecraft.item.crafting.CraftingManager; +import net.minecraft.item.crafting.IRecipe; +import net.minecraft.item.crafting.ShapedRecipes; +import net.minecraft.item.crafting.ShapelessRecipes; +import net.minecraftforge.oredict.ShapedOreRecipe; +import net.minecraftforge.oredict.ShapelessOreRecipe; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.SetMultimap; + +import cpw.mods.fml.relauncher.ReflectionHelper; +import gregtech.GT_Mod; +import gregtech.api.GregTech_API; +import gregtech.api.enums.GT_Values; +import gregtech.api.enums.Materials; +import gregtech.api.enums.OrePrefixes; +import gregtech.api.enums.SubTag; +import gregtech.api.enums.TierEU; +import gregtech.api.objects.ItemData; +import gregtech.api.objects.MaterialStack; +import gregtech.api.recipe.RecipeCategories; +import ic2.api.reactor.IReactorComponent; + +/** + * Class for Automatic Recipe registering. + */ +public class GT_RecipeRegistrator { + + /** + * List of Materials, which are used in the Creation of Sticks. All Rod Materials are automatically added to this + * List. + */ + public static final List<Materials> sRodMaterialList = new ArrayList<>(); + + private static final ItemStack sMt1 = new ItemStack(Blocks.dirt, 1, 0), sMt2 = new ItemStack(Blocks.dirt, 1, 0); + private static final String s_H = "h", s_F = "f", s_I = "I", s_P = "P", s_R = "R"; + private static final RecipeShape[] sShapes = new RecipeShape[] { + new RecipeShape(sMt1, null, sMt1, sMt1, sMt1, sMt1, null, sMt1, null), + new RecipeShape(sMt1, null, sMt1, sMt1, null, sMt1, sMt1, sMt1, sMt1), + new RecipeShape(null, sMt1, null, sMt1, sMt1, sMt1, sMt1, null, sMt1), + new RecipeShape(sMt1, sMt1, sMt1, sMt1, null, sMt1, null, null, null), + new RecipeShape(sMt1, null, sMt1, sMt1, sMt1, sMt1, sMt1, sMt1, sMt1), + new RecipeShape(sMt1, sMt1, sMt1, sMt1, null, sMt1, sMt1, null, sMt1), + new RecipeShape(null, null, null, sMt1, null, sMt1, sMt1, null, sMt1), + new RecipeShape(null, sMt1, null, null, sMt1, null, null, sMt2, null), + new RecipeShape(sMt1, sMt1, sMt1, null, sMt2, null, null, sMt2, null), + new RecipeShape(null, sMt1, null, null, sMt2, null, null, sMt2, null), + new RecipeShape(sMt1, sMt1, null, sMt1, sMt2, null, null, sMt2, null), + new RecipeShape(null, sMt1, sMt1, null, sMt2, sMt1, null, sMt2, null), + new RecipeShape(sMt1, sMt1, null, null, sMt2, null, null, sMt2, null), + new RecipeShape(null, sMt1, sMt1, null, sMt2, null, null, sMt2, null), + new RecipeShape(null, sMt1, null, sMt1, null, null, null, sMt1, sMt2), + new RecipeShape(null, sMt1, null, null, null, sMt1, sMt2, sMt1, null), + new RecipeShape(null, sMt1, null, sMt1, null, sMt1, null, null, sMt2), + new RecipeShape(null, sMt1, null, sMt1, null, sMt1, sMt2, null, null), + new RecipeShape(null, sMt2, null, null, sMt1, null, null, sMt1, null), + new RecipeShape(null, sMt2, null, null, sMt2, null, sMt1, sMt1, sMt1), + new RecipeShape(null, sMt2, null, null, sMt2, null, null, sMt1, null), + new RecipeShape(null, sMt2, null, sMt1, sMt2, null, sMt1, sMt1, null), + new RecipeShape(null, sMt2, null, null, sMt2, sMt1, null, sMt1, sMt1), + new RecipeShape(null, sMt2, null, null, sMt2, null, sMt1, sMt1, null), + new RecipeShape(sMt1, null, null, null, sMt2, null, null, null, sMt2), + new RecipeShape(null, null, sMt1, null, sMt2, null, sMt2, null, null), + new RecipeShape(sMt1, null, null, null, sMt2, null, null, null, null), + new RecipeShape(null, null, sMt1, null, sMt2, null, null, null, null), + new RecipeShape(sMt1, sMt2, null, null, null, null, null, null, null), + new RecipeShape(sMt2, sMt1, null, null, null, null, null, null, null), + new RecipeShape(sMt1, null, null, sMt2, null, null, null, null, null), + new RecipeShape(sMt2, null, null, sMt1, null, null, null, null, null), + new RecipeShape(sMt1, sMt1, sMt1, sMt1, sMt1, sMt1, null, sMt2, null), + new RecipeShape(sMt1, sMt1, null, sMt1, sMt1, sMt2, sMt1, sMt1, null), + new RecipeShape(null, sMt1, sMt1, sMt2, sMt1, sMt1, null, sMt1, sMt1), + new RecipeShape(null, sMt2, null, sMt1, sMt1, sMt1, sMt1, sMt1, sMt1), + new RecipeShape(sMt1, sMt1, sMt1, sMt1, sMt2, sMt1, null, sMt2, null), + new RecipeShape(sMt1, sMt1, null, sMt1, sMt2, sMt2, sMt1, sMt1, null), + new RecipeShape(null, sMt1, sMt1, sMt2, sMt2, sMt1, null, sMt1, sMt1), + new RecipeShape(null, sMt2, null, sMt1, sMt2, sMt1, sMt1, sMt1, sMt1), + new RecipeShape(sMt1, null, null, null, sMt1, null, null, null, null), + new RecipeShape(null, sMt1, null, sMt1, null, null, null, null, null), + new RecipeShape(sMt1, sMt1, null, sMt2, null, sMt1, sMt2, null, null), + new RecipeShape(null, sMt1, sMt1, sMt1, null, sMt2, null, null, sMt2) }; + public static final Field SHAPED_ORE_RECIPE_WIDTH = ReflectionHelper.findField(ShapedOreRecipe.class, "width"); + public static final Field SHAPED_ORE_RECIPE_HEIGHT = ReflectionHelper.findField(ShapedOreRecipe.class, "height"); + private static volatile Map<RecipeShape, List<IRecipe>> indexedRecipeListCache; + private static final String[][] sShapesA = new String[][] { null, null, null, + { "Helmet", s_P + s_P + s_P, s_P + s_H + s_P }, + { "ChestPlate", s_P + s_H + s_P, s_P + s_P + s_P, s_P + s_P + s_P }, + { "Pants", s_P + s_P + s_P, s_P + s_H + s_P, s_P + " " + s_P }, { "Boots", s_P + " " + s_P, s_P + s_H + s_P }, + { "Sword", " " + s_P + " ", s_F + s_P + s_H, " " + s_R + " " }, + { "Pickaxe", s_P + s_I + s_I, s_F + s_R + s_H, " " + s_R + " " }, + { "Shovel", s_F + s_P + s_H, " " + s_R + " ", " " + s_R + " " }, + { "Axe", s_P + s_I + s_H, s_P + s_R + " ", s_F + s_R + " " }, + { "Axe", s_P + s_I + s_H, s_P + s_R + " ", s_F + s_R + " " }, + { "Hoe", s_P + s_I + s_H, s_F + s_R + " ", " " + s_R + " " }, + { "Hoe", s_P + s_I + s_H, s_F + s_R + " ", " " + s_R + " " }, + { "Sickle", " " + s_P + " ", s_P + s_F + " ", s_H + s_P + s_R }, + { "Sickle", " " + s_P + " ", s_P + s_F + " ", s_H + s_P + s_R }, + { "Sickle", " " + s_P + " ", s_P + s_F + " ", s_H + s_P + s_R }, + { "Sickle", " " + s_P + " ", s_P + s_F + " ", s_H + s_P + s_R }, + { "Sword", " " + s_R + " ", s_F + s_P + s_H, " " + s_P + " " }, + { "Pickaxe", " " + s_R + " ", s_F + s_R + s_H, s_P + s_I + s_I }, + { "Shovel", " " + s_R + " ", " " + s_R + " ", s_F + s_P + s_H }, + { "Axe", s_F + s_R + " ", s_P + s_R + " ", s_P + s_I + s_H }, + { "Axe", s_F + s_R + " ", s_P + s_R + " ", s_P + s_I + s_H }, + { "Hoe", " " + s_R + " ", s_F + s_R + " ", s_P + s_I + s_H }, + { "Hoe", " " + s_R + " ", s_F + s_R + " ", s_P + s_I + s_H }, + { "Spear", s_P + s_H + " ", s_F + s_R + " ", " " + " " + s_R }, + { "Spear", s_P + s_H + " ", s_F + s_R + " ", " " + " " + s_R }, { "Knive", s_H + s_P, s_R + s_F }, + { "Knive", s_F + s_H, s_P + s_R }, { "Knive", s_F + s_H, s_P + s_R }, { "Knive", s_P + s_F, s_R + s_H }, + { "Knive", s_P + s_F, s_R + s_H }, null, null, null, null, + { "WarAxe", s_P + s_P + s_P, s_P + s_R + s_P, s_F + s_R + s_H }, null, null, null, + { "Shears", s_H + s_P, s_P + s_F }, { "Shears", s_H + s_P, s_P + s_F }, + { "Scythe", s_I + s_P + s_H, s_R + s_F + s_P, s_R + " " + " " }, + { "Scythe", s_H + s_P + s_I, s_P + s_F + s_R, " " + " " + s_R } }; + + static { + // flush the cache on post load finish + GregTech_API.sAfterGTPostload.add(() -> indexedRecipeListCache = null); + } + + public static void registerMaterialRecycling(ItemStack aStack, Materials aMaterial, long aMaterialAmount, + MaterialStack aByproduct) { + if (GT_Utility.isStackInvalid(aStack)) return; + if (aByproduct != null) { + aByproduct = aByproduct.clone(); + aByproduct.mAmount /= aStack.stackSize; + } + GT_OreDictUnificator.addItemData( + GT_Utility.copyAmount(1, aStack), + new ItemData(aMaterial, aMaterialAmount / aStack.stackSize, aByproduct)); + } + + public static void registerMaterialRecycling(ItemStack aStack, ItemData aData) { + if (GT_Utility.isStackInvalid(aStack) || GT_Utility.areStacksEqual(new ItemStack(Items.blaze_rod), aStack) + || aData == null + || !aData.hasValidMaterialData() + || !aData.mMaterial.mMaterial.mAutoGenerateRecycleRecipes + || aData.mMaterial.mAmount <= 0 + || GT_Utility.getFluidForFilledItem(aStack, false) != null + || aData.mMaterial.mMaterial.mSubTags.contains(SubTag.NO_RECIPES)) return; + registerReverseMacerating(GT_Utility.copyAmount(1, aStack), aData, aData.mPrefix == null); + if (!GT_Utility.areStacksEqual(GT_ModHandler.getIC2Item("iridiumOre", 1L), aStack)) { + registerReverseSmelting( + GT_Utility.copyAmount(1, aStack), + aData.mMaterial.mMaterial, + aData.mMaterial.mAmount, + true); + registerReverseFluidSmelting( + GT_Utility.copyAmount(1, aStack), + aData.mMaterial.mMaterial, + aData.mMaterial.mAmount, + aData.getByProduct(0)); + registerReverseArcSmelting(GT_Utility.copyAmount(1, aStack), aData); + } + } + + /** + * @param aStack the stack to be recycled. + * @param aMaterial the Material. + * @param aMaterialAmount the amount of it in Material Units. + */ + public static void registerReverseFluidSmelting(ItemStack aStack, Materials aMaterial, long aMaterialAmount, + MaterialStack aByproduct) { + if (aStack == null || aMaterial == null + || aMaterial.mSmeltInto.mStandardMoltenFluid == null + || !aMaterial.contains(SubTag.SMELTING_TO_FLUID) + || (L * aMaterialAmount) / (M * aStack.stackSize) <= 0) return; + + ItemStack recipeOutput = aByproduct == null ? null + : aByproduct.mMaterial.contains(SubTag.NO_SMELTING) || !aByproduct.mMaterial.contains(SubTag.METAL) + ? aByproduct.mMaterial.contains(SubTag.FLAMMABLE) + ? GT_OreDictUnificator.getDust(Materials.Ash, aByproduct.mAmount / 2) + : aByproduct.mMaterial.contains(SubTag.UNBURNABLE) + ? GT_OreDictUnificator.getDustOrIngot(aByproduct.mMaterial.mSmeltInto, aByproduct.mAmount) + : null + : GT_OreDictUnificator.getIngotOrDust(aByproduct.mMaterial.mSmeltInto, aByproduct.mAmount); + + GT_RecipeBuilder builder = RA.stdBuilder() + .itemInputs(GT_Utility.copyAmount(1, aStack)); + if (recipeOutput != null) { + builder.itemOutputs(recipeOutput); + } + builder.fluidOutputs(aMaterial.mSmeltInto.getMolten((L * aMaterialAmount) / (M * aStack.stackSize))) + .duration((int) Math.max(1, (24 * aMaterialAmount) / M)) + .eut(Math.max(8, (int) Math.sqrt(2 * aMaterial.mSmeltInto.mStandardMoltenFluid.getTemperature()))) + .recipeCategory(RecipeCategories.fluidExtractorRecycling) + .addTo(fluidExtractionRecipes); + } + + /** + * @param aStack the stack to be recycled. + * @param aMaterial the Material. + * @param aMaterialAmount the amount of it in Material Units. + * @param aAllowAlloySmelter if it is allowed to be recycled inside the Alloy Smelter. + */ + public static void registerReverseSmelting(ItemStack aStack, Materials aMaterial, long aMaterialAmount, + boolean aAllowAlloySmelter) { + if (aStack == null || aMaterial == null + || aMaterialAmount <= 0 + || aMaterial.contains(SubTag.NO_SMELTING) + || (aMaterialAmount > M && aMaterial.contains(SubTag.METAL)) + || (aMaterial.getProcessingMaterialTierEU() > TierEU.IV)) return; + if (aMaterial == Materials.Naquadah || aMaterial == Materials.NaquadahEnriched) return; + + aMaterialAmount /= aStack.stackSize; + + if (aAllowAlloySmelter) GT_ModHandler.addSmeltingAndAlloySmeltingRecipe( + GT_Utility.copyAmount(1, aStack), + GT_OreDictUnificator.getIngot(aMaterial.mSmeltInto, aMaterialAmount), + false); + else GT_ModHandler.addSmeltingRecipe( + GT_Utility.copyAmount(1, aStack), + GT_OreDictUnificator.getIngot(aMaterial.mSmeltInto, aMaterialAmount)); + } + + public static void registerReverseArcSmelting(ItemStack aStack, Materials aMaterial, long aMaterialAmount, + MaterialStack aByProduct01, MaterialStack aByProduct02, MaterialStack aByProduct03) { + registerReverseArcSmelting( + aStack, + new ItemData( + aMaterial == null ? null : new MaterialStack(aMaterial, aMaterialAmount), + aByProduct01, + aByProduct02, + aByProduct03)); + } + + public static void registerReverseArcSmelting(ItemStack aStack, ItemData aData) { + if (aStack == null || aData == null) return; + aData = new ItemData(aData); + + if (!aData.hasValidMaterialData()) return; + boolean isRecycle = true; + + for (MaterialStack tMaterial : aData.getAllMaterialStacks()) { + if (tMaterial.mMaterial == Materials.Iron || tMaterial.mMaterial == Materials.Copper + || tMaterial.mMaterial == Materials.WroughtIron + || tMaterial.mMaterial == Materials.AnnealedCopper) { + ItemData stackData = GT_OreDictUnificator.getItemData(aStack); + if (stackData != null + && (stackData.mPrefix == OrePrefixes.ingot || stackData.mPrefix == OrePrefixes.dust)) { + // iron ingot/dust -> wrought iron, copper ingot/dust -> annealed copper + isRecycle = false; + } + } + + if (tMaterial.mMaterial.contains(SubTag.UNBURNABLE)) { + tMaterial.mMaterial = tMaterial.mMaterial.mSmeltInto.mArcSmeltInto; + continue; + } + if (tMaterial.mMaterial.contains(SubTag.EXPLOSIVE)) { + tMaterial.mMaterial = Materials.Ash; + tMaterial.mAmount /= 16; + continue; + } + if (tMaterial.mMaterial.contains(SubTag.FLAMMABLE)) { + tMaterial.mMaterial = Materials.Ash; + tMaterial.mAmount /= 8; + continue; + } + if (tMaterial.mMaterial.contains(SubTag.NO_SMELTING)) { + tMaterial.mAmount = 0; + continue; + } + if (tMaterial.mMaterial.contains(SubTag.METAL)) { + if (GT_Mod.gregtechproxy.mArcSmeltIntoAnnealed) { + tMaterial.mMaterial = tMaterial.mMaterial.mSmeltInto.mArcSmeltInto; + } else { + tMaterial.mMaterial = tMaterial.mMaterial.mSmeltInto.mSmeltInto; + } + continue; + } + tMaterial.mAmount = 0; + } + + aData = new ItemData(aData); + if (aData.mByProducts.length > 3) for (MaterialStack tMaterial : aData.getAllMaterialStacks()) { + if (tMaterial.mMaterial == Materials.Ash) tMaterial.mAmount = 0; + } + + aData = new ItemData(aData); + + if (!aData.hasValidMaterialData()) return; + + long tAmount = 0; + for (MaterialStack tMaterial : aData.getAllMaterialStacks()) + tAmount += tMaterial.mAmount * tMaterial.mMaterial.getMass(); + + ArrayList<ItemStack> outputs = new ArrayList<>(); + if (GT_OreDictUnificator.getIngotOrDust(aData.mMaterial) != null) { + outputs.add(GT_OreDictUnificator.getIngotOrDust(aData.mMaterial)); + } + for (int i = 0; i < 8; i++) { + if (GT_OreDictUnificator.getIngotOrDust(aData.getByProduct(i)) != null) { + outputs.add(GT_OreDictUnificator.getIngotOrDust(aData.getByProduct(i))); + } + } + if (!outputs.isEmpty()) { + GT_RecipeBuilder recipeBuilder = GT_Values.RA.stdBuilder(); + recipeBuilder.itemInputs(aStack) + .itemOutputs(outputs.toArray(new ItemStack[0])) + .fluidInputs(Materials.Oxygen.getGas((int) Math.max(16, tAmount / M))) + .duration(((int) Math.max(16, tAmount / M)) * TICKS) + .eut(90) + .metadata(RECYCLE, isRecycle) + .addTo(UniversalArcFurnace); + } + + } + + public static void registerReverseMacerating(ItemStack aStack, Materials aMaterial, long aMaterialAmount, + MaterialStack aByProduct01, MaterialStack aByProduct02, MaterialStack aByProduct03, boolean aAllowHammer) { + registerReverseMacerating( + aStack, + new ItemData( + aMaterial == null ? null : new MaterialStack(aMaterial, aMaterialAmount), + aByProduct01, + aByProduct02, + aByProduct03), + aAllowHammer); + } + + public static void registerReverseMacerating(ItemStack aStack, ItemData aData, boolean aAllowHammer) { + if (aStack == null || aData == null) return; + aData = new ItemData(aData); + + if (!aData.hasValidMaterialData()) return; + + for (MaterialStack tMaterial : aData.getAllMaterialStacks()) + tMaterial.mMaterial = tMaterial.mMaterial.mMacerateInto; + + aData = new ItemData(aData); + + if (!aData.hasValidMaterialData()) return; + + long tAmount = 0; + for (MaterialStack tMaterial : aData.getAllMaterialStacks()) { + tAmount += tMaterial.mAmount * tMaterial.mMaterial.getMass(); + } + + { + ArrayList<ItemStack> outputs = new ArrayList<>(); + if (GT_OreDictUnificator.getDust(aData.mMaterial) != null) { + outputs.add(GT_OreDictUnificator.getDust(aData.mMaterial)); + } + for (int i = 0; i < 3; i++) { + if (GT_OreDictUnificator.getDust(aData.getByProduct(i)) != null) { + outputs.add(GT_OreDictUnificator.getDust(aData.getByProduct(i))); + } + } + if (!outputs.isEmpty()) { + ItemStack[] outputsArray = outputs.toArray(new ItemStack[0]); + GT_RecipeBuilder recipeBuilder = GT_Values.RA.stdBuilder(); + recipeBuilder.itemInputs(aStack) + .itemOutputs(outputsArray) + .duration( + (aData.mMaterial.mMaterial == Materials.Marble ? 1 : (int) Math.max(16, tAmount / M)) * TICKS) + .eut(4) + .recipeCategory(RecipeCategories.maceratorRecycling) + .addTo(maceratorRecipes); + } + } + + if (!aAllowHammer) { + return; + } + + for (MaterialStack tMaterial : aData.getAllMaterialStacks()) { + if (tMaterial.mMaterial.contains(SubTag.CRYSTAL) && !tMaterial.mMaterial.contains(SubTag.METAL) + && tMaterial.mMaterial != Materials.Glass + && GT_OreDictUnificator.getDust(aData.mMaterial) != null) { + GT_Values.RA.stdBuilder() + .itemInputs(GT_Utility.copyAmount(1, aStack)) + .itemOutputs(GT_OreDictUnificator.getDust(aData.mMaterial)) + .duration(10 * SECONDS) + .eut(TierEU.RECIPE_LV) + .recipeCategory(RecipeCategories.forgeHammerRecycling) + .addTo(hammerRecipes); + break; + } + } + + } + + /** + * Place Materials which you want to replace in Non-GT-Recipes here (warning HUGHE impact on loading times!) + */ + private static final Materials[] VANILLA_MATS = { Cobalt, Gold, Iron, Lead, FierySteel, Void, Bronze, Diamond, Ruby, + Sapphire, Steel, IronWood, Steeleaf, Knightmetal, Thaumium, DarkSteel, }; + + /** + * You give this Function a Material and it will scan almost everything for adding recycling Recipes and replacing + * Ingots, Gems etc. + * + * @param aMats Materials, for example an Ingot or a Gem. + * @param aPlate the Plate referenced to aMat + * @param aRecipeReplacing allows to replace the Recipe with a Plate variant + */ + public static synchronized void registerUsagesForMaterials(String aPlate, boolean aRecipeReplacing, + ItemStack... aMats) { + for (ItemStack aMat : aMats) { + aMat = GT_Utility.copyOrNull(aMat); + + if (aMat == null) continue; + + ItemData aItemData = GT_OreDictUnificator.getItemData(aMat); + if (aItemData == null || aItemData.mPrefix != OrePrefixes.ingot) aPlate = null; + if (aPlate != null && GT_OreDictUnificator.getFirstOre(aPlate, 1) == null) aPlate = null; + + sMt1.func_150996_a(aMat.getItem()); + sMt1.stackSize = 1; + Items.feather.setDamage(sMt1, Items.feather.getDamage(aMat)); + + sMt2.func_150996_a(new ItemStack(Blocks.dirt).getItem()); + sMt2.stackSize = 1; + Items.feather.setDamage(sMt2, 0); + + if (aItemData != null && aItemData.hasValidPrefixMaterialData()) { + for (RecipeShape tRecipe : sShapes) { + for (ItemStack tCrafted : GT_ModHandler.getRecipeOutputsBuffered(tRecipe.shape)) { + GT_OreDictUnificator.addItemData( + tCrafted, + new ItemData(aItemData.mMaterial.mMaterial, aItemData.mMaterial.mAmount * tRecipe.amount1)); + // + // GT_Log.out.println("###################################################################################"); + // GT_Log.out.println("registerUsagesForMaterials used aPlate: "+aPlate); + // GT_Log.out.println("registerUsagesForMaterials used aPlate: + // "+aMat.getUnlocalizedName()); + // GT_Log.out.println("registerUsagesForMaterials used aPlate: + // "+aMat.getDisplayName()); + // + // GT_Log.out.println("###################################################################################"); + } + } + } + registerStickStuff(aPlate, aItemData, aRecipeReplacing); + } + } + + private static List<IRecipe> getRecipeList(RecipeShape shape) { + boolean force = !GregTech_API.sPostloadStarted || GregTech_API.sPostloadFinished; + if (force || indexedRecipeListCache == null) { + synchronized (GT_RecipeRegistrator.class) { + if (indexedRecipeListCache == null || force) { + indexedRecipeListCache = createIndexedRecipeListCache(); + } + } + } + return indexedRecipeListCache.get(shape); + } + + private static Map<RecipeShape, List<IRecipe>> createIndexedRecipeListCache() { + Map<RecipeShape, List<IRecipe>> result = new IdentityHashMap<>(); + ArrayList<IRecipe> allRecipeList = (ArrayList<IRecipe>) CraftingManager.getInstance() + .getRecipeList(); + // filter using the empty slots in the shape. + // if the empty slots doesn't match, the recipe will definitely fail + SetMultimap<List<Integer>, RecipeShape> filter = HashMultimap.create(); + for (RecipeShape shape : sShapes) { + for (List<Integer> list : shape.getEmptySlotsAllVariants()) { + filter.put(list, shape); + } + } + List<Integer> buffer = new ArrayList<>(9); + for (IRecipe tRecipe : allRecipeList) { + if (tRecipe instanceof ShapelessRecipes || tRecipe instanceof ShapelessOreRecipe) { + // we don't target shapeless recipes + continue; + } + buffer.clear(); + ItemStack tStack = tRecipe.getRecipeOutput(); + if (GT_Utility.isStackValid(tStack) && tStack.getMaxStackSize() == 1 + && tStack.getMaxDamage() > 0 + && !(tStack.getItem() instanceof ItemBlock) + && !(tStack.getItem() instanceof IReactorComponent) + && !GT_ModHandler.isElectricItem(tStack) + && !GT_Utility.isStackInList(tStack, GT_ModHandler.sNonReplaceableItems)) { + if (tRecipe instanceof ShapedOreRecipe tShapedRecipe) { + if (checkRecipeShape( + buffer, + tShapedRecipe.getInput(), + getRecipeWidth(tShapedRecipe), + getRecipeHeight(tShapedRecipe))) { + for (RecipeShape s : filter.get(buffer)) { + result.computeIfAbsent(s, k -> new ArrayList<>()) + .add(tRecipe); + } + } + } else if (tRecipe instanceof ShapedRecipes tShapedRecipe) { + if (checkRecipeShape( + buffer, + tShapedRecipe.recipeItems, + getRecipeWidth(tShapedRecipe), + getRecipeHeight(tShapedRecipe))) { + for (RecipeShape s : filter.get(buffer)) { + result.computeIfAbsent(s, k -> new ArrayList<>()) + .add(tRecipe); + } + } + } else { + for (RecipeShape s : sShapes) { + // unknown recipe type. cannot determine empty slots. we choose to add to the recipe list for + // all shapes + result.computeIfAbsent(s, k -> new ArrayList<>()) + .add(tRecipe); + } + } + } + } + return result; + } + + private static boolean checkRecipeShape(List<Integer> emptySlotIndexesBuffer, Object[] input, int tRecipeWidth, + int tRecipeHeight) { + for (int y = 0; y < 3; y++) { + for (int x = 0; x < 3; x++) { + if (x >= tRecipeWidth || y >= tRecipeHeight) { + emptySlotIndexesBuffer.add(x + y * 3); + continue; + } + Object tObject = input[x + y * tRecipeWidth]; + if (tObject == null) { + emptySlotIndexesBuffer.add(x + y * 3); + continue; + } + if (tObject instanceof ItemStack + && (((ItemStack) tObject).getItem() == null || ((ItemStack) tObject).getMaxStackSize() < 2 + || ((ItemStack) tObject).getMaxDamage() > 0 + || ((ItemStack) tObject).getItem() instanceof ItemBlock)) { + return false; + } + if (tObject instanceof List && ((List<?>) tObject).isEmpty()) { + return false; + } + } + } + return true; + } + + private static synchronized void registerStickStuff(String aPlate, ItemData aItemData, boolean aRecipeReplacing) { + ItemStack tStack; + for (Materials tMaterial : sRodMaterialList) { + ItemStack tMt2 = GT_OreDictUnificator.get(OrePrefixes.stick, tMaterial, 1); + if (tMt2 != null) { + sMt2.func_150996_a(tMt2.getItem()); + sMt2.stackSize = 1; + Items.feather.setDamage(sMt2, Items.feather.getDamage(tMt2)); + + for (int i = 0; i < sShapes.length; i++) { + RecipeShape tRecipe = sShapes[i]; + + for (ItemStack tCrafted : GT_ModHandler + .getRecipeOutputs(getRecipeList(tRecipe), true, tRecipe.shape)) { + if (aItemData != null && aItemData.hasValidPrefixMaterialData()) + GT_OreDictUnificator.addItemData( + tCrafted, + new ItemData( + aItemData.mMaterial.mMaterial, + aItemData.mMaterial.mAmount * tRecipe.amount1, + new MaterialStack(tMaterial, OrePrefixes.stick.mMaterialAmount * tRecipe.amount2))); + + if (aRecipeReplacing && aPlate != null && sShapesA[i] != null && sShapesA[i].length > 1) { + assert aItemData != null; + + if (null != (tStack = GT_ModHandler.removeRecipe(tRecipe.shape))) { + switch (sShapesA[i].length) { + case 2 -> GT_ModHandler.addCraftingRecipe( + tStack, + GT_ModHandler.RecipeBits.BUFFERED, + new Object[] { sShapesA[i][1], s_P.charAt(0), aPlate, s_R.charAt(0), + OrePrefixes.stick.get(tMaterial), s_I.charAt(0), aItemData }); + case 3 -> GT_ModHandler.addCraftingRecipe( + tStack, + GT_ModHandler.RecipeBits.BUFFERED, + new Object[] { sShapesA[i][1], sShapesA[i][2], s_P.charAt(0), aPlate, + s_R.charAt(0), OrePrefixes.stick.get(tMaterial), s_I.charAt(0), + aItemData }); + default -> GT_ModHandler.addCraftingRecipe( + tStack, + GT_ModHandler.RecipeBits.BUFFERED, + new Object[] { sShapesA[i][1], sShapesA[i][2], sShapesA[i][3], s_P.charAt(0), + aPlate, s_R.charAt(0), OrePrefixes.stick.get(tMaterial), s_I.charAt(0), + aItemData }); + } + } + } + } + } + } + } + } + + /** + * Registers wiremill recipes for given material using integrated circuits. + * + * @param aMaterial material to register + * @param baseDuration base duration ticks for ingot -> 1x wire recipe + * @param aEUt EU/t for recipe If you provide a proper EU tier for recipe processing then aEUt will be + * overriden with it. + */ + public static void registerWiremillRecipes(Materials aMaterial, int baseDuration, int aEUt) { + registerWiremillRecipes( + aMaterial, + baseDuration, + calculateRecipeEU(aMaterial, aEUt), + OrePrefixes.ingot, + OrePrefixes.stick, + 2); + } + + /** + * Registers wiremill recipes for given material using integrated circuits. + * + * @param aMaterial material to register + * @param baseDuration base duration ticks for ingot -> 1x wire recipe + * @param aEUt EU/t for recipe + * @param prefix1 prefix corresponds to ingot + * @param prefix2 prefix corresponds to stick + * @param multiplier amount of wires created from 1 ingot + */ + public static void registerWiremillRecipes(Materials aMaterial, int baseDuration, int aEUt, OrePrefixes prefix1, + OrePrefixes prefix2, int multiplier) { + if (GT_OreDictUnificator.get(prefix1, aMaterial, 1L) != null + && GT_OreDictUnificator.get(OrePrefixes.wireGt01, aMaterial, 1L) != null) { + GT_Values.RA.stdBuilder() + .itemInputs(GT_OreDictUnificator.get(prefix1, aMaterial, 1L), GT_Utility.getIntegratedCircuit(1)) + .itemOutputs(GT_OreDictUnificator.get(OrePrefixes.wireGt01, aMaterial, multiplier)) + .duration(baseDuration * TICKS) + .eut(aEUt) + .addTo(wiremillRecipes); + GT_Values.RA.stdBuilder() + .itemInputs( + GT_OreDictUnificator.get(prefix1, aMaterial, 2L / multiplier), + GT_Utility.getIntegratedCircuit(2)) + .itemOutputs(GT_OreDictUnificator.get(OrePrefixes.wireGt02, aMaterial, 1L)) + .duration(((int) (baseDuration * 1.5f)) * TICKS) + .eut(aEUt) + .addTo(wiremillRecipes); + GT_Values.RA.stdBuilder() + .itemInputs( + GT_OreDictUnificator.get(prefix1, aMaterial, 4L / multiplier), + GT_Utility.getIntegratedCircuit(4)) + .itemOutputs(GT_OreDictUnificator.get(OrePrefixes.wireGt04, aMaterial, 1L)) + .duration(baseDuration * 2 * TICKS) + .eut(aEUt) + .addTo(wiremillRecipes); + GT_Values.RA.stdBuilder() + .itemInputs( + GT_OreDictUnificator.get(prefix1, aMaterial, 8L / multiplier), + GT_Utility.getIntegratedCircuit(8)) + .itemOutputs(GT_OreDictUnificator.get(OrePrefixes.wireGt08, aMaterial, 1L)) + .duration(((int) (baseDuration * 2.5f)) * TICKS) + .eut(aEUt) + .addTo(wiremillRecipes); + GT_Values.RA.stdBuilder() + .itemInputs( + GT_OreDictUnificator.get(prefix1, aMaterial, 12L / multiplier), + GT_Utility.getIntegratedCircuit(12)) + .itemOutputs(GT_OreDictUnificator.get(OrePrefixes.wireGt12, aMaterial, 1L)) + .duration(baseDuration * 3 * TICKS) + .eut(aEUt) + .addTo(wiremillRecipes); + GT_Values.RA.stdBuilder() + .itemInputs( + GT_OreDictUnificator.get(prefix1, aMaterial, 16L / multiplier), + GT_Utility.getIntegratedCircuit(16)) + .itemOutputs(GT_OreDictUnificator.get(OrePrefixes.wireGt16, aMaterial, 1L)) + .duration(((int) (baseDuration * 3.5f)) * TICKS) + .eut(aEUt) + .addTo(wiremillRecipes); + } + + if (GT_OreDictUnificator.get(prefix2, aMaterial, 1L) != null + && GT_OreDictUnificator.get(OrePrefixes.wireGt01, aMaterial, 1L) != null) { + GT_Values.RA.stdBuilder() + .itemInputs(GT_OreDictUnificator.get(prefix2, aMaterial, 1L), GT_Utility.getIntegratedCircuit(1)) + .itemOutputs(GT_OreDictUnificator.get(OrePrefixes.wireGt01, aMaterial, 2L / multiplier)) + .duration(((int) (baseDuration * 0.5f)) * TICKS) + .eut(aEUt) + .addTo(wiremillRecipes); + GT_Values.RA.stdBuilder() + .itemInputs( + GT_OreDictUnificator.get(prefix2, aMaterial, 4L / multiplier), + GT_Utility.getIntegratedCircuit(2)) + .itemOutputs(GT_OreDictUnificator.get(OrePrefixes.wireGt02, aMaterial, 1L)) + .duration(baseDuration * TICKS) + .eut(aEUt) + .addTo(wiremillRecipes); + GT_Values.RA.stdBuilder() + .itemInputs( + GT_OreDictUnificator.get(prefix2, aMaterial, 8L / multiplier), + GT_Utility.getIntegratedCircuit(4)) + .itemOutputs(GT_OreDictUnificator.get(OrePrefixes.wireGt04, aMaterial, 1L)) + .duration(((int) (baseDuration * 1.5f)) * TICKS) + .eut(aEUt) + .addTo(wiremillRecipes); + GT_Values.RA.stdBuilder() + .itemInputs( + GT_OreDictUnificator.get(prefix2, aMaterial, 16L / multiplier), + GT_Utility.getIntegratedCircuit(8)) + .itemOutputs(GT_OreDictUnificator.get(OrePrefixes.wireGt08, aMaterial, 1L)) + .duration(baseDuration * 2 * TICKS) + .eut(aEUt) + .addTo(wiremillRecipes); + GT_Values.RA.stdBuilder() + .itemInputs( + GT_OreDictUnificator.get(prefix2, aMaterial, 24L / multiplier), + GT_Utility.getIntegratedCircuit(12)) + .itemOutputs(GT_OreDictUnificator.get(OrePrefixes.wireGt12, aMaterial, 1L)) + .duration(((int) (baseDuration * 2.5f)) * TICKS) + .eut(aEUt) + .addTo(wiremillRecipes); + GT_Values.RA.stdBuilder() + .itemInputs( + GT_OreDictUnificator.get(prefix2, aMaterial, 32L / multiplier), + GT_Utility.getIntegratedCircuit(16)) + .itemOutputs(GT_OreDictUnificator.get(OrePrefixes.wireGt16, aMaterial, 1L)) + .duration(baseDuration * 3 * TICKS) + .eut(aEUt) + .addTo(wiremillRecipes); + } + if (GT_OreDictUnificator.get(prefix1, aMaterial, 1L) != null + && GT_OreDictUnificator.get(OrePrefixes.wireFine, aMaterial, 1L) != null) { + GT_Values.RA.stdBuilder() + .itemInputs(GT_OreDictUnificator.get(prefix1, aMaterial, 1L), GT_Utility.getIntegratedCircuit(3)) + .itemOutputs(GT_OreDictUnificator.get(OrePrefixes.wireFine, aMaterial, 4L * multiplier)) + .duration(baseDuration * TICKS) + .eut(aEUt) + .addTo(wiremillRecipes); + } + if (GT_OreDictUnificator.get(prefix2, aMaterial, 1L) != null + && GT_OreDictUnificator.get(OrePrefixes.wireFine, aMaterial, 1L) != null) { + GT_Values.RA.stdBuilder() + .itemInputs(GT_OreDictUnificator.get(prefix2, aMaterial, 1L), GT_Utility.getIntegratedCircuit(3)) + .itemOutputs(GT_OreDictUnificator.get(OrePrefixes.wireFine, aMaterial, 2L * multiplier)) + .duration(((int) (baseDuration * 0.5f)) * TICKS) + .eut(aEUt) + .addTo(wiremillRecipes); + } + } + + public static boolean hasVanillaRecipes(Materials materials) { + return Arrays.stream(VANILLA_MATS) + .anyMatch(mat -> mat == materials); + } + + private static int getRecipeWidth(ShapedOreRecipe r) { + try { + return (int) SHAPED_ORE_RECIPE_WIDTH.get(r); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + private static int getRecipeHeight(ShapedOreRecipe r) { + try { + return (int) SHAPED_ORE_RECIPE_HEIGHT.get(r); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + private static int getRecipeHeight(ShapedRecipes r) { + return r.recipeHeight; + } + + private static int getRecipeWidth(ShapedRecipes r) { + return r.recipeWidth; + } + + private static class RecipeShape { + + private final ItemStack[] shape; + private int amount1; + private int amount2; + + public RecipeShape(ItemStack... shape) { + this.shape = shape; + + for (ItemStack stack : shape) { + if (stack == sMt1) this.amount1++; + if (stack == sMt2) this.amount2++; + } + } + + public List<List<Integer>> getEmptySlotsAllVariants() { + // "shake" the grid in 8 direction and see if the recipe shape is still valid + // also include the "no movement" case + ImmutableList.Builder<List<Integer>> b = ImmutableList.builder(); + for (int i = -1; i < 2; i++) { + if (i != 0 && !isColClear(i + 1)) continue; + for (int j = -1; j < 2; j++) { + if (j != 0 && !isRowClear(j + 1)) continue; + b.add(getEmptySlots(i, j)); + } + } + return b.build(); + } + + private boolean isRowClear(int row) { + for (int i = 0; i < 3; i++) { + if (shape[i + row * 3] != null) return false; + } + return true; + } + + private boolean isColClear(int col) { + for (int i = 0; i < 3; i++) { + if (shape[col + i * 3] != null) return false; + } + return true; + } + + private List<Integer> getEmptySlots(int offsetX, int offsetY) { + ImmutableList.Builder<Integer> b = ImmutableList.builder(); + for (int i = 0; i < shape.length; i++) { + int mappedIndex = i - offsetX - offsetY * 3; + // empty slot if it either + // 1) map to a slot outside the original shape + // 2) map to an empty slot in original shape + if (mappedIndex < 0 || mappedIndex > 8 || shape[mappedIndex] == null) b.add(i); + } + return b.build(); + } + } +} diff --git a/src/main/java/gregtech/api/util/GT_RenderingWorld.java b/src/main/java/gregtech/api/util/GT_RenderingWorld.java new file mode 100644 index 0000000000..7220b921a5 --- /dev/null +++ b/src/main/java/gregtech/api/util/GT_RenderingWorld.java @@ -0,0 +1,195 @@ +package gregtech.api.util; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +import net.minecraft.block.Block; +import net.minecraft.client.Minecraft; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.world.ChunkCoordIntPair; +import net.minecraft.world.ChunkPosition; +import net.minecraft.world.IBlockAccess; +import net.minecraft.world.biome.BiomeGenBase; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.common.util.ForgeDirection; +import net.minecraftforge.event.world.ChunkEvent; +import net.minecraftforge.event.world.WorldEvent; + +import cpw.mods.fml.common.FMLCommonHandler; +import cpw.mods.fml.common.eventhandler.EventPriority; +import cpw.mods.fml.common.eventhandler.SubscribeEvent; +import cpw.mods.fml.common.gameevent.TickEvent; + +/** + * Provide a fake IBlockAccess to support CTM. Facade are supposed to set these when they are placed/received by client. + */ +public class GT_RenderingWorld implements IBlockAccess { + + private static final GT_RenderingWorld INSTANCE = new GT_RenderingWorld(); + /* + * I do not think this map would ever grow too huge, so I won't go too overcomplicated on this one + */ + private final Map<ChunkPosition, BlockInfo> infos = new HashMap<>(); + private final Map<ChunkCoordIntPair, Set<ChunkPosition>> index = new HashMap<>(); + private IBlockAccess mWorld = Minecraft.getMinecraft().theWorld; + + private GT_RenderingWorld() { + new FMLEventHandler(); + new ForgeEventHandler(); + } + + public static GT_RenderingWorld getInstance() { + return INSTANCE; + } + + public static GT_RenderingWorld getInstance(IBlockAccess aWorld) { + if (aWorld == INSTANCE) return INSTANCE; + if (aWorld == null) INSTANCE.mWorld = Minecraft.getMinecraft().theWorld; + else INSTANCE.mWorld = aWorld; + return INSTANCE; + } + + private void setWorld(IBlockAccess aWorld) { + if (aWorld == null) mWorld = Minecraft.getMinecraft().theWorld; + else mWorld = aWorld; + } + + public void register(int x, int y, int z, Block block, int meta) { + ChunkPosition key = new ChunkPosition(x, y, z); + infos.put(key, new BlockInfo(block, meta)); + index.computeIfAbsent(new ChunkCoordIntPair(x >> 4, z >> 4), p -> new HashSet<>()) + .add(key); + } + + public void unregister(int x, int y, int z, Block block, int meta) { + ChunkPosition key = new ChunkPosition(x, y, z); + if (infos.remove(key, new BlockInfo(block, meta))) { + ChunkCoordIntPair chunkKey = new ChunkCoordIntPair(x >> 4, z >> 4); + Set<ChunkPosition> set = index.get(chunkKey); + set.remove(key); + if (set.isEmpty()) index.remove(chunkKey); + } + } + + @Override + public Block getBlock(int p_147439_1_, int p_147439_2_, int p_147439_3_) { + BlockInfo blockInfo = infos.get(new ChunkPosition(p_147439_1_, p_147439_2_, p_147439_3_)); + return blockInfo != null ? blockInfo.block : mWorld.getBlock(p_147439_1_, p_147439_2_, p_147439_3_); + } + + @Override + public TileEntity getTileEntity(int p_147438_1_, int p_147438_2_, int p_147438_3_) { + return mWorld.getTileEntity(p_147438_1_, p_147438_2_, p_147438_3_); + } + + @Override + public int getLightBrightnessForSkyBlocks(int p_72802_1_, int p_72802_2_, int p_72802_3_, int p_72802_4_) { + return mWorld.getLightBrightnessForSkyBlocks(p_72802_1_, p_72802_2_, p_72802_3_, p_72802_4_); + } + + @Override + public int getBlockMetadata(int p_72805_1_, int p_72805_2_, int p_72805_3_) { + BlockInfo blockInfo = infos.get(new ChunkPosition(p_72805_1_, p_72805_2_, p_72805_3_)); + return blockInfo != null ? blockInfo.meta : mWorld.getBlockMetadata(p_72805_1_, p_72805_2_, p_72805_3_); + } + + @Override + public int isBlockProvidingPowerTo(int p_72879_1_, int p_72879_2_, int p_72879_3_, int p_72879_4_) { + return mWorld.isBlockProvidingPowerTo(p_72879_1_, p_72879_2_, p_72879_3_, p_72879_4_); + } + + @Override + public boolean isAirBlock(int p_147437_1_, int p_147437_2_, int p_147437_3_) { + return getBlock(p_147437_1_, p_147437_2_, p_147437_3_).isAir(mWorld, p_147437_1_, p_147437_2_, p_147437_3_); + } + + @Override + public BiomeGenBase getBiomeGenForCoords(int p_72807_1_, int p_72807_2_) { + return mWorld.getBiomeGenForCoords(p_72807_1_, p_72807_2_); + } + + @Override + public int getHeight() { + return mWorld.getHeight(); + } + + @Override + public boolean extendedLevelsInChunkCache() { + return mWorld.extendedLevelsInChunkCache(); + } + + @Override + public boolean isSideSolid(int x, int y, int z, ForgeDirection side, boolean _default) { + return getBlock(x, y, z).isSideSolid(this, x, y, z, side); + } + + public class FMLEventHandler { + + public FMLEventHandler() { + FMLCommonHandler.instance() + .bus() + .register(this); + } + + @SubscribeEvent(priority = EventPriority.HIGHEST) + public void onRenderTickStart(TickEvent.RenderTickEvent e) { + if (e.phase == TickEvent.Phase.START) mWorld = Minecraft.getMinecraft().theWorld; + } + } + + public class ForgeEventHandler { + + private ForgeEventHandler() { + MinecraftForge.EVENT_BUS.register(this); + } + + @SubscribeEvent + public void onChunkUnloaded(ChunkEvent.Unload e) { + if (!e.world.isRemote) return; + Set<ChunkPosition> set = index.remove( + e.getChunk() + .getChunkCoordIntPair()); + if (set != null) infos.keySet() + .removeAll(set); + } + + @SubscribeEvent + public void onWorldUnloaded(WorldEvent.Unload e) { + if (!e.world.isRemote) return; + infos.clear(); + index.clear(); + } + } + + private static class BlockInfo { + + private final Block block; + private final int meta; + + public BlockInfo(Block block, int meta) { + this.block = block; + this.meta = meta; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + BlockInfo blockInfo = (BlockInfo) o; + + if (meta != blockInfo.meta) return false; + return Objects.equals(block, blockInfo.block); + } + + @Override + public int hashCode() { + int result = block != null ? block.hashCode() : 0; + result = 31 * result + meta; + return result; + } + } +} diff --git a/src/main/java/gregtech/api/util/GT_Shaped_Recipe.java b/src/main/java/gregtech/api/util/GT_Shaped_Recipe.java new file mode 100644 index 0000000000..95a1a0bb66 --- /dev/null +++ b/src/main/java/gregtech/api/util/GT_Shaped_Recipe.java @@ -0,0 +1,100 @@ +package gregtech.api.util; + +import net.minecraft.enchantment.Enchantment; +import net.minecraft.enchantment.EnchantmentHelper; +import net.minecraft.inventory.InventoryCrafting; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.world.World; +import net.minecraftforge.oredict.ShapedOreRecipe; + +import gregtech.api.interfaces.internal.IGT_CraftingRecipe; + +public class GT_Shaped_Recipe extends ShapedOreRecipe implements IGT_CraftingRecipe { + + public final boolean mRemovableByGT, mKeepingNBT; + private final Enchantment[] mEnchantmentsAdded; + private final int[] mEnchantmentLevelsAdded; + + public GT_Shaped_Recipe(ItemStack aResult, boolean aDismantleAble, boolean aRemovableByGT, boolean aKeepingNBT, + Enchantment[] aEnchantmentsAdded, int[] aEnchantmentLevelsAdded, Object... aRecipe) { + super(aResult, aRecipe); + mEnchantmentsAdded = aEnchantmentsAdded; + mEnchantmentLevelsAdded = aEnchantmentLevelsAdded; + mRemovableByGT = aRemovableByGT; + mKeepingNBT = aKeepingNBT; + } + + @Override + public boolean matches(InventoryCrafting aGrid, World aWorld) { + if (mKeepingNBT) { + ItemStack tStack = null; + for (int i = 0; i < aGrid.getSizeInventory(); i++) { + if (aGrid.getStackInSlot(i) != null) { + if (tStack != null) { + if ((tStack.hasTagCompound() != aGrid.getStackInSlot(i) + .hasTagCompound()) || (tStack.hasTagCompound() + && !tStack.getTagCompound() + .equals( + aGrid.getStackInSlot(i) + .getTagCompound()))) + return false; + } + tStack = aGrid.getStackInSlot(i); + } + } + } + return super.matches(aGrid, aWorld); + } + + @Override + public ItemStack getCraftingResult(InventoryCrafting aGrid) { + ItemStack rStack = super.getCraftingResult(aGrid); + if (rStack != null) { + // Update the Stack + GT_Utility.updateItemStack(rStack); + + // Keeping NBT + if (mKeepingNBT) for (int i = 0; i < aGrid.getSizeInventory(); i++) { + if (aGrid.getStackInSlot(i) != null && aGrid.getStackInSlot(i) + .hasTagCompound()) { + rStack.setTagCompound( + (NBTTagCompound) aGrid.getStackInSlot(i) + .getTagCompound() + .copy()); + break; + } + } + + // Charge Values + if (GT_ModHandler.isElectricItem(rStack)) { + GT_ModHandler.dischargeElectricItem(rStack, Integer.MAX_VALUE, Integer.MAX_VALUE, true, false, true); + int tCharge = 0; + for (int i = 0; i < aGrid.getSizeInventory(); i++) tCharge += GT_ModHandler.dischargeElectricItem( + aGrid.getStackInSlot(i), + Integer.MAX_VALUE, + Integer.MAX_VALUE, + true, + true, + true); + if (tCharge > 0) GT_ModHandler.chargeElectricItem(rStack, tCharge, Integer.MAX_VALUE, true, false); + } + + // Add Enchantments + for (int i = 0; i < mEnchantmentsAdded.length; i++) GT_Utility.ItemNBT.addEnchantment( + rStack, + mEnchantmentsAdded[i], + EnchantmentHelper.getEnchantmentLevel(mEnchantmentsAdded[i].effectId, rStack) + + mEnchantmentLevelsAdded[i]); + + // Update the Stack again + GT_Utility.updateItemStack(rStack); + } + return rStack; + } + + @Override + public boolean isRemovable() { + return mRemovableByGT; + } +} diff --git a/src/main/java/gregtech/api/util/GT_Shapeless_Recipe.java b/src/main/java/gregtech/api/util/GT_Shapeless_Recipe.java new file mode 100644 index 0000000000..582dd7cc10 --- /dev/null +++ b/src/main/java/gregtech/api/util/GT_Shapeless_Recipe.java @@ -0,0 +1,100 @@ +package gregtech.api.util; + +import net.minecraft.enchantment.Enchantment; +import net.minecraft.enchantment.EnchantmentHelper; +import net.minecraft.inventory.InventoryCrafting; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.world.World; +import net.minecraftforge.oredict.ShapelessOreRecipe; + +import gregtech.api.interfaces.internal.IGT_CraftingRecipe; + +public class GT_Shapeless_Recipe extends ShapelessOreRecipe implements IGT_CraftingRecipe { + + public final boolean mRemovableByGT, mKeepingNBT; + private final Enchantment[] mEnchantmentsAdded; + private final int[] mEnchantmentLevelsAdded; + + public GT_Shapeless_Recipe(ItemStack aResult, boolean aDismantleAble, boolean aRemovableByGT, boolean aKeepingNBT, + Enchantment[] aEnchantmentsAdded, int[] aEnchantmentLevelsAdded, Object... aRecipe) { + super(aResult, aRecipe); + mEnchantmentsAdded = aEnchantmentsAdded; + mEnchantmentLevelsAdded = aEnchantmentLevelsAdded; + mRemovableByGT = aRemovableByGT; + mKeepingNBT = aKeepingNBT; + } + + @Override + public boolean matches(InventoryCrafting aGrid, World aWorld) { + if (mKeepingNBT) { + ItemStack tStack = null; + for (int i = 0; i < aGrid.getSizeInventory(); i++) { + if (aGrid.getStackInSlot(i) != null) { + if (tStack != null) { + if ((tStack.hasTagCompound() != aGrid.getStackInSlot(i) + .hasTagCompound()) || (tStack.hasTagCompound() + && !tStack.getTagCompound() + .equals( + aGrid.getStackInSlot(i) + .getTagCompound()))) + return false; + } + tStack = aGrid.getStackInSlot(i); + } + } + } + return super.matches(aGrid, aWorld); + } + + @Override + public ItemStack getCraftingResult(InventoryCrafting aGrid) { + ItemStack rStack = super.getCraftingResult(aGrid); + if (rStack != null) { + // Update the Stack + GT_Utility.updateItemStack(rStack); + + // Keeping NBT + if (mKeepingNBT) for (int i = 0; i < aGrid.getSizeInventory(); i++) { + if (aGrid.getStackInSlot(i) != null && aGrid.getStackInSlot(i) + .hasTagCompound()) { + rStack.setTagCompound( + (NBTTagCompound) aGrid.getStackInSlot(i) + .getTagCompound() + .copy()); + break; + } + } + + // Charge Values + if (GT_ModHandler.isElectricItem(rStack)) { + GT_ModHandler.dischargeElectricItem(rStack, Integer.MAX_VALUE, Integer.MAX_VALUE, true, false, true); + int tCharge = 0; + for (int i = 0; i < aGrid.getSizeInventory(); i++) tCharge += GT_ModHandler.dischargeElectricItem( + aGrid.getStackInSlot(i), + Integer.MAX_VALUE, + Integer.MAX_VALUE, + true, + true, + true); + if (tCharge > 0) GT_ModHandler.chargeElectricItem(rStack, tCharge, Integer.MAX_VALUE, true, false); + } + + // Add Enchantments + for (int i = 0; i < mEnchantmentsAdded.length; i++) GT_Utility.ItemNBT.addEnchantment( + rStack, + mEnchantmentsAdded[i], + EnchantmentHelper.getEnchantmentLevel(mEnchantmentsAdded[i].effectId, rStack) + + mEnchantmentLevelsAdded[i]); + + // Update the Stack again + GT_Utility.updateItemStack(rStack); + } + return rStack; + } + + @Override + public boolean isRemovable() { + return mRemovableByGT; + } +} diff --git a/src/main/java/gregtech/api/util/GT_SpawnEventHandler.java b/src/main/java/gregtech/api/util/GT_SpawnEventHandler.java new file mode 100644 index 0000000000..ebdba1144b --- /dev/null +++ b/src/main/java/gregtech/api/util/GT_SpawnEventHandler.java @@ -0,0 +1,81 @@ +package gregtech.api.util; + +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +import net.minecraft.entity.EnumCreatureType; +import net.minecraft.entity.monster.EntitySlime; +import net.minecraft.tileentity.TileEntity; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.entity.living.LivingSpawnEvent.CheckSpawn; + +import cpw.mods.fml.common.eventhandler.Event; +import cpw.mods.fml.common.eventhandler.SubscribeEvent; +import gregtech.api.enums.GT_Values; +import gregtech.api.metatileentity.BaseMetaTileEntity; +import gregtech.common.tileentities.machines.basic.GT_MetaTileEntity_MonsterRepellent; + +public class GT_SpawnEventHandler { + + public static volatile List<int[]> mobReps = new CopyOnWriteArrayList<>(); + // Future Optimiztation ideas, if this isn't sufficient + // 1: Keep a weakref list of mob repellents so we already have the tile + // 2: Have the tick method update a HashMap of (int[], range) so we don't have to load the tile at all + + public GT_SpawnEventHandler() { + MinecraftForge.EVENT_BUS.register(this); + } + + // Range of a powered repellent + public static int getPoweredRepellentRange(int aTier) { + return 16 + (48 * aTier); + } + + // Range of an unpowered repellent + public static int getUnpoweredRepellentRange(int aTier) { + return 4 + (12 * aTier); + } + + @SubscribeEvent + public void denyMobSpawn(CheckSpawn event) { + if (event.getResult() == Event.Result.DENY) return; + + if (event.entityLiving instanceof EntitySlime slime && !slime.hasCustomNameTag() + && event.getResult() == Event.Result.ALLOW) { + event.setResult(Event.Result.DEFAULT); + } + + if (event.getResult() == Event.Result.ALLOW) { + return; + } + + if (event.entityLiving.isCreatureType(EnumCreatureType.monster, false)) { + final double maxRangeCheck = Math.pow(getPoweredRepellentRange(GT_Values.V.length - 1), 2); + for (int[] rep : mobReps) { + if (rep[3] == event.entity.worldObj.provider.dimensionId) { + // If the chunk isn't loaded, we ignore this Repellent + if (!event.entity.worldObj.blockExists(rep[0], rep[1], rep[2])) continue; + final double dx = rep[0] + 0.5F - event.entity.posX; + final double dy = rep[1] + 0.5F - event.entity.posY; + final double dz = rep[2] + 0.5F - event.entity.posZ; + + final double check = (dx * dx + dz * dz + dy * dy); + // Fail early if outside of max range + if (check > maxRangeCheck) continue; + + final TileEntity tTile = event.entity.worldObj.getTileEntity(rep[0], rep[1], rep[2]); + if (tTile instanceof BaseMetaTileEntity metaTile + && metaTile.getMetaTileEntity() instanceof GT_MetaTileEntity_MonsterRepellent repellent + && check <= Math.pow(repellent.mRange, 2)) { + if (event.entityLiving instanceof EntitySlime slime) { + slime.setCustomNameTag("DoNotSpawnSlimes"); + } + event.setResult(Event.Result.DENY); + // We're already DENYing it. No reason to keep checking + return; + } + } + } + } + } +} diff --git a/src/main/java/gregtech/api/util/GT_StreamUtil.java b/src/main/java/gregtech/api/util/GT_StreamUtil.java new file mode 100644 index 0000000000..c29e611c4e --- /dev/null +++ b/src/main/java/gregtech/api/util/GT_StreamUtil.java @@ -0,0 +1,44 @@ +package gregtech.api.util; + +import java.util.Arrays; +import java.util.function.Supplier; +import java.util.stream.Stream; + +import javax.annotation.Nullable; +import javax.annotation.ParametersAreNonnullByDefault; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +public final class GT_StreamUtil { + + /** + * Backport of {@link Stream#ofNullable}. + */ + public static <T> Stream<T> ofNullable(@Nullable T value) { + return value == null ? Stream.empty() : Stream.of(value); + } + + /** + * Returns a sequential ordered {@code Stream} whose elements are the specified values, + * if {@code condition} is true, otherwise returns an empty {@code Stream}. + * + * @param <T> the type of stream elements + * @param values the elements of the new stream + * @return the new stream + */ + public static <T> Stream<T> ofConditional(boolean condition, T[] values) { + return condition ? Arrays.stream(values) : Stream.empty(); + } + + /** + * Returns a sequential {@code Stream} containing a single element, which will be lazily evaluated from supplier. + * + * @param <T> the type of stream elements + * @param supplier the supplier for single stream element + * @return the new stream + */ + public static <T> Stream<T> ofSupplier(Supplier<T> supplier) { + return Stream.generate(supplier) + .limit(1); + } +} diff --git a/src/main/java/gregtech/api/util/GT_StructureUtility.java b/src/main/java/gregtech/api/util/GT_StructureUtility.java new file mode 100644 index 0000000000..d3c4c10a0d --- /dev/null +++ b/src/main/java/gregtech/api/util/GT_StructureUtility.java @@ -0,0 +1,512 @@ +package gregtech.api.util; + +import static com.gtnewhorizon.structurelib.structure.IStructureElement.PlaceResult.ACCEPT; +import static com.gtnewhorizon.structurelib.structure.IStructureElement.PlaceResult.ACCEPT_STOP; +import static com.gtnewhorizon.structurelib.structure.IStructureElement.PlaceResult.REJECT; +import static com.gtnewhorizon.structurelib.structure.IStructureElement.PlaceResult.SKIP; +import static com.gtnewhorizon.structurelib.util.ItemStackPredicate.NBTMode.EXACT; + +import java.util.Arrays; +import java.util.List; +import java.util.function.BiConsumer; +import java.util.function.BiPredicate; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.ToIntFunction; + +import javax.annotation.Nonnull; + +import net.minecraft.block.Block; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.init.Items; +import net.minecraft.item.Item; +import net.minecraft.item.ItemBlock; +import net.minecraft.item.ItemStack; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.ChatComponentTranslation; +import net.minecraft.util.IChatComponent; +import net.minecraft.util.IIcon; +import net.minecraft.world.World; + +import com.gtnewhorizon.structurelib.StructureLibAPI; +import com.gtnewhorizon.structurelib.structure.AutoPlaceEnvironment; +import com.gtnewhorizon.structurelib.structure.IItemSource; +import com.gtnewhorizon.structurelib.structure.IStructureElement; +import com.gtnewhorizon.structurelib.structure.IStructureElementNoPlacement; +import com.gtnewhorizon.structurelib.structure.StructureUtility; +import com.gtnewhorizon.structurelib.util.ItemStackPredicate; + +import gregtech.api.GregTech_API; +import gregtech.api.enums.HeatingCoilLevel; +import gregtech.api.enums.Materials; +import gregtech.api.enums.OrePrefixes; +import gregtech.api.interfaces.IHeatingCoil; +import gregtech.api.interfaces.metatileentity.IMetaTileEntity; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.metatileentity.BaseMetaPipeEntity; +import gregtech.api.metatileentity.implementations.GT_MetaPipeEntity_Frame; +import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_TieredMachineBlock; +import gregtech.common.blocks.GT_Block_Casings5; +import gregtech.common.blocks.GT_Item_Machines; + +public class GT_StructureUtility { + + // private static final Map<Class<?>, String> customNames = new HashMap<>(); + private GT_StructureUtility() { + throw new AssertionError("Not instantiable"); + } + + public static boolean hasMTE(IGregTechTileEntity aTile, Class<? extends IMetaTileEntity> clazz) { + return aTile != null && clazz.isInstance(aTile.getMetaTileEntity()); + } + + public static <T> IStructureElementNoPlacement<T> ofHatchAdder(IGT_HatchAdder<T> aHatchAdder, int aTextureIndex, + int aDots) { + return ofHatchAdder(aHatchAdder, aTextureIndex, StructureLibAPI.getBlockHint(), aDots - 1); + } + + public static <T> IStructureElement<T> ofFrame(Materials aFrameMaterial) { + if (aFrameMaterial == null) throw new IllegalArgumentException(); + return new IStructureElement<>() { + + private IIcon[] mIcons; + + @Override + public boolean check(T t, World world, int x, int y, int z) { + TileEntity tBase = world.getTileEntity(x, y, z); + if (tBase instanceof BaseMetaPipeEntity tPipeBase) { + if (tPipeBase.isInvalidTileEntity()) return false; + if (tPipeBase.getMetaTileEntity() instanceof GT_MetaPipeEntity_Frame) + return aFrameMaterial == ((GT_MetaPipeEntity_Frame) tPipeBase.getMetaTileEntity()).mMaterial; + } + return false; + } + + @Override + public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { + if (mIcons == null) { + mIcons = new IIcon[6]; + Arrays.fill(mIcons, aFrameMaterial.mIconSet.mTextures[OrePrefixes.frameGt.mTextureIndex].getIcon()); + } + StructureLibAPI.hintParticleTinted(world, x, y, z, mIcons, aFrameMaterial.mRGBa); + return true; + } + + @Override + public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) { + ItemStack tFrameStack = getFrameStack(); + if (!GT_Utility.isStackValid(tFrameStack) + || !(tFrameStack.getItem() instanceof ItemBlock tFrameStackItem)) return false; + return tFrameStackItem + .placeBlockAt(tFrameStack, null, world, x, y, z, 6, 0, 0, 0, Items.feather.getDamage(tFrameStack)); + } + + private ItemStack getFrameStack() { + return GT_OreDictUnificator.get(OrePrefixes.frameGt, aFrameMaterial, 1); + } + + @Override + public BlocksToPlace getBlocksToPlace(T t, World world, int x, int y, int z, ItemStack trigger, + AutoPlaceEnvironment env) { + ItemStack tFrameStack = getFrameStack(); + if (!GT_Utility.isStackValid(tFrameStack) || !(tFrameStack.getItem() instanceof ItemBlock)) + return BlocksToPlace.errored; + return BlocksToPlace.create(tFrameStack); + } + + @Override + public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger, + IItemSource s, EntityPlayerMP actor, Consumer<IChatComponent> chatter) { + return survivalPlaceBlock( + t, + world, + x, + y, + z, + trigger, + AutoPlaceEnvironment.fromLegacy(s, actor, chatter)); + } + + @Override + public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger, + AutoPlaceEnvironment env) { + if (check(t, world, x, y, z)) return SKIP; + ItemStack tFrameStack = getFrameStack(); + if (!GT_Utility.isStackValid(tFrameStack) || !(tFrameStack.getItem() instanceof ItemBlock)) + return REJECT; // honestly, this is more like a programming error or pack issue + return StructureUtility.survivalPlaceBlock( + tFrameStack, + ItemStackPredicate.NBTMode.IGNORE_KNOWN_INSIGNIFICANT_TAGS, + null, + false, + world, + x, + y, + z, + env.getSource(), + env.getActor(), + env.getChatter()); + } + }; + } + + public static <T> GT_HatchElementBuilder<T> buildHatchAdder() { + return GT_HatchElementBuilder.builder(); + } + + /** + * Completely equivalent to {@link #buildHatchAdder()}, except it plays nicer with type inference when statically + * imported + */ + public static <T> GT_HatchElementBuilder<T> buildHatchAdder(Class<T> typeToken) { + return GT_HatchElementBuilder.builder(); + } + + public static <T> IStructureElementNoPlacement<T> ofHatchAdder(IGT_HatchAdder<T> aHatchAdder, int aTextureIndex, + Block aHintBlock, int aHintMeta) { + if (aHatchAdder == null || aHintBlock == null) { + throw new IllegalArgumentException(); + } + return new IStructureElementNoPlacement<>() { + + @Override + public boolean check(T t, World world, int x, int y, int z) { + TileEntity tileEntity = world.getTileEntity(x, y, z); + return tileEntity instanceof IGregTechTileEntity + && aHatchAdder.apply(t, (IGregTechTileEntity) tileEntity, (short) aTextureIndex); + } + + @Override + public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { + StructureLibAPI.hintParticle(world, x, y, z, aHintBlock, aHintMeta); + return true; + } + }; + } + + public static <T> IStructureElement<T> ofHatchAdder(IGT_HatchAdder<T> aHatchAdder, int aTextureIndex, + Block aHintBlock, int aHintMeta, BiPredicate<T, IGregTechTileEntity> shouldSkip, + Function<T, Class<? extends IMetaTileEntity>> aMetaId, final IStructureElement.PlaceResult acceptType) { + if (aHatchAdder == null) { + throw new IllegalArgumentException(); + } + return new IStructureElement<>() { + + @Override + public boolean check(T t, World world, int x, int y, int z) { + TileEntity tileEntity = world.getTileEntity(x, y, z); + return tileEntity instanceof IGregTechTileEntity + && aHatchAdder.apply(t, (IGregTechTileEntity) tileEntity, (short) aTextureIndex); + } + + @Override + public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { + StructureLibAPI.hintParticle(world, x, y, z, aHintBlock, aHintMeta); + return true; + } + + @Override + public boolean placeBlock(T t, World world, int i, int i1, int i2, ItemStack itemStack) { + // TODO + return false; + } + + @Override + public BlocksToPlace getBlocksToPlace(T t, World world, int x, int y, int z, ItemStack trigger, + AutoPlaceEnvironment env) { + Class<? extends IMetaTileEntity> clazz = aMetaId.apply(t); + if (clazz == null) return BlocksToPlace.createEmpty(); + return BlocksToPlace.create(is -> clazz.isInstance(GT_Item_Machines.getMetaTileEntity(is))); + } + + @Override + public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger, + IItemSource s, EntityPlayerMP actor, Consumer<IChatComponent> chatter) { + return survivalPlaceBlock( + t, + world, + x, + y, + z, + trigger, + AutoPlaceEnvironment.fromLegacy(s, actor, chatter)); + } + + @Override + public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger, + AutoPlaceEnvironment env) { + if (shouldSkip != null) { + TileEntity tileEntity = world.getTileEntity(x, y, z); + if (tileEntity instanceof IGregTechTileEntity + && shouldSkip.test(t, (IGregTechTileEntity) tileEntity)) return SKIP; + } + if (!StructureLibAPI.isBlockTriviallyReplaceable(world, x, y, z, env.getActor())) return REJECT; + Class<? extends IMetaTileEntity> clazz = aMetaId.apply(t); + if (clazz == null) return REJECT; + ItemStack taken = env.getSource() + .takeOne(is -> clazz.isInstance(GT_Item_Machines.getMetaTileEntity(is)), true); + if (GT_Utility.isStackInvalid(taken)) { + env.getChatter() + .accept( + new ChatComponentTranslation( + "GT5U.autoplace.error.no_mte.class_name", + clazz.getSimpleName())); + return REJECT; + } + if (StructureUtility + .survivalPlaceBlock(taken, EXACT, null, true, world, x, y, z, env.getSource(), env.getActor()) + == ACCEPT) return acceptType; + return REJECT; + } + }; + } + + public static <T> IStructureElement<T> ofHatchAdder(IGT_HatchAdder<T> aHatchAdder, int aTextureIndex, + Block aHintBlock, int aHintMeta, BiPredicate<T, IGregTechTileEntity> shouldSkip, ToIntFunction<T> aMetaId) { + if (aHatchAdder == null) { + throw new IllegalArgumentException(); + } + return new IStructureElement<>() { + + @Override + public boolean check(T t, World world, int x, int y, int z) { + TileEntity tileEntity = world.getTileEntity(x, y, z); + return tileEntity instanceof IGregTechTileEntity + && aHatchAdder.apply(t, (IGregTechTileEntity) tileEntity, (short) aTextureIndex); + } + + @Override + public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { + StructureLibAPI.hintParticle(world, x, y, z, aHintBlock, aHintMeta); + return true; + } + + @Override + public boolean placeBlock(T t, World world, int i, int i1, int i2, ItemStack itemStack) { + // TODO + return false; + } + + @Override + public BlocksToPlace getBlocksToPlace(T t, World world, int x, int y, int z, ItemStack trigger, + AutoPlaceEnvironment env) { + GT_Item_Machines item = (GT_Item_Machines) Item.getItemFromBlock(GregTech_API.sBlockMachines); + int meta = aMetaId.applyAsInt(t); + if (meta < 0) return BlocksToPlace.createEmpty(); + return BlocksToPlace.create( + ItemStackPredicate.from(item) + .setMeta(meta)); + } + + @Override + public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger, + IItemSource s, EntityPlayerMP actor, Consumer<IChatComponent> chatter) { + return survivalPlaceBlock( + t, + world, + x, + y, + z, + trigger, + AutoPlaceEnvironment.fromLegacy(s, actor, chatter)); + } + + @Override + public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger, + AutoPlaceEnvironment env) { + if (shouldSkip != null) { + TileEntity tileEntity = world.getTileEntity(x, y, z); + if (tileEntity instanceof IGregTechTileEntity + && shouldSkip.test(t, (IGregTechTileEntity) tileEntity)) return SKIP; + } + if (!StructureLibAPI.isBlockTriviallyReplaceable(world, x, y, z, env.getActor())) return REJECT; + GT_Item_Machines item = (GT_Item_Machines) Item.getItemFromBlock(GregTech_API.sBlockMachines); + int meta = aMetaId.applyAsInt(t); + if (meta < 0) return REJECT; + ItemStack taken = env.getSource() + .takeOne( + ItemStackPredicate.from(item) + .setMeta(meta), + true); + if (GT_Utility.isStackInvalid(taken)) { + env.getChatter() + .accept(new ChatComponentTranslation("GT5U.autoplace.error.no_mte.id", meta)); + return REJECT; + } + return StructureUtility + .survivalPlaceBlock(taken, EXACT, null, true, world, x, y, z, env.getSource(), env.getActor()) + == ACCEPT ? ACCEPT_STOP : REJECT; + } + }; + } + + public static <T> IStructureElement<T> ofHatchAdderOptional(IGT_HatchAdder<T> aHatchAdder, int textureIndex, + int dots, Block placeCasing, int placeCasingMeta) { + return ofHatchAdderOptional( + aHatchAdder, + textureIndex, + StructureLibAPI.getBlockHint(), + dots - 1, + placeCasing, + placeCasingMeta); + } + + public static <T> IStructureElement<T> ofHatchAdderOptional(IGT_HatchAdder<T> aHatchAdder, int aTextureIndex, + Block aHintBlock, int hintMeta, Block placeCasing, int placeCasingMeta) { + if (aHatchAdder == null || aHintBlock == null) { + throw new IllegalArgumentException(); + } + return new IStructureElement<>() { + + @Override + public boolean check(T t, World world, int x, int y, int z) { + TileEntity tileEntity = world.getTileEntity(x, y, z); + Block worldBlock = world.getBlock(x, y, z); + return (tileEntity instanceof IGregTechTileEntity + && aHatchAdder.apply(t, (IGregTechTileEntity) tileEntity, (short) aTextureIndex)) + || (worldBlock == placeCasing && worldBlock.getDamageValue(world, x, y, z) == placeCasingMeta); + } + + @Override + public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { + StructureLibAPI.hintParticle(world, x, y, z, aHintBlock, hintMeta); + return true; + } + + @Override + public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) { + world.setBlock(x, y, z, placeCasing, placeCasingMeta, 2); + return true; + } + + @Override + public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger, + IItemSource s, EntityPlayerMP actor, Consumer<IChatComponent> chatter) { + if (check(t, world, x, y, z)) return SKIP; + return StructureUtility + .survivalPlaceBlock(placeCasing, placeCasingMeta, world, x, y, z, s, actor, chatter); + } + }; + } + + /** + * Assume all coils accepted. + * + * @see #ofCoil(BiPredicate, Function) + */ + public static <T> IStructureElement<T> ofCoil(BiConsumer<T, HeatingCoilLevel> aHeatingCoilSetter, + Function<T, HeatingCoilLevel> aHeatingCoilGetter) { + return ofCoil((t, l) -> { + aHeatingCoilSetter.accept(t, l); + return true; + }, aHeatingCoilGetter); + } + + /** + * Heating coil structure element. + * + * @param aHeatingCoilSetter Notify the controller of this new coil. Got called exactly once per coil. Might be + * called less times if structure test fails. If the setter returns false then it assumes + * the coil is rejected. + * @param aHeatingCoilGetter Get the current heating level. Null means no coil recorded yet. + */ + public static <T> IStructureElement<T> ofCoil(BiPredicate<T, HeatingCoilLevel> aHeatingCoilSetter, + Function<T, HeatingCoilLevel> aHeatingCoilGetter) { + if (aHeatingCoilSetter == null || aHeatingCoilGetter == null) { + throw new IllegalArgumentException(); + } + return new IStructureElement<>() { + + @Override + public boolean check(T t, World world, int x, int y, int z) { + Block block = world.getBlock(x, y, z); + if (!(block instanceof IHeatingCoil)) return false; + HeatingCoilLevel existingLevel = aHeatingCoilGetter.apply(t), + newLevel = ((IHeatingCoil) block).getCoilHeat(world.getBlockMetadata(x, y, z)); + if (existingLevel == null || existingLevel == HeatingCoilLevel.None) { + return aHeatingCoilSetter.test(t, newLevel); + } else { + return newLevel == existingLevel; + } + } + + @Override + public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { + StructureLibAPI.hintParticle(world, x, y, z, GregTech_API.sBlockCasings5, getMetaFromHint(trigger)); + return true; + } + + private int getMetaFromHint(ItemStack trigger) { + return GT_Block_Casings5.getMetaFromCoilHeat(getHeatFromHint(trigger)); + } + + private HeatingCoilLevel getHeatFromHint(ItemStack trigger) { + return HeatingCoilLevel + .getFromTier((byte) Math.min(HeatingCoilLevel.getMaxTier(), Math.max(0, trigger.stackSize - 1))); + } + + @Override + public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) { + return world.setBlock(x, y, z, GregTech_API.sBlockCasings5, getMetaFromHint(trigger), 3); + } + + @Override + public BlocksToPlace getBlocksToPlace(T t, World world, int x, int y, int z, ItemStack trigger, + AutoPlaceEnvironment env) { + return BlocksToPlace.create(GregTech_API.sBlockCasings5, getMetaFromHint(trigger)); + } + + @Override + public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger, + IItemSource s, EntityPlayerMP actor, Consumer<IChatComponent> chatter) { + return survivalPlaceBlock( + t, + world, + x, + y, + z, + trigger, + AutoPlaceEnvironment.fromLegacy(s, actor, chatter)); + } + + @Override + public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger, + AutoPlaceEnvironment env) { + Block block = world.getBlock(x, y, z); + boolean isCoil = block instanceof IHeatingCoil + && ((IHeatingCoil) block).getCoilHeat(world.getBlockMetadata(x, y, z)) == getHeatFromHint(trigger); + if (isCoil) return SKIP; + return StructureUtility.survivalPlaceBlock( + GregTech_API.sBlockCasings5, + getMetaFromHint(trigger), + world, + x, + y, + z, + env.getSource(), + env.getActor(), + env.getChatter()); + } + }; + } + + @Nonnull + public static Predicate<ItemStack> filterByMTEClass(List<? extends Class<? extends IMetaTileEntity>> list) { + return is -> { + IMetaTileEntity tile = GT_Item_Machines.getMetaTileEntity(is); + return tile != null && list.stream() + .anyMatch(c -> c.isInstance(tile)); + }; + } + + @Nonnull + public static Predicate<ItemStack> filterByMTETier(int aMinTier, int aMaxTier) { + return is -> { + IMetaTileEntity tile = GT_Item_Machines.getMetaTileEntity(is); + return tile instanceof GT_MetaTileEntity_TieredMachineBlock + && ((GT_MetaTileEntity_TieredMachineBlock) tile).mTier <= aMaxTier + && ((GT_MetaTileEntity_TieredMachineBlock) tile).mTier >= aMinTier; + }; + } +} diff --git a/src/main/java/gregtech/api/util/GT_StructureUtilityMuTE.java b/src/main/java/gregtech/api/util/GT_StructureUtilityMuTE.java new file mode 100644 index 0000000000..8e8d027463 --- /dev/null +++ b/src/main/java/gregtech/api/util/GT_StructureUtilityMuTE.java @@ -0,0 +1,271 @@ +package gregtech.api.util; + +import static gregtech.GT_Mod.GT_FML_LOGGER; +import static gregtech.api.multitileentity.enums.GT_MultiTileComponentCasing.*; +import static gregtech.api.multitileentity.enums.GT_MultiTileUpgradeCasing.*; +import static gregtech.loaders.preload.GT_Loader_MultiTileEntities.*; + +import java.util.Arrays; + +import net.minecraft.block.Block; +import net.minecraft.item.ItemStack; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.IIcon; +import net.minecraft.world.World; + +import com.gtnewhorizon.structurelib.StructureLibAPI; +import com.gtnewhorizon.structurelib.structure.IStructureElement; + +import gregtech.api.enums.GT_Values; +import gregtech.api.enums.OrePrefixes; +import gregtech.api.enums.TextureSet; +import gregtech.api.multitileentity.MultiTileEntityContainer; +import gregtech.api.multitileentity.MultiTileEntityRegistry; +import gregtech.api.multitileentity.enums.GT_MultiTileUpgradeCasing; +import gregtech.api.multitileentity.interfaces.IMultiBlockController; +import gregtech.api.multitileentity.interfaces.IMultiTileEntity; +import gregtech.api.multitileentity.multiblock.base.Controller; +import gregtech.api.multitileentity.multiblock.base.MultiBlockPart; + +public class GT_StructureUtilityMuTE { + + public static final MuTEStructureCasing MOTOR_CASINGS = FunctionalCasings.Motor.getCasing(); + public static final MuTEStructureCasing PUMP_CASINGS = FunctionalCasings.Pump.getCasing(); + public static final MuTEStructureCasing CONVEYOR_CASINGS = FunctionalCasings.Conveyor.getCasing(); + public static final MuTEStructureCasing PISTON_CASINGS = FunctionalCasings.Piston.getCasing(); + public static final MuTEStructureCasing ROBOT_ARM_CASINGS = FunctionalCasings.RobotArm.getCasing(); + public static final MuTEStructureCasing EMITTER_CASINGS = FunctionalCasings.Emitter.getCasing(); + public static final MuTEStructureCasing SENSOR_CASINGS = FunctionalCasings.Sensor.getCasing(); + public static final MuTEStructureCasing FIELD_GENERATOR_CASINGS = FunctionalCasings.FieldGenerator.getCasing(); + public static final MuTEStructureCasing INVENTORY_CASINGS = UpgradeCasings.Inventory.getCasing(); + public static final MuTEStructureCasing TANK_CASINGS = UpgradeCasings.Tank.getCasing(); + public static final MuTEStructureCasing AMPERAGE_CASINGS = UpgradeCasings.Amperage.getCasing(); + public static final MuTEStructureCasing LASER_CASINGS = UpgradeCasings.Laser.getCasing(); + public static final MuTEStructureCasing WIRELESS_CASINGS = UpgradeCasings.Wireless.getCasing(); + public static final MuTEStructureCasing CLEANROOM_CASINGS = UpgradeCasings.Cleanroom.getCasing(); + public static final MuTEStructureCasing HEATER_CASINGS = UpgradeCasings.Heater.getCasing(); + public static final MuTEStructureCasing INSULATOR_CASINGS = UpgradeCasings.Insulator.getCasing(); + + public enum FunctionalCasings { + + Motor(COMPONENT_CASING_REGISTRY_NAME, LV_Motor.getId(), MV_Motor.getId(), HV_Motor.getId(), EV_Motor.getId(), + IV_Motor.getId(), LuV_Motor.getId(), ZPM_Motor.getId(), UV_Motor.getId(), UHV_Motor.getId(), + UEV_Motor.getId(), UIV_Motor.getId(), UMV_Motor.getId(), UXV_Motor.getId(), MAX_Motor.getId()), + + Pump(COMPONENT_CASING_REGISTRY_NAME, LV_Pump.getId(), MV_Pump.getId(), HV_Pump.getId(), EV_Pump.getId(), + IV_Pump.getId(), LuV_Pump.getId(), ZPM_Pump.getId(), UV_Pump.getId(), UHV_Pump.getId(), UEV_Pump.getId(), + UIV_Pump.getId(), UMV_Pump.getId(), UXV_Pump.getId(), MAX_Pump.getId()), + + Conveyor(COMPONENT_CASING_REGISTRY_NAME, LV_Conveyor.getId(), MV_Conveyor.getId(), HV_Conveyor.getId(), + EV_Conveyor.getId(), IV_Conveyor.getId(), LuV_Conveyor.getId(), ZPM_Conveyor.getId(), UV_Conveyor.getId(), + UHV_Conveyor.getId(), UEV_Conveyor.getId(), UIV_Conveyor.getId(), UMV_Conveyor.getId(), + UXV_Conveyor.getId(), MAX_Conveyor.getId()), + + Piston(COMPONENT_CASING_REGISTRY_NAME, LV_Piston.getId(), MV_Piston.getId(), HV_Piston.getId(), + EV_Piston.getId(), IV_Piston.getId(), LuV_Piston.getId(), ZPM_Piston.getId(), UV_Piston.getId(), + UHV_Piston.getId(), UEV_Piston.getId(), UIV_Piston.getId(), UMV_Piston.getId(), UXV_Piston.getId(), + MAX_Piston.getId()), + + RobotArm(COMPONENT_CASING_REGISTRY_NAME, LV_RobotArm.getId(), MV_RobotArm.getId(), HV_RobotArm.getId(), + EV_RobotArm.getId(), IV_RobotArm.getId(), LuV_RobotArm.getId(), ZPM_RobotArm.getId(), UV_RobotArm.getId(), + UHV_RobotArm.getId(), UEV_RobotArm.getId(), UIV_RobotArm.getId(), UMV_RobotArm.getId(), + UXV_RobotArm.getId(), MAX_RobotArm.getId()), + + Emitter(COMPONENT_CASING_REGISTRY_NAME, LV_Emitter.getId(), MV_Emitter.getId(), HV_Emitter.getId(), + EV_Emitter.getId(), IV_Emitter.getId(), LuV_Emitter.getId(), ZPM_Emitter.getId(), UV_Emitter.getId(), + UHV_Emitter.getId(), UEV_Emitter.getId(), UIV_Emitter.getId(), UMV_Emitter.getId(), UXV_Emitter.getId(), + MAX_Emitter.getId()), + + Sensor(COMPONENT_CASING_REGISTRY_NAME, LV_Sensor.getId(), MV_Sensor.getId(), HV_Sensor.getId(), + EV_Sensor.getId(), IV_Sensor.getId(), LuV_Sensor.getId(), ZPM_Sensor.getId(), UV_Sensor.getId(), + UHV_Sensor.getId(), UEV_Sensor.getId(), UIV_Sensor.getId(), UMV_Sensor.getId(), UXV_Sensor.getId(), + MAX_Sensor.getId()), + + FieldGenerator(COMPONENT_CASING_REGISTRY_NAME, LV_FieldGenerator.getId(), MV_FieldGenerator.getId(), + HV_FieldGenerator.getId(), EV_FieldGenerator.getId(), IV_FieldGenerator.getId(), LuV_FieldGenerator.getId(), + ZPM_FieldGenerator.getId(), UV_FieldGenerator.getId(), UHV_FieldGenerator.getId(), + UEV_FieldGenerator.getId(), UIV_FieldGenerator.getId(), UMV_FieldGenerator.getId(), + UXV_FieldGenerator.getId(), MAX_FieldGenerator.getId()); + + private final MuTEStructureCasing casing; + + FunctionalCasings(String registryName, Integer... validIds) { + casing = createMuTEStructureCasing(registryName, validIds); + } + + public MuTEStructureCasing getCasing() { + return casing; + } + } + + public enum UpgradeCasings { + + Inventory(UPGRADE_CASING_REGISTRY_NAME, ULV_Inventory.getId(), LV_Inventory.getId(), MV_Inventory.getId(), + HV_Inventory.getId(), EV_Inventory.getId(), IV_Inventory.getId(), LuV_Inventory.getId(), + ZPM_Inventory.getId(), UV_Inventory.getId(), UHV_Inventory.getId(), UEV_Inventory.getId(), + UIV_Inventory.getId(), UMV_Inventory.getId(), UXV_Inventory.getId(), MAX_Inventory.getId()), + + Tank(UPGRADE_CASING_REGISTRY_NAME, ULV_Tank.getId(), LV_Tank.getId(), MV_Tank.getId(), HV_Tank.getId(), + EV_Tank.getId(), IV_Tank.getId(), LuV_Tank.getId(), ZPM_Tank.getId(), UV_Tank.getId(), UHV_Tank.getId(), + UEV_Tank.getId(), UIV_Tank.getId(), UMV_Tank.getId(), UXV_Tank.getId(), MAX_Tank.getId()), + + Amperage(UPGRADE_CASING_REGISTRY_NAME, Amp_4.getId(), Amp_16.getId(), Amp_64.getId(), Amp_256.getId(), + Amp_1_024.getId(), Amp_4_096.getId(), Amp_16_384.getId(), Amp_65_536.getId(), Amp_262_144.getId(), + Amp_1_048_576.getId()), + + Laser(UPGRADE_CASING_REGISTRY_NAME, GT_MultiTileUpgradeCasing.Laser.getId()), + + Wireless(UPGRADE_CASING_REGISTRY_NAME, GT_MultiTileUpgradeCasing.Wireless.getId()), + + Cleanroom(UPGRADE_CASING_REGISTRY_NAME, GT_MultiTileUpgradeCasing.Cleanroom.getId()), + + Heater(UPGRADE_CASING_REGISTRY_NAME, Heater_Prototype.getId(), Heater_IndustrialGrade.getId(), + Heater_NextGen.getId(), Heater_Omnipotent.getId(), Heater_OmegaType.getId()), + + Insulator(UPGRADE_CASING_REGISTRY_NAME, Insulator_Prototype.getId(), Insulator_IndustrialGrade.getId(), + Insulator_NextGen.getId(), Insulator_Omnipotent.getId(), Insulator_OmegaType.getId()); + + private final MuTEStructureCasing casing; + + UpgradeCasings(String registryName, Integer... validIds) { + casing = createMuTEStructureCasing(registryName, validIds); + } + + public MuTEStructureCasing getCasing() { + return casing; + } + } + + /** + * Specify all casing sets that are valid for a multiblock structure position. The first casing will be used as + * default when doing auto place + * + * @param modes Allowed modes on the casings + * @param validCasings Allowed casing sets + * @return Structure Element + * @param <T> Multiblock class + */ + public static <T> IStructureElement<T> ofMuTECasings(int modes, MuTEStructureCasing... validCasings) { + if (validCasings == null || validCasings.length == 0) { + throw new IllegalArgumentException(); + } + return new IStructureElement<>() { + + final MuTEStructureCasing[] allowedCasings = validCasings; + private final static short[] DEFAULT = new short[] { 255, 255, 255, 0 }; + private static IIcon[] mIcons = null; + + @Override + public boolean check(T t, World world, int x, int y, int z) { + final TileEntity tileEntity = world.getTileEntity(x, y, z); + if (!(tileEntity instanceof MultiBlockPart part)) return false; + + for (MuTEStructureCasing casing : allowedCasings) { + if (casing.isCasingValid(part.getMultiTileEntityRegistryID(), part.getMultiTileEntityID())) { + final IMultiBlockController tTarget = part.getTarget(false); + if (tTarget != null && tTarget != t) return false; + + part.setTarget((IMultiBlockController) t, modes); + + ((Controller<?, ?>) t).registerSpecialCasings(part); + return true; + } + } + + return false; + } + + @Override + public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { + // Moved here from Controller. TODO: Proper implementation + if (mIcons == null) { + mIcons = new IIcon[6]; + Arrays.fill(mIcons, TextureSet.SET_NONE.mTextures[OrePrefixes.block.mTextureIndex].getIcon()); + } + final short[] RGBA = DEFAULT; + StructureLibAPI.hintParticleTinted(world, x, y, z, mIcons, RGBA); + return true; + } + + @Override + public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) { + final MultiTileEntityRegistry tRegistry = MultiTileEntityRegistry + .getRegistry(validCasings[0].getRegistryId()); + if (tRegistry == null) { + GT_FML_LOGGER.error("NULL REGISTRY"); + return false; + } + final MultiTileEntityContainer tContainer = tRegistry + .getNewTileEntityContainer(world, x, y, z, validCasings[0].defaultMeta, null); + if (tContainer == null) { + GT_FML_LOGGER.error("NULL CONTAINER"); + return false; + } + final IMultiTileEntity te = ((IMultiTileEntity) tContainer.mTileEntity); + if (!(te instanceof MultiBlockPart)) { + GT_FML_LOGGER.error("Not a multiblock part"); + return false; + } + if (world.setBlock(x, y, z, tContainer.mBlock, 15 - tContainer.mBlockMetaData, 2)) { + tContainer.setMultiTile(world, x, y, z); + ((MultiBlockPart) te).setTarget((IMultiBlockController) t, modes); + + ((Controller<?, ?>) t).registerSpecialCasings((MultiBlockPart) te); + } + + return false; + } + }; + } + + public static MuTEStructureCasing createMuTEStructureCasing(String registryName, Integer... validIds) { + return new MuTEStructureCasing(registryName, validIds); + } + + /** + * Object used to store a set of casings (e.g. all motor casings) + */ + public static class MuTEStructureCasing { + + private String registryName; + private int registryId = GT_Values.W; + private final int defaultMeta; + private final Integer[] validIds; + + public MuTEStructureCasing(String registryName, Integer... validIds) { + MultiTileEntityRegistry registry = MultiTileEntityRegistry.getRegistry(registryName); + if (validIds == null || validIds.length == 0 || registry == null) { + throw new IllegalArgumentException(); + } + this.registryName = registryName; + this.validIds = validIds; + this.defaultMeta = validIds[0]; + } + + public boolean isCasingValid(int registryId, int id) { + if (getRegistryId() != registryId) { + return false; + } + for (Integer validId : validIds) { + if (validId == id) { + return true; + } + } + return false; + } + + public int getDefaultMeta() { + return defaultMeta; + } + + public int getRegistryId() { + // TODO: MuTE registry seems to somehow shift, probably due to NBT shenanigans. Lazy init circumvents this + // but it should be properly fixed in the future + if (registryId == GT_Values.W) { + MultiTileEntityRegistry registry = MultiTileEntityRegistry.getRegistry(registryName); + registryId = Block.getIdFromBlock(registry.mBlock); + } + return registryId; + } + } +} diff --git a/src/main/java/gregtech/api/util/GT_ToolHarvestHelper.java b/src/main/java/gregtech/api/util/GT_ToolHarvestHelper.java new file mode 100644 index 0000000000..4263b77be6 --- /dev/null +++ b/src/main/java/gregtech/api/util/GT_ToolHarvestHelper.java @@ -0,0 +1,71 @@ +package gregtech.api.util; + +import net.minecraft.block.Block; +import net.minecraft.block.material.Material; + +import ic2.core.block.BlockMultiID; +import ic2.core.block.BlockScaffold; +import ic2.core.block.machine.BlockMiningPipe; +import ic2.core.block.machine.BlockMiningTip; +import ic2.core.block.wiring.BlockCable; +import ic2.core.crop.BlockCrop; + +public class GT_ToolHarvestHelper { + + public static boolean isAppropriateTool(Block aBlock, byte aMetaData, String... tTools) { + + if (aBlock == null || tTools == null) { + return false; + } + String targetTool = aBlock.getHarvestTool(aMetaData); + return !isStringEmpty(targetTool) && isArrayContains(targetTool, tTools); + } + + public static boolean isAppropriateMaterial(Block aBlock, Material... tMats) { + if (aBlock == null || tMats == null) { + return false; + } + return isArrayContains(aBlock.getMaterial(), tMats); + } + + public static boolean isSpecialBlock(Block aBlock, Block... tBlocks) { + if (aBlock == null || tBlocks == null) { + return false; + } + return isArrayContains(aBlock, tBlocks); + } + + public static <T> boolean isArrayContains(T obj, T[] list) { + + if (obj == null || list == null) { + return false; + } + + for (T iObj : list) { + if (obj == iObj || obj.equals(iObj)) { + return true; + } + } + return false; + } + + public static boolean isStringEmpty(String s) { + return s == null || s.isEmpty(); + } + + public static boolean isIC2Wrenchable(Block block) { + return (block instanceof BlockMultiID && !(block instanceof BlockCable) && !(block instanceof BlockCrop)) + || block instanceof BlockScaffold + || block instanceof BlockMiningPipe + || block instanceof BlockMiningTip; + } + + public static boolean hasNull(Object... obj) { + for (Object iObj : obj) { + if (iObj == null) { + return true; + } + } + return false; + } +} diff --git a/src/main/java/gregtech/api/util/GT_TooltipDataCache.java b/src/main/java/gregtech/api/util/GT_TooltipDataCache.java new file mode 100644 index 0000000000..431ef34fa4 --- /dev/null +++ b/src/main/java/gregtech/api/util/GT_TooltipDataCache.java @@ -0,0 +1,105 @@ +package gregtech.api.util; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import net.minecraft.util.StatCollector; + +import gregtech.GT_Mod; + +public class GT_TooltipDataCache { + + public static class TooltipData { + + public List<String> text; + public List<String> shiftText; + + public TooltipData(List<String> text, List<String> shiftText) { + this.text = text; + this.shiftText = shiftText; + } + } + + private final Map<String, TooltipData> fetchedTooltipData = new HashMap<>(); + + /** + * Returns tooltip data respecting the user's configured verbosity levels, applying any formatting arguments. + * + * @param key the key to lookup + * @param args arguments for string formatting (prefer using positional arguments) + * @return The tooltip data the user asked for + */ + public TooltipData getData(String key, Object... args) { + TooltipData tooltipData = fetchedTooltipData.get(key); + if (tooltipData == null) { + tooltipData = getUncachedTooltipData(key, args); + fetchedTooltipData.put(key, tooltipData); + } + return tooltipData; + } + + /** + * Builds tooltip data respecting the user's configured verbosity levels, applying any formatting arguments. + * + * @param key the key to lookup + * @param args arguments for string formatting (prefer using positional arguments) + * @return The tooltip data the user asked for + */ + public TooltipData getUncachedTooltipData(String key, Object... args) { + List<String> lines = getAllLines(key, args); + int normalLines = lines.size(); + if (Math.max(GT_Mod.gregtechproxy.mTooltipVerbosity, GT_Mod.gregtechproxy.mTooltipShiftVerbosity) >= 3) { + lines.addAll(getAllLines(key + ".extended", args)); // Are extended lines enabled? If so add them to the + // lines + } + if (lines.size() == 0) { + lines.add(key); // Fallback in case no lines could be found at all + } + return new TooltipData( + lines.subList(0, getVerbosityIndex(GT_Mod.gregtechproxy.mTooltipVerbosity, normalLines, lines.size())), + lines + .subList(0, getVerbosityIndex(GT_Mod.gregtechproxy.mTooltipShiftVerbosity, normalLines, lines.size()))); + } + + /** + * Gets all the lines for the given key and every other subsequent consecutive key with a .n suffix, n in {1,2,3...} + * + * @param key the key to lookup + * @param args arguments for string formatting (prefer using positional arguments) + * @return The lines for the key and all of it's subkeys + */ + private List<String> getAllLines(String key, Object... args) { + List<String> lines = new ArrayList<>(); + String keyToLookup = key; + int i = 1; // First loop has no .number postfix + while (StatCollector.canTranslate(keyToLookup)) { + lines.add(StatCollector.translateToLocalFormatted(keyToLookup, args)); + keyToLookup = key + "." + i++; + } + return lines; + } + + /** + * Determines how many lines from a tooltip to include from the full line list to respect a given verbosity level. + * + * @param tooltipVerbosity the verbosity level we're applying + * @param defaultIndex return if tooltipVerbosity is 2 + * @param maxIndex return if tooltipVerbosity is greater than 2 + * @return verbosity appropriate index + */ + private static int getVerbosityIndex(int tooltipVerbosity, int defaultIndex, int maxIndex) { + int index; + if (tooltipVerbosity < 1) { + index = 0; + } else if (tooltipVerbosity == 1) { + index = 1; + } else if (tooltipVerbosity == 2) { + index = defaultIndex; + } else { + index = maxIndex; + } + return index; + } +} diff --git a/src/main/java/gregtech/api/util/GT_Util.java b/src/main/java/gregtech/api/util/GT_Util.java new file mode 100644 index 0000000000..8a799a9616 --- /dev/null +++ b/src/main/java/gregtech/api/util/GT_Util.java @@ -0,0 +1,202 @@ +package gregtech.api.util; + +import net.minecraft.block.Block; +import net.minecraft.init.Blocks; +import net.minecraft.nbt.NBTBase; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.ChunkCoordinates; +import net.minecraft.util.Tuple; +import net.minecraft.world.World; +import net.minecraft.world.chunk.Chunk; + +import gregtech.api.multitileentity.interfaces.IMultiTileEntity; + +public class GT_Util { + + // Last broken tile entity + public static final ThreadLocal<TileEntity> LAST_BROKEN_TILEENTITY = new ThreadLocal<>(); + + public static Tuple tuple(String key, Object value) { + return new Tuple(key, value); + } + + public static NBTTagCompound fuseNBT(NBTTagCompound aNBT1, NBTTagCompound aNBT2) { + if (aNBT1 == null) return aNBT2 == null ? new NBTTagCompound() : (NBTTagCompound) aNBT2.copy(); + final NBTTagCompound rNBT = (NBTTagCompound) aNBT1.copy(); + if (aNBT2 == null) return rNBT; + for (Object tKey : aNBT2.func_150296_c /* getKeySet */()) + if (!rNBT.hasKey(tKey.toString())) rNBT.setTag(tKey.toString(), aNBT2.getTag(tKey.toString())); + return rNBT; + } + + /** + * Construct a NBTTagCompound from a series of key, value pairs. Inspired from GT6. + */ + public static NBTTagCompound makeNBT(Tuple... aTags) { + final NBTTagCompound rNBT = new NBTTagCompound(); + for (Tuple t : aTags) { + if (t.getSecond() == null) continue; + + if (t.getSecond() instanceof Boolean) rNBT.setBoolean( + t.getFirst() + .toString(), + (Boolean) t.getSecond()); + else if (t.getSecond() instanceof Byte) rNBT.setByte( + t.getFirst() + .toString(), + (Byte) t.getSecond()); + else if (t.getSecond() instanceof Short) rNBT.setShort( + t.getFirst() + .toString(), + (Short) t.getSecond()); + else if (t.getSecond() instanceof Integer) rNBT.setInteger( + t.getFirst() + .toString(), + (Integer) t.getSecond()); + else if (t.getSecond() instanceof Long) rNBT.setLong( + t.getFirst() + .toString(), + (Long) t.getSecond()); + else if (t.getSecond() instanceof Float) rNBT.setFloat( + t.getFirst() + .toString(), + (Float) t.getSecond()); + else if (t.getSecond() instanceof Double) rNBT.setDouble( + t.getFirst() + .toString(), + (Double) t.getSecond()); + else if (t.getSecond() instanceof String) rNBT.setString( + t.getFirst() + .toString(), + (String) t.getSecond()); + else if (t.getSecond() instanceof NBTBase) rNBT.setTag( + t.getFirst() + .toString(), + (NBTBase) t.getSecond()); + else rNBT.setString( + t.getFirst() + .toString(), + t.getSecond() + .toString()); + } + + return rNBT; + } + + /** + * Get a TileEntity + */ + public static TileEntity getTileEntity(World aWorld, int aX, int aY, int aZ, boolean aLoadUnloadedChunks) { + if (aLoadUnloadedChunks || aWorld.blockExists(aX, aY, aZ)) { + TileEntity rTileEntity = aWorld.getTileEntity(aX, aY, aZ); + if (rTileEntity instanceof IMultiTileEntity && ((IMultiTileEntity) rTileEntity).isDead()) return null; + if (rTileEntity != null) return rTileEntity; + rTileEntity = LAST_BROKEN_TILEENTITY.get(); + if (rTileEntity != null && rTileEntity.xCoord == aX && rTileEntity.yCoord == aY && rTileEntity.zCoord == aZ) + return rTileEntity; + } + return null; + } + + /** Sets the TileEntity at the passed position, with the option of turning adjacent TileEntity updates off. */ + public static TileEntity setTileEntity(World aWorld, int aX, int aY, int aZ, TileEntity aTileEntity, + boolean aCauseTileEntityUpdates) { + if (aCauseTileEntityUpdates) aWorld.setTileEntity(aX, aY, aZ, aTileEntity); + else { + Chunk tChunk = aWorld.getChunkFromChunkCoords(aX >> 4, aZ >> 4); + if (tChunk != null) { + aWorld.addTileEntity(aTileEntity); + tChunk.func_150812_a /* setBlockTileEntityInChunk */(aX & 15, aY, aZ & 15, aTileEntity); + tChunk.setChunkModified(); + } + } + return aTileEntity; + } + + public static boolean setTileEntity(World aWorld, int aX, int aY, int aZ, Block aBlock, short aMeta, long aFlags, + boolean aRemoveGrassBelow) { + if (aRemoveGrassBelow) { + final Block tBlock = aWorld.getBlock(aX, aY - 1, aZ); + if (tBlock == Blocks.grass || tBlock == Blocks.mycelium) + aWorld.setBlock(aX, aY - 1, aZ, Blocks.dirt, 0, (byte) aFlags); + } + return aWorld.setBlock(aX, aY, aZ, aBlock, aMeta, (byte) aFlags); + } + + public static TileEntity getTileEntity(World aWorld, ChunkCoordinates aCoords, boolean aLoadUnloadedChunks) { + return getTileEntity(aWorld, aCoords.posX, aCoords.posY, aCoords.posZ, aLoadUnloadedChunks); + } + + /** Marks a Chunk dirty so it is saved */ + public static boolean markChunkDirty(World aWorld, int aX, int aZ) { + if (aWorld == null || aWorld.isRemote) return false; + Chunk aChunk = aWorld.getChunkFromBlockCoords(aX, aZ); + if (aChunk == null) { + aWorld.getBlockMetadata(aX, 0, aZ); + aChunk = aWorld.getChunkFromBlockCoords(aX, aZ); + if (aChunk == null) { + GT_Log.err.println( + "Some important Chunk does not exist for some reason at Coordinates X: " + aX + " and Z: " + aZ); + return false; + } + } + aChunk.setChunkModified(); + return true; + } + + /** Marks a Chunk dirty so it is saved */ + public static boolean markChunkDirty(Object aTileEntity) { + return aTileEntity instanceof TileEntity && markChunkDirty( + ((TileEntity) aTileEntity).getWorldObj(), + ((TileEntity) aTileEntity).xCoord, + ((TileEntity) aTileEntity).zCoord); + } + + public static int mixRGBInt(int aRGB1, int aRGB2) { + return getRGBInt( + new short[] { (short) ((getR(aRGB1) + getR(aRGB2)) >> 1), (short) ((getG(aRGB1) + getG(aRGB2)) >> 1), + (short) ((getB(aRGB1) + getB(aRGB2)) >> 1) }); + } + + public static int getRGBInt(short[] aColors) { + return aColors == null ? 16777215 : (aColors[0] << 16) | (aColors[1] << 8) | aColors[2]; + } + + public static int getRGBaInt(short[] aColors) { + return aColors == null ? 16777215 : (aColors[0]) << 16 | (aColors[1] << 8) | aColors[2] | (aColors[3] << 24); + } + + public static String toHexString(short[] aColors) { + return aColors == null ? "FFFFFF" : Integer.toHexString((aColors[0] << 16) | (aColors[1] << 8) | aColors[2]); + } + + public static int getRGBInt(short aR, short aG, short aB) { + return (aR << 16) | (aG << 8) | aB; + } + + public static int getRGBaInt(short aR, short aG, short aB, short aA) { + return (aR << 16) | (aG << 8) | aB | (aA << 24); + } + + public static short[] getRGBaArray(int aColors) { + return new short[] { (short) ((aColors >>> 16) & 255), (short) ((aColors >>> 8) & 255), (short) (aColors & 255), + (short) ((aColors >>> 24) & 255) }; + } + + public static short getR(int aColors) { + return (short) ((aColors >>> 16) & 255); + } + + public static short getG(int aColors) { + return (short) ((aColors >>> 8) & 255); + } + + public static short getB(int aColors) { + return (short) (aColors & 255); + } + + public static short getA(int aColors) { + return (short) ((aColors >>> 24) & 255); + } +} diff --git a/src/main/java/gregtech/api/util/GT_Utility.java b/src/main/java/gregtech/api/util/GT_Utility.java new file mode 100644 index 0000000000..62c4498927 --- /dev/null +++ b/src/main/java/gregtech/api/util/GT_Utility.java @@ -0,0 +1,4982 @@ +package gregtech.api.util; + +import static gregtech.GT_Mod.GT_FML_LOGGER; +import static gregtech.api.enums.GT_Values.D1; +import static gregtech.api.enums.GT_Values.E; +import static gregtech.api.enums.GT_Values.GT; +import static gregtech.api.enums.GT_Values.L; +import static gregtech.api.enums.GT_Values.M; +import static gregtech.api.enums.GT_Values.NW; +import static gregtech.api.enums.GT_Values.V; +import static gregtech.api.enums.GT_Values.W; +import static gregtech.api.enums.Materials.FLUID_MAP; +import static gregtech.api.enums.Mods.Translocator; +import static gregtech.common.GT_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.GregTech_API; +import gregtech.api.damagesources.GT_DamageSources; +import gregtech.api.damagesources.GT_DamageSources.DamageSourceHotItem; +import gregtech.api.enchants.Enchantment_Hazmat; +import gregtech.api.enchants.Enchantment_Radioactivity; +import gregtech.api.enums.GT_Values; +import gregtech.api.enums.ItemList; +import gregtech.api.enums.Materials; +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.GT_EnergyArmor_Item; +import gregtech.api.items.GT_Generic_Item; +import gregtech.api.items.GT_MetaGenerated_Tool; +import gregtech.api.metatileentity.MetaTileEntity; +import gregtech.api.net.GT_Packet_Sound; +import gregtech.api.objects.CollectorUtils; +import gregtech.api.objects.GT_ItemStack; +import gregtech.api.objects.GT_ItemStack2; +import gregtech.api.objects.ItemData; +import gregtech.api.recipe.RecipeMaps; +import gregtech.api.threads.GT_Runnable_Sound; +import gregtech.api.util.extensions.ArrayExt; +import gregtech.common.GT_Pollution; +import gregtech.common.blocks.GT_Block_Ores_Abstract; +import ic2.api.recipe.IRecipeInput; +import ic2.api.recipe.RecipeInputItemStack; +import ic2.api.recipe.RecipeInputOreDict; +import ic2.api.recipe.RecipeOutput; + +/** + * NEVER INCLUDE THIS FILE IN YOUR MOD!!! + * <p/> + * Just a few Utility Functions I use. + */ +public class GT_Utility { + + /** + * 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<GT_ItemStack, FluidContainerData> sFilledContainerToData = new /* Concurrent */ HashMap<>(); + private static final Map<GT_ItemStack, 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<GT_PlayedSound, 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 { + GregTech_API.sItemStackMappings.add(sFilledContainerToData); + GregTech_API.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( + () -> GT_OreDictUnificator.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(GT_Log.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(GT_Log.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(GT_Log.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(GT_Log.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(GT_Log.err); + } + } else { + try { + return aClass.getConstructors()[aConstructorIndex].newInstance(aParameters); + } catch (Throwable e) { + if (aLogErrors) e.printStackTrace(GT_Log.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(GT_Log.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(GT_Log.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 GT_EnergyArmor_Item) { + if ((((GT_EnergyArmor_Item) aPlayer.inventory.armorInventory[i].getItem()).mSpecials & 512) + != 0) { + if (GT_ModHandler.canUseElectricItem(aPlayer.inventory.armorInventory[i], 10000)) { + return true; + } + } + } + } + } + } + } catch (Throwable e) { + if (D1) e.printStackTrace(GT_Log.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, GT_Values.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[GT_Utility.getTier(voltage)]; + } + + public static String getColoredTierNameFromVoltage(long voltage) { + return getColoredTierNameFromTier(getTier(voltage)); + } + + public static String getColoredTierNameFromTier(byte tier) { + return GT_Values.TIER_COLORS[tier] + GT_Values.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 >= GT_Values.VN.length - 1) { + return " (MAX+)"; + } + return " (" + GT_Values.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() + && (aIgnoreNBT || (((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); + } + + public static boolean areStacksEqualOrNull(ItemStack stack1, ItemStack stack2) { + return (stack1 == null && stack2 == null) || GT_Utility.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( + GT_OreDictUnificator.get_nocopy(aStack1), + GT_OreDictUnificator.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 GT_ItemStack(tData.filledContainer), tData); + Map<String, FluidContainerData> tFluidToContainer = sEmptyContainerToFluidToData + .get(new GT_ItemStack(tData.emptyContainer)); + List<ItemStack> tContainers = sFluidToContainers.get(fluidName); + if (tFluidToContainer == null) { + sEmptyContainerToFluidToData + .put(new GT_ItemStack(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 GT_ItemStack(aData.filledContainer), aData); + Map<String, FluidContainerData> tFluidToContainer = sEmptyContainerToFluidToData + .get(new GT_ItemStack(aData.emptyContainer)); + List<ItemStack> tContainers = sFluidToContainers.get(fluidName); + if (tFluidToContainer == null) { + sEmptyContainerToFluidToData + .put(new GT_ItemStack(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 (GT_ModHandler.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 GT_ItemStack(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 GT_ItemStack(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 GT_ItemStack(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 GT_ItemStack(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 = GT_ModHandler.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 = GT_Utility.getFluidForFilledItem(stack, true); + if (fluidStack == null) { + fluidStack = GT_Utility.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 = GT_OreDictUnificator.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(GT_OreDictUnificator.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 = GT_OreDictUnificator.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(GT_OreDictUnificator.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, GT_OreDictUnificator::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(GT_OreDictUnificator.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 = GT_OreDictUnificator.getAssociation(aInput); + for (Object o : aOutput) { + if (o == null) { + GT_FML_LOGGER.info("EmptyIC2Output!" + aInput.getUnlocalizedName()); + return false; + } + } + ItemStack[] tStack = GT_OreDictUnificator.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 = GregTech_API.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 = GregTech_API.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", GT_LanguageManager.addStringLocalization("Book." + aTitle + ".Name", aTitle)); + tNBT.setString("author", aAuthor); + NBTTagList tNBTList = new NBTTagList(); + for (byte i = 0; i < aPages.length; i++) { + aPages[i] = GT_LanguageManager + .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 GT_Log.err.println("WARNING: String for written Book too long! -> " + aPages[i]); + } else { + GT_Log.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); + GT_Log.out.println( + "GT_Mod: Added Book to Book List - Mapping: '" + aMapping + + "' - Name: '" + + aTitle + + "' - Author: '" + + aAuthor + + "'"); + GregTech_API.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 (GregTech_API.sMultiThreadedSounds) new Thread( + new GT_Runnable_Sound( + GT.getThePlayer().worldObj, + aX, + aY, + aZ, + aTimeUntilNextSound, + aSoundResourceLocation, + aSoundStrength, + aSoundModulation), + "Sound Effect").start(); + else new GT_Runnable_Sound( + GT.getThePlayer().worldObj, + aX, + aY, + aZ, + aTimeUntilNextSound, + aSoundResourceLocation, + aSoundStrength, + aSoundModulation).run(); + return true; + } + + /** + * @inheritDoc + * @Deprecated Use {@link #doSoundAtClient(ResourceLocation, int, float, float, double, double, double)} + */ + @Deprecated + public static boolean doSoundAtClient(String aSoundName, int aTimeUntilNextSound, float aSoundStrength, + float aSoundModulation, double aX, double aY, double aZ) { + if (isStringInvalid(aSoundName)) return false; + return doSoundAtClient( + new ResourceLocation(aSoundName), + aTimeUntilNextSound, + aSoundStrength, + aSoundModulation, + aX, + aY, + aZ); + } + + 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 GT_Packet_Sound(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 GT_Packet_Sound( + 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; + } + + @Deprecated // why do you use Objects? + public static Block getBlock(Object aBlock) { + return (Block) aBlock; + } + + 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); + } + + @Deprecated // why do you use Objects? And if you want to check your block to be not null, check it directly! + public static boolean isBlockValid(Object aBlock) { + return (aBlock instanceof Block); + } + + @Deprecated // why do you use Objects? And if you want to check your block to be null, check it directly! + public static boolean isBlockInvalid(Object aBlock) { + return !(aBlock instanceof Block); + } + + 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( + GT_ModHandler.getIC2Item("debug", 1), + aStack, + true); + } + + public static ItemStack updateItemStack(ItemStack aStack) { + if (isStackValid(aStack) && aStack.getItem() instanceof GT_Generic_Item) + ((GT_Generic_Item) 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 + */ + @Deprecated + public static int getTextureId(ItemStack stack) { + return getTextureId(Block.getBlockFromItem(stack.getItem()), (byte) stack.getItemDamage()); + } + + /** + * 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, GregTech_API.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, GregTech_API.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, GregTech_API.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, GregTech_API.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, GregTech_API.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, GregTech_API.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(Enchantment_Hazmat.INSTANCE.effectId); + + return tLevel != null && tLevel >= 1; + } + + public static float getHeatDamageFromItem(ItemStack aStack) { + ItemData tData = GT_OreDictUnificator.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 = GT_OreDictUnificator.getItemData(aStack); + if (tData != null && tData.hasValidMaterialData()) { + if (tData.mMaterial.mMaterial.mEnchantmentArmors instanceof Enchantment_Radioactivity) + return tData.mMaterial.mMaterial.mEnchantmentArmorsLevel; + if (tData.mMaterial.mMaterial.mEnchantmentTools instanceof Enchantment_Radioactivity) + return tData.mMaterial.mMaterial.mEnchantmentToolsLevel; + } + return EnchantmentHelper.getEnchantmentLevel(Enchantment_Radioactivity.INSTANCE.effectId, aStack); + } + + public static boolean isImmuneToBreathingGasses(EntityLivingBase aEntity) { + return isWearingFullGasHazmat(aEntity); + } + + public static boolean applyHeatDamage(EntityLivingBase entity, float damage) { + return applyHeatDamage(entity, damage, GT_DamageSources.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(GT_DamageSources.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(GT_DamageSources.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; + } + + @Deprecated + public static ItemStack setStack(Object aSetStack, Object aToStack) { + if (aSetStack instanceof ItemStack setStack && aToStack instanceof ItemStack toStack) + return setStack(setStack, toStack); + return null; + } + + 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; + } + + /** + * @deprecated use {@link #multiplyStack(int, ItemStack)} instead + */ + @Deprecated + public static ItemStack multiplyStack(long aMultiplier, Object... aStacks) { + return multiplyStack((int) aMultiplier, firstStackOrNull(aStacks)); + } + + 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; + } + + /** + * @deprecated use {@link #copyAmountUnsafe(int, ItemStack)} instead + */ + @Deprecated + public static ItemStack copyAmountUnsafe(long aAmount, Object... aStacks) { + return copyAmountUnsafe((int) aAmount, firstStackOrNull(aStacks)); + } + + /** + * 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; + } + + /** + * @deprecated use {@link #copyMetaData(int, ItemStack)} instead + */ + @Deprecated + public static ItemStack copyMetaData(long aMetaData, Object... aStacks) { + return copyMetaData((int) aMetaData, firstStackOrNull(aStacks)); + } + + 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, Object... aStacks) { + return copyAmountAndMetaData(aAmount, aMetaData, firstStackOrNull(aStacks)); + } + + /** + * @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; + } + + /** + * @deprecated use {@link #mul(int, ItemStack)} instead + */ + @Deprecated + public static ItemStack mul(long aMultiplier, Object... aStacks) { + return mul((int) aMultiplier, firstStackOrNull(aStacks)); + } + + /** + * 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 = GT_OreDictUnificator.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(GT_ItemStack.internalCopyStack(aStack, true)); + } + + public static boolean isStackInList(ItemStack aStack, Collection<GT_ItemStack> aList) { + if (aStack == null) { + return false; + } + return isStackInList(new GT_ItemStack(aStack), aList); + } + + public static boolean isStackInList(ItemStack aStack, Set<GT_ItemStack2> aList) { + if (aStack == null) { + return false; + } + return isStackInList(new GT_ItemStack2(aStack), aList); + } + + public static boolean isStackInList(GT_ItemStack aStack, Collection<GT_ItemStack> aList) { + return aStack != null + && (aList.contains(aStack) || aList.contains(new GT_ItemStack(aStack.mItem, aStack.mStackSize, W))); + } + + public static boolean isStackInList(GT_ItemStack2 aStack, Set<GT_ItemStack2> aList) { + return aStack != null + && (aList.contains(aStack) || aList.contains(new GT_ItemStack2(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) { + GT_Log.err.println("Failed to clone Map of type " + aMap.getClass()); + e.printStackTrace(GT_Log.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 && !GregTech_API.sDimensionalList.contains(aDimensionID)) + return true; + return !GregTech_API.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(GT_Log.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( + GT_Utility.trans("162", "Name: ") + EnumChatFormatting.BLUE + + ((tTileEntity instanceof IInventory inv) ? inv.getInventoryName() : tBlock.getUnlocalizedName()) + + EnumChatFormatting.RESET + + GT_Utility.trans("163", " MetaData: ") + + EnumChatFormatting.AQUA + + aWorld.getBlockMetadata(aX, aY, aZ) + + EnumChatFormatting.RESET); + tList.add( + GT_Utility.trans("164", "Hardness: ") + EnumChatFormatting.YELLOW + + tBlock.getBlockHardness(aWorld, aX, aY, aZ) + + EnumChatFormatting.RESET + + GT_Utility.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 + GT_Utility.trans("166", "Is valid Beacon Pyramid Material") + + EnumChatFormatting.RESET); + } catch (Throwable e) { + if (D1) e.printStackTrace(GT_Log.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( + GT_Utility.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) { + if (D1) e.printStackTrace(GT_Log.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) { + if (D1) e.printStackTrace(GT_Log.err); + } + return rEUAmount; + } + + private static void addPollutionInfo(ArrayList<String> tList, Chunk currentChunk) { + if (GT_Pollution.hasPollution(currentChunk)) { + tList.add( + GT_Utility.trans("202", "Pollution in Chunk: ") + EnumChatFormatting.RED + + formatNumbers(GT_Pollution.getPollution(currentChunk)) + + EnumChatFormatting.RESET + + GT_Utility.trans("203", " gibbl")); + } else { + tList.add( + EnumChatFormatting.GREEN + GT_Utility.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 + GT_Utility.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 (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) { + if (D1) e.printStackTrace(GT_Log.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( + GT_Utility.trans("187", "Type -- Crop-Name: ") + crop.getCrop() + .name() + + GT_Utility.trans("188", " Growth: ") + + crop.getGrowth() + + GT_Utility.trans("189", " Gain: ") + + crop.getGain() + + GT_Utility.trans("190", " Resistance: ") + + crop.getResistance()); + } + tList.add( + GT_Utility.trans("191", "Plant -- Fertilizer: ") + crop.getNutrientStorage() + + GT_Utility.trans("192", " Water: ") + + crop.getHydrationStorage() + + GT_Utility.trans("193", " Weed-Ex: ") + + crop.getWeedExStorage() + + GT_Utility.trans("194", " Scan-Level: ") + + crop.getScanLevel()); + tList.add( + GT_Utility.trans("195", "Environment -- Nutrients: ") + crop.getNutrients() + + GT_Utility.trans("196", " Humidity: ") + + crop.getHumidity() + + GT_Utility.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(GT_Utility.trans("198", "Attributes:") + tString.replaceFirst(",", E)); + tList.add( + GT_Utility.trans("199", "Discovered by: ") + crop.getCrop() + .discoveredBy()); + } + } + } catch (Throwable e) { + if (D1) e.printStackTrace(GT_Log.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) { + if (D1) e.printStackTrace(GT_Log.err); + } + } + + private static void addOwnerInfo(ArrayList<String> tList, TileEntity tTileEntity) { + try { + if (tTileEntity instanceof IGregTechTileEntity gtTE) { + tList.add( + GT_Utility.trans("186", "Owned by: ") + EnumChatFormatting.BLUE + + gtTE.getOwnerName() + + EnumChatFormatting.RESET); + } + } catch (Throwable e) { + if (D1) e.printStackTrace(GT_Log.err); + } + } + + private static void addEnergyContainerInfo(ArrayList<String> tList, TileEntity tTileEntity) { + try { + if (tTileEntity instanceof IBasicEnergyContainer energyContainer && energyContainer.getEUCapacity() > 0) { + tList.add( + GT_Utility.trans("179", "Max IN: ") + EnumChatFormatting.RED + + formatNumbers(energyContainer.getInputVoltage()) + + " (" + + GT_Values.VN[getTier(energyContainer.getInputVoltage())] + + ") " + + EnumChatFormatting.RESET + + GT_Utility.trans("182", " EU at ") + + EnumChatFormatting.RED + + formatNumbers(energyContainer.getInputAmperage()) + + EnumChatFormatting.RESET + + GT_Utility.trans("183", " A")); + tList.add( + GT_Utility.trans("181", "Max OUT: ") + EnumChatFormatting.RED + + formatNumbers(energyContainer.getOutputVoltage()) + + " (" + + GT_Values.VN[getTier(energyContainer.getOutputVoltage())] + + ") " + + EnumChatFormatting.RESET + + GT_Utility.trans("182", " EU at ") + + EnumChatFormatting.RED + + formatNumbers(energyContainer.getOutputAmperage()) + + EnumChatFormatting.RESET + + GT_Utility.trans("183", " A")); + tList.add( + GT_Utility.trans("184", "Energy: ") + EnumChatFormatting.GREEN + + formatNumbers(energyContainer.getStoredEU()) + + EnumChatFormatting.RESET + + " EU / " + + EnumChatFormatting.YELLOW + + formatNumbers(energyContainer.getEUCapacity()) + + EnumChatFormatting.RESET + + " EU"); + } + } catch (Throwable e) { + if (D1) e.printStackTrace(GT_Log.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) { + if (D1) e.printStackTrace(GT_Log.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( + GT_Utility.trans("178", "Progress/Load: ") + EnumChatFormatting.GREEN + + formatNumbers(progress.getProgress()) + + EnumChatFormatting.RESET + + " / " + + EnumChatFormatting.YELLOW + + formatNumbers(tValue) + + EnumChatFormatting.RESET); + } + } catch (Throwable e) { + if (D1) e.printStackTrace(GT_Log.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 + GT_Utility.trans("177", "Has Muffler Upgrade") + + EnumChatFormatting.RESET); + } + } catch (Throwable e) { + if (D1) e.printStackTrace(GT_Log.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( + GT_Utility.trans("176", "Contained Energy: ") + EnumChatFormatting.YELLOW + + formatNumbers(storage.getStored()) + + EnumChatFormatting.RESET + + " EU / " + + EnumChatFormatting.YELLOW + + formatNumbers(storage.getCapacity()) + + EnumChatFormatting.RESET + + " EU"); + } + } catch (Throwable e) { + if (D1) e.printStackTrace(GT_Log.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( + GT_Utility.trans("175", "Conduction Loss: ") + EnumChatFormatting.YELLOW + + conductor.getConductionLoss() + + EnumChatFormatting.RESET); + } + } catch (Throwable e) { + if (D1) e.printStackTrace(GT_Log.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( + GT_Utility.trans("171", "Facing: ") + EnumChatFormatting.GREEN + + wrenchable.getFacing() + + EnumChatFormatting.RESET + + GT_Utility.trans("172", " / Chance: ") + + EnumChatFormatting.YELLOW + + (wrenchable.getWrenchDropRate() * 100) + + EnumChatFormatting.RESET + + "%"); + tList.add( + wrenchable.wrenchCanRemove(aPlayer) + ? EnumChatFormatting.GREEN + GT_Utility.trans("173", "You can remove this with a Wrench") + + EnumChatFormatting.RESET + : EnumChatFormatting.RED + GT_Utility.trans("174", "You can NOT remove this with a Wrench") + + EnumChatFormatting.RESET); + } + } catch (Throwable e) { + if (D1) e.printStackTrace(GT_Log.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( + GT_Utility.trans("219", "Extended Facing: ") + EnumChatFormatting.GREEN + + tAlignment.getExtendedFacing() + + EnumChatFormatting.RESET); + } + } + } catch (Throwable e) { + if (D1) e.printStackTrace(GT_Log.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( + GT_Utility.trans("168", "Heat: ") + EnumChatFormatting.GREEN + + formatNumbers(reactor.getHeat()) + + EnumChatFormatting.RESET + + " / " + + EnumChatFormatting.YELLOW + + formatNumbers(reactor.getMaxHeat()) + + EnumChatFormatting.RESET); + tList.add( + GT_Utility.trans("169", "HEM: ") + EnumChatFormatting.YELLOW + + reactor.getHeatEffectModifier() + + EnumChatFormatting.RESET); + } + } catch (Throwable e) { + if (D1) e.printStackTrace(GT_Log.err); + } + return rEUAmount; + } + + public static String trans(String aKey, String aEnglish) { + return GT_LanguageManager.addStringLocalization("Interaction_DESCRIPTION_Index_" + aKey, aEnglish); + } + + public static String getTrans(String aKey) { + return GT_LanguageManager.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 && GT_OreDictUnificator.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; + } + + /** + * @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 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); + } + } + + /** + * THIS IS BULLSHIT!!! WHY DO I HAVE TO DO THIS SHIT JUST TO HAVE ENCHANTS PROPERLY!?! + */ + public static class GT_EnchantmentHelper { + + private static final BullshitIteratorA mBullshitIteratorA = new BullshitIteratorA(); + private static final BullshitIteratorB mBullshitIteratorB = new BullshitIteratorB(); + + private static void applyBullshit(IBullshit aBullshitModifier, ItemStack aStack) { + if (aStack != null) { + NBTTagList nbttaglist = aStack.getEnchantmentTagList(); + if (nbttaglist != null) { + try { + for (int i = 0; i < nbttaglist.tagCount(); ++i) { + short short1 = nbttaglist.getCompoundTagAt(i) + .getShort("id"); + short short2 = nbttaglist.getCompoundTagAt(i) + .getShort("lvl"); + if (Enchantment.enchantmentsList[short1] != null) + aBullshitModifier.calculateModifier(Enchantment.enchantmentsList[short1], short2); + } + } catch (Throwable e) { + /**/ + } + } + } + } + + private static void applyArrayOfBullshit(IBullshit aBullshitModifier, ItemStack[] aStacks) { + for (ItemStack itemstack : aStacks) { + applyBullshit(aBullshitModifier, itemstack); + } + } + + public static void applyBullshitA(EntityLivingBase aPlayer, Entity aEntity, ItemStack aStack) { + mBullshitIteratorA.mPlayer = aPlayer; + mBullshitIteratorA.mEntity = aEntity; + if (aPlayer != null) applyArrayOfBullshit(mBullshitIteratorA, aPlayer.getLastActiveItems()); + if (aStack != null) applyBullshit(mBullshitIteratorA, aStack); + } + + public static void applyBullshitB(EntityLivingBase aPlayer, Entity aEntity, ItemStack aStack) { + mBullshitIteratorB.mPlayer = aPlayer; + mBullshitIteratorB.mEntity = aEntity; + if (aPlayer != null) applyArrayOfBullshit(mBullshitIteratorB, aPlayer.getLastActiveItems()); + if (aStack != null) applyBullshit(mBullshitIteratorB, aStack); + } + + interface IBullshit { + + void calculateModifier(Enchantment aEnchantment, int aLevel); + } + + static final class BullshitIteratorA implements IBullshit { + + public EntityLivingBase mPlayer; + public Entity mEntity; + + BullshitIteratorA() {} + + @Override + public void calculateModifier(Enchantment aEnchantment, int aLevel) { + aEnchantment.func_151367_b(mPlayer, mEntity, aLevel); + } + } + + static final class BullshitIteratorB implements IBullshit { + + public EntityLivingBase mPlayer; + public Entity mEntity; + + BullshitIteratorB() {} + + @Override + public void calculateModifier(Enchantment aEnchantment, int aLevel) { + aEnchantment.func_151368_a(mPlayer, mEntity, aLevel); + } + } + } + + 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 GT_OreDictUnificator.getAssociation(aStack) != null + && GT_OreDictUnificator.getAssociation(aStack).mMaterial.mMaterial.equals(aMaterials); + } + + public static boolean isPartOfOrePrefix(ItemStack aStack, OrePrefixes aPrefix) { + return GT_OreDictUnificator.getAssociation(aStack) != null + && GT_OreDictUnificator.getAssociation(aStack).mPrefix.equals(aPrefix); + } + + public static final ImmutableSet<String> ORE_BLOCK_CLASSES = ImmutableSet.of( + "com.github.bartimaeusnek.bartworks.system.material.BW_MetaGenerated_Ores", + "com.github.bartimaeusnek.bartworks.system.material.BW_MetaGenerated_SmallOres", + "gtPlusPlus.core.block.base.BlockBaseOre"); + + public static boolean isOre(Block aBlock, int aMeta) { + return (aBlock instanceof GT_Block_Ores_Abstract) || isOre(new ItemStack(aBlock, 1, aMeta)) + || ORE_BLOCK_CLASSES.contains( + aBlock.getClass() + .getName()); + } + + public static boolean isOre(ItemStack aStack) { + int tItem = GT_Utility.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 = GT_OreDictUnificator + .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<GT_Recipe> 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 = GT_OreDictUnificator.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 GT_MetaGenerated_Tool); + + return Optional.of( + new GT_Recipe( + false, + new ItemStack[] { output }, + inputs.toArray(new ItemStack[0]), + null, + null, + null, + null, + 300, + 30, + 0)); + } + + public static Optional<GT_Recipe> 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 = GT_OreDictUnificator.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 GT_MetaGenerated_Tool); + + return Optional.of( + new GT_Recipe( + 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 GT_Utility.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; + } + + /** + * @deprecated typo in method name. use {@link #isAnyIntegratedCircuit(ItemStack)} instead. + */ + @Deprecated + public static boolean checkIfSameIntegratedCircuit(ItemStack itemStack) { + if (itemStack == null) return false; + for (int i = 0; i < 25; i++) if (itemStack.isItemEqual(GT_Utility.getIntegratedCircuit(i))) return true; + return false; + } + + 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_GT_Utility_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_GT_Utility_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_GT_Utility_ItemId(item, metaData, nbt); + } + + /** + * This method stores metadata as wildcard and NBT as null. + */ + public static ItemId createAsWildcard(ItemStack itemStack) { + return new AutoValue_GT_Utility_ItemId(itemStack.getItem(), W, null); + } + + /** + * This method stores NBT as null. + */ + public static ItemId createWithoutNBT(ItemStack itemStack) { + return new AutoValue_GT_Utility_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_GT_Utility_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_GT_Utility_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; + } + + 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; + GT_Recipe tFuel = RecipeMaps.plasmaFuels.getBackend() + .findFuel(aLiquid); + if (tFuel != null) return tFuel.mSpecialValue; + return 0; + } + + public static MovingObjectPosition getPlayerLookingTarget() { + // Basically copied from waila, thanks Caedis for such challenge + Minecraft mc = Minecraft.getMinecraft(); + EntityLivingBase viewpoint = mc.renderViewEntity; + if (viewpoint == null) return null; + + float reachDistance = mc.playerController.getBlockReachDistance(); + Vec3 posVec = viewpoint.getPosition(0); + Vec3 lookVec = viewpoint.getLook(0); + Vec3 modifiedPosVec = posVec + .addVector(lookVec.xCoord * reachDistance, lookVec.yCoord * reachDistance, lookVec.zCoord * reachDistance); + + return viewpoint.worldObj.rayTraceBlocks(posVec, modifiedPosVec); + } +} diff --git a/src/main/java/gregtech/api/util/GT_UtilityClient.java b/src/main/java/gregtech/api/util/GT_UtilityClient.java new file mode 100644 index 0000000000..398c1f6b41 --- /dev/null +++ b/src/main/java/gregtech/api/util/GT_UtilityClient.java @@ -0,0 +1,52 @@ +package gregtech.api.util; + +import java.lang.reflect.Field; +import java.util.List; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.item.EnumRarity; +import net.minecraft.item.ItemStack; +import net.minecraft.util.EnumChatFormatting; + +import com.google.common.collect.Lists; + +import cpw.mods.fml.relauncher.ReflectionHelper; + +public class GT_UtilityClient { + + private static final Field isDrawingField = ReflectionHelper + .findField(Tessellator.class, "isDrawing", "field_78415_z"); + + public static boolean isDrawing(Tessellator tess) { + try { + return isDrawingField.getBoolean(tess); + } catch (IllegalAccessException e) { + e.printStackTrace(); + return false; + } + } + + public static List<String> getTooltip(ItemStack aStack, boolean aGuiStyle) { + try { + List<String> tooltip = aStack.getTooltip( + Minecraft.getMinecraft().thePlayer, + Minecraft.getMinecraft().gameSettings.advancedItemTooltips); + if (aGuiStyle) { + tooltip.set( + 0, + (aStack.getRarity() == null ? EnumRarity.common : aStack.getRarity()).rarityColor + tooltip.get(0)); + for (int i = 1; i < tooltip.size(); i++) { + tooltip.set(i, EnumChatFormatting.GRAY + tooltip.get(i)); + } + } + return tooltip; + } catch (RuntimeException e) { + // Collections.singletonList() can not be added to. we don't want that + if (aGuiStyle) return Lists.newArrayList( + (aStack.getRarity() == null ? EnumRarity.common : aStack.getRarity()).rarityColor + + aStack.getDisplayName()); + return Lists.newArrayList(aStack.getDisplayName()); + } + } +} diff --git a/src/main/java/gregtech/api/util/GT_Waila.java b/src/main/java/gregtech/api/util/GT_Waila.java new file mode 100644 index 0000000000..aaa68ba4c7 --- /dev/null +++ b/src/main/java/gregtech/api/util/GT_Waila.java @@ -0,0 +1,23 @@ +package gregtech.api.util; + +public abstract class GT_Waila { + + public static String getMachineProgressString(boolean isActive, int maxProgresstime, int progresstime) { + return getMachineProgressString(isActive, (long) maxProgresstime, (long) progresstime); + } + + public static String getMachineProgressString(boolean isActive, long maxProgresstime, long progresstime) { + + if (!isActive) return "Idle"; + + StringBuilder ret = new StringBuilder("In progress: ") + .append(String.format("%,.2f", (double) progresstime / 20)) + .append("s / ") + .append(String.format("%,.2f", (double) maxProgresstime / 20)) + .append("s (") + .append(GT_Utility.formatNumbers((Math.round((double) progresstime / maxProgresstime * 1000) / 10.0))) + .append("%)"); + + return ret.toString(); + } +} diff --git a/src/main/java/gregtech/api/util/GasSpargingRecipe.java b/src/main/java/gregtech/api/util/GasSpargingRecipe.java new file mode 100644 index 0000000000..667cc78d85 --- /dev/null +++ b/src/main/java/gregtech/api/util/GasSpargingRecipe.java @@ -0,0 +1,103 @@ +package gregtech.api.util; + +import net.minecraftforge.fluids.FluidStack; + +import gtPlusPlus.api.objects.data.AutoMap; +import gtPlusPlus.core.util.data.ArrayUtils; +import gtPlusPlus.core.util.minecraft.ItemUtils; +import gtPlusPlus.core.util.minecraft.MaterialUtils; + +public class GasSpargingRecipe implements Comparable<GasSpargingRecipe> { + + public final FluidStack mInputGas; + public final FluidStack mInputSpentFuel; + public final FluidStack mOutputSpargedFuel; + public final int[] mMaxOutputQuantity; + public final FluidStack[] mFluidInputs; + public final FluidStack[] mFluidOutputs; + public final int mDuration; + public final int mEUt; + + public GasSpargingRecipe(FluidStack aSpargeGas, FluidStack aSpentFuel, FluidStack aSpargedFuel, + FluidStack[] aOutputs, int[] aMaxOutputQuantity) { + mInputGas = aSpargeGas; + mInputSpentFuel = aSpentFuel; + mOutputSpargedFuel = aSpargedFuel; + mFluidInputs = new FluidStack[] { mInputGas, mInputSpentFuel }; + aOutputs = ArrayUtils.insertElementAtIndex(aOutputs, 0, aSpargeGas); + aOutputs = ArrayUtils.insertElementAtIndex(aOutputs, 1, aSpargedFuel); + mFluidOutputs = aOutputs; + mMaxOutputQuantity = aMaxOutputQuantity; + mDuration = 500; + mEUt = MaterialUtils.getVoltageForTier(5); + } + + @Override + public boolean equals(Object o) { + if (o instanceof GasSpargingRecipe i) { + if (this.mInputGas.equals(i.mInputGas) && this.mInputSpentFuel.equals(i.mInputSpentFuel)) { + return true; + } + } + return false; + } + + public int getMaxOutput(int aIndex) { + if (aIndex == 0) { + return mInputGas.amount * 100; + } else if (aIndex == 1) { + return mOutputSpargedFuel.amount * 100; + } + aIndex -= 2; + if ((aIndex < 0) || (aIndex >= this.mMaxOutputQuantity.length)) { + return 10000; + } + return this.mMaxOutputQuantity[aIndex]; + } + + public boolean isValid() { + if (mInputGas == null || mInputGas.amount <= 0 + || mInputSpentFuel == null + || mInputSpentFuel.amount <= 0 + || mFluidOutputs == null + || mFluidOutputs.length < 1 + || mMaxOutputQuantity == null + || mMaxOutputQuantity.length < 1 + || mFluidOutputs.length != mMaxOutputQuantity.length) { + return false; + } + return true; + } + + public boolean containsInputs(FluidStack aSpargeGas, FluidStack aSpentFuel) { + if (aSpargeGas != null && aSpargeGas.getFluid() + .equals(this.mInputGas.getFluid())) { + if (aSpentFuel != null && aSpentFuel.getFluid() + .equals(this.mInputSpentFuel.getFluid())) { + return true; + } + } + return false; + } + + @Override + public int compareTo(GasSpargingRecipe o) { + if (o.mFluidOutputs.length > this.mFluidOutputs.length) { + return 1; + } else if (o.mFluidOutputs.length == this.mFluidOutputs.length) { + return 0; + } else { + return -1; + } + } + + public String[] getRecipeInfo() { + AutoMap<String> result = new AutoMap<>(); + result.put("Input " + ItemUtils.getArrayStackNames(mFluidInputs)); + result.put("Output " + ItemUtils.getArrayStackNames(mFluidOutputs)); + result.put("Duration: " + mDuration); + result.put("EU/t: " + mEUt); + String s[] = result.toArray(); + return s; + } +} diff --git a/src/main/java/gregtech/api/util/GasSpargingRecipeMap.java b/src/main/java/gregtech/api/util/GasSpargingRecipeMap.java new file mode 100644 index 0000000000..6dcc7721e0 --- /dev/null +++ b/src/main/java/gregtech/api/util/GasSpargingRecipeMap.java @@ -0,0 +1,45 @@ +package gregtech.api.util; + +import static gregtech.api.enums.Mods.GregTech; + +import net.minecraftforge.fluids.FluidStack; + +import gtPlusPlus.api.objects.data.AutoMap; + +public class GasSpargingRecipeMap extends AutoMap<GasSpargingRecipe> { + + public static final AutoMap<GasSpargingRecipe> mRecipes = new AutoMap<>(); + public static final String mUnlocalizedName = "gtpp.recipe.lftr.sparging"; + public static final String mNEIName = mUnlocalizedName; + public static final String mNEIDisplayName = "LFTR Gas Sparging"; + public static final String mNEIGUIPath = GregTech.getResourcePath("textures", "gui/basicmachines/FissionFuel.png"); + + public static boolean addRecipe(FluidStack aSpargeGas, FluidStack aSpentFuel, FluidStack aSpargedFuel, + FluidStack[] aOutputs, int[] aMaxOutputs) { + if (aSpargeGas == null || aSpargeGas.amount <= 0 + || aSpentFuel == null + || aSpentFuel.amount <= 0 + || aSpargedFuel == null + || aSpargedFuel.amount <= 0 + || aOutputs == null + || aOutputs.length < 1 + || aMaxOutputs == null + || aMaxOutputs.length < 1 + || aOutputs.length != aMaxOutputs.length) { + return false; + } + int aMapSize = mRecipes.size(); + GasSpargingRecipe aRecipe = new GasSpargingRecipe(aSpargeGas, aSpentFuel, aSpargedFuel, aOutputs, aMaxOutputs); + mRecipes.put(aRecipe); + return mRecipes.size() > aMapSize; + } + + public static GasSpargingRecipe findRecipe(FluidStack aSpargeGas, FluidStack aSpentFuel) { + for (GasSpargingRecipe aRecipe : mRecipes) { + if (aRecipe.containsInputs(aSpargeGas, aSpentFuel)) { + return aRecipe; + } + } + return null; + } +} diff --git a/src/main/java/gregtech/api/util/HotFuel.java b/src/main/java/gregtech/api/util/HotFuel.java new file mode 100644 index 0000000000..6054a57b84 --- /dev/null +++ b/src/main/java/gregtech/api/util/HotFuel.java @@ -0,0 +1,40 @@ +package gregtech.api.util; + +import net.minecraft.item.ItemStack; +import net.minecraftforge.fluids.FluidStack; + +import gtPlusPlus.api.recipe.GTPPRecipeMaps; + +public class HotFuel { + + public static void addNewHotFuel(FluidStack aInput1, FluidStack aOutput1, ItemStack[] outputItems, int[] chances, + int aSpecialValue) { + GTPPRecipeMaps.thermalBoilerRecipes.addRecipe( + true, + null, + outputItems, + null, + chances, + new FluidStack[] { aInput1 }, + new FluidStack[] { aOutput1 }, + 1, // 1 Tick + 0, // No Eu produced + aSpecialValue // Magic Number + ); + } + + public static void addNewHotFuel(FluidStack aInput1, FluidStack aOutput1, FluidStack aOutput2, int aSpecialValue) { + GTPPRecipeMaps.thermalBoilerRecipes.addRecipe( + false, + null, + null, + null, + null, + new FluidStack[] { aInput1 }, + new FluidStack[] { aOutput1, aOutput2 }, + 20, // 1 Second + 0, // No Eu produced + aSpecialValue // Magic Number + ); + } +} diff --git a/src/main/java/gregtech/api/util/IGT_HatchAdder.java b/src/main/java/gregtech/api/util/IGT_HatchAdder.java new file mode 100644 index 0000000000..21796f172e --- /dev/null +++ b/src/main/java/gregtech/api/util/IGT_HatchAdder.java @@ -0,0 +1,28 @@ +package gregtech.api.util; + +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; + +public interface IGT_HatchAdder<T> { + + /** + * Callback to add hatch, needs to check if hatch is valid (and add it) + * + * @param iGregTechTileEntity hatch + * @param aShort requested texture index, or null if not... + * @return managed to add hatch (structure still valid) + */ + boolean apply(T t, IGregTechTileEntity iGregTechTileEntity, Short aShort); + + /** + * hack to work around java generic issues. + */ + @SuppressWarnings("unchecked") + default <T2 extends T> IGT_HatchAdder<T2> rebrand() { + return (IGT_HatchAdder<T2>) this; + } + + default IGT_HatchAdder<T> orElse(IGT_HatchAdder<? super T> fallback) { + return (t, iGregTechTileEntity, aShort) -> IGT_HatchAdder.this.apply(t, iGregTechTileEntity, aShort) + || fallback.apply(t, iGregTechTileEntity, aShort); + } +} diff --git a/src/main/java/gregtech/api/util/ISerializableObject.java b/src/main/java/gregtech/api/util/ISerializableObject.java new file mode 100644 index 0000000000..7f1626bac5 --- /dev/null +++ b/src/main/java/gregtech/api/util/ISerializableObject.java @@ -0,0 +1,159 @@ +package gregtech.api.util; + +import java.io.IOException; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.CompressedStreamTools; +import net.minecraft.nbt.NBTBase; +import net.minecraft.nbt.NBTSizeTracker; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagInt; + +import com.google.common.io.ByteArrayDataInput; + +import io.netty.buffer.ByteBuf; + +/** + * We could well have used {@link java.io.Serializable}, but that's too slow and should generally be avoided + * + * @author glease + */ +public interface ISerializableObject { + + @Nonnull + ISerializableObject copy(); + + /** + * If you are overriding this, you must <b>NOT</b> return {@link NBTTagInt} here! That return type is how we tell + * that we are loading legacy data, and only {@link LegacyCoverData} is allowed to return it. You probably want to + * return {@link NBTTagCompound} anyway. + */ + @Nonnull + NBTBase saveDataToNBT(); + + /** + * Write data to given ByteBuf The data saved this way is intended to be stored for short amount of time over + * network. DO NOT store it to disks. + */ + // the NBT is an unfortunate piece of tech. everything uses it but its API is not as efficient as could be + void writeToByteBuf(ByteBuf aBuf); + + void loadDataFromNBT(NBTBase aNBT); + + /** + * Read data from given parameter and return this. The data read this way is intended to be stored for short amount + * of time over network. + * + * @param aPlayer the player who is sending this packet to server. null if it's client reading data. + */ + // the NBT is an unfortunate piece of tech. everything uses it but its API is not as efficient as could be + @Nonnull + ISerializableObject readFromPacket(ByteArrayDataInput aBuf, @Nullable EntityPlayerMP aPlayer); + + /** + * Reverse engineered and adapted {@link cpw.mods.fml.common.network.ByteBufUtils#readTag(ByteBuf)} Given buffer + * must contain a serialized NBTTagCompound in minecraft encoding + */ + static NBTTagCompound readCompoundTagFromGreggyByteBuf(ByteArrayDataInput aBuf) { + short size = aBuf.readShort(); + if (size < 0) return null; + else { + byte[] buf = new byte[size]; // this is shit, how many copies have we been doing? + aBuf.readFully(buf); + try { + return CompressedStreamTools.func_152457_a(buf, new NBTSizeTracker(2097152L)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + + /** + * Reverse engineered and adapted {@link cpw.mods.fml.common.network.ByteBufUtils#readItemStack(ByteBuf)} Given + * buffer must contain a serialized ItemStack in minecraft encoding + */ + static ItemStack readItemStackFromGreggyByteBuf(ByteArrayDataInput aBuf) { + ItemStack stack = null; + short id = aBuf.readShort(); + if (id >= 0) { + byte size = aBuf.readByte(); + short meta = aBuf.readShort(); + stack = new ItemStack(Item.getItemById(id), size, meta); + stack.stackTagCompound = readCompoundTagFromGreggyByteBuf(aBuf); + } + return stack; + } + + final class LegacyCoverData implements ISerializableObject { + + private int mData; + + public LegacyCoverData() {} + + public LegacyCoverData(int mData) { + this.mData = mData; + } + + @Override + @Nonnull + public ISerializableObject copy() { + return new LegacyCoverData(mData); + } + + @Override + @Nonnull + public NBTBase saveDataToNBT() { + return new NBTTagInt(mData); + } + + @Override + public void writeToByteBuf(ByteBuf aBuf) { + aBuf.writeInt(mData); + } + + @Override + public void loadDataFromNBT(NBTBase aNBT) { + mData = aNBT instanceof NBTTagInt ? ((NBTTagInt) aNBT).func_150287_d() : 0; + } + + @Override + @Nonnull + public ISerializableObject readFromPacket(ByteArrayDataInput aBuf, EntityPlayerMP aPlayer) { + mData = aBuf.readInt(); + return this; + } + + public int get() { + return mData; + } + + public void set(int mData) { + this.mData = mData; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + LegacyCoverData that = (LegacyCoverData) o; + + return mData == that.mData; + } + + @Override + public int hashCode() { + return mData; + } + + @Override + public String toString() { + return String.valueOf(mData); + } + } +} diff --git a/src/main/java/gregtech/api/util/LightingHelper.java b/src/main/java/gregtech/api/util/LightingHelper.java new file mode 100644 index 0000000000..ad2510e937 --- /dev/null +++ b/src/main/java/gregtech/api/util/LightingHelper.java @@ -0,0 +1,1434 @@ +/* + * LightingHelper - Derived and adapted from @Mineshopper / carpentersblocks Copyright (c) 2013-2021. This library is + * free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation version 2.1 of the License. This library is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of + * the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package gregtech.api.util; + +import net.minecraft.block.Block; +import net.minecraft.client.renderer.EntityRenderer; +import net.minecraft.client.renderer.RenderBlocks; +import net.minecraft.client.renderer.Tessellator; +import net.minecraftforge.common.util.ForgeDirection; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; + +@SuppressWarnings("unused") +@SideOnly(Side.CLIENT) +public class LightingHelper { + + public static final int NORMAL_BRIGHTNESS = 0xff00ff; + public static final int MAX_BRIGHTNESS = 0xf000f0; + public static final float NO_Z_FIGHT_OFFSET = 1.0F / 1024.0F; + protected static final float[] LIGHTNESS = { 0.5F, 1.0F, 0.8F, 0.8F, 0.6F, 0.6F }; + private final RenderBlocks renderBlocks; + /** + * Brightness for side. + */ + private int brightness; + /** + * Ambient occlusion values for all four corners of side. + */ + private float aoTopLeft, aoBottomLeft, aoBottomRight, aoTopRight; + + private boolean hasLightnessOverride; + private float lightnessOverride; + private boolean hasBrightnessOverride; + private int brightnessOverride; + private boolean hasColorOverride; + private int colorOverride = 0xffffff; + + /** + * Class constructor specifying the {@link RenderBlocks}. + * + * @param renderBlocks the {@link RenderBlocks} + */ + public LightingHelper(RenderBlocks renderBlocks) { + this.renderBlocks = renderBlocks; + if (renderBlocks.useInventoryTint) { + // Block will be rendered in an inventory, so it needs its lightness maxed + setLightnessOverride(1.0F); + } + } + + /** + * Gets average brightness from two brightness values. + * + * @param brightnessA the first brightness value + * @param brightnessB the second brightness value + * @return the mixed brightness + */ + public static int getAverageBrightness(int brightnessA, int brightnessB) { + int sectionA1 = brightnessA >> 16 & 0xff; + int sectionA2 = brightnessA & 255; + + int sectionB1 = brightnessB >> 16 & 0xff; + int sectionB2 = brightnessB & 255; + + int difference1 = (int) ((sectionA1 + sectionB1) / 2.0F); + int difference2 = (int) ((sectionA2 + sectionB2) / 2.0F); + + return difference1 << 16 | difference2; + } + + /** + * Gets rgb color from RGBA array. + * + * @param color the integer color + * @return a float array with rgb values + */ + public static float[] getRGB(short[] color) { + float red = color[0] / 255.0F; + float green = color[1] / 255.0F; + float blue = color[2] / 255.0F; + + return new float[] { red, green, blue }; + } + + /** + * Clears brightness override. + */ + public void clearBrightnessOverride() { + hasBrightnessOverride = false; + } + + /** + * Clears color override. + */ + public void clearColorOverride() { + hasColorOverride = false; + } + + /** + * Clears lightness override. + */ + public void clearLightnessOverride() { + hasLightnessOverride = false; + } + + /** + * @return the Ambient Occlusion for Bottom-Left corner + */ + public float getAoBottomLeft() { + return aoBottomLeft; + } + + /** + * @return the Ambient Occlusion for Bottom-Right corner + */ + public float getAoBottomRight() { + return aoBottomRight; + } + + /** + * @return the Ambient Occlusion for Top-Left corner + */ + public float getAoTopLeft() { + return aoTopLeft; + } + + /** + * @return the Ambient Occlusion for Top-Right corner + */ + public float getAoTopRight() { + return aoTopRight; + } + + /** + * Sets brightness override. + * + * @param brightness the brightness override + * @return the {@link LightingHelper} + */ + public LightingHelper setBrightnessOverride(int brightness) { + hasBrightnessOverride = true; + brightnessOverride = brightness; + return this; + } + + public LightingHelper setColorOverride(short[] color) { + return setColorOverride(getColor(color)); + } + + /** + * Sets color override. + * + * @param color the color override + * @return the {@link LightingHelper} + */ + public LightingHelper setColorOverride(int color) { + hasColorOverride = true; + colorOverride = color; + return this; + } + + /** + * Gets int color from RGBA array. + * + * @param rgba the short RGBA color array + * @return int color + */ + public static int getColor(short[] rgba) { + return (rgba[2] & 0xff) | (rgba[1] & 0xff) << 8 | (rgba[0] & 0xff) << 16; + } + + /** + * Sets lightness override. + * + * @param lightness the lightness override + * @return the {@link LightingHelper} + */ + public LightingHelper setLightnessOverride(float lightness) { + hasLightnessOverride = true; + lightnessOverride = lightness; + return this; + } + + /** + * Sets up the color using lightness, brightness, and the primary color value (usually the dye color) for the side. + * + * @param side the side + * @param rgba the primary short[] RGBA color array + */ + public void setupColor(ForgeDirection side, short[] rgba) { + setupColor(side, getColor(rgba)); + } + + /** + * Sets up the color using lightness, brightness, and the primary color value (usually the dye color) for the side. + * + * @param side the side + * @param hexColor the primary color + */ + public void setupColor(ForgeDirection side, int hexColor) { + Tessellator tessellator = Tessellator.instance; + float lightness = hasLightnessOverride ? lightnessOverride : LIGHTNESS[side.ordinal()]; + float[] rgb = getRGB(hexColor); + + if (hasColorOverride && !renderBlocks.hasOverrideBlockTexture()) { + rgb = getRGB(colorOverride); + } + + applyAnaglyph(rgb); + + if (renderBlocks.enableAO) { + tessellator.setBrightness(hasBrightnessOverride ? brightnessOverride : brightness); + + if (renderBlocks.hasOverrideBlockTexture()) { + + renderBlocks.colorRedTopLeft = renderBlocks.colorRedBottomLeft = renderBlocks.colorRedBottomRight = renderBlocks.colorRedTopRight = rgb[0]; + renderBlocks.colorGreenTopLeft = renderBlocks.colorGreenBottomLeft = renderBlocks.colorGreenBottomRight = renderBlocks.colorGreenTopRight = rgb[1]; + renderBlocks.colorBlueTopLeft = renderBlocks.colorBlueBottomLeft = renderBlocks.colorBlueBottomRight = renderBlocks.colorBlueTopRight = rgb[2]; + + } else { + + renderBlocks.colorRedTopLeft = renderBlocks.colorRedBottomLeft = renderBlocks.colorRedBottomRight = renderBlocks.colorRedTopRight = rgb[0] + * lightness; + renderBlocks.colorGreenTopLeft = renderBlocks.colorGreenBottomLeft = renderBlocks.colorGreenBottomRight = renderBlocks.colorGreenTopRight = rgb[1] + * lightness; + renderBlocks.colorBlueTopLeft = renderBlocks.colorBlueBottomLeft = renderBlocks.colorBlueBottomRight = renderBlocks.colorBlueTopRight = rgb[2] + * lightness; + + renderBlocks.colorRedTopLeft *= aoTopLeft; + renderBlocks.colorGreenTopLeft *= aoTopLeft; + renderBlocks.colorBlueTopLeft *= aoTopLeft; + renderBlocks.colorRedBottomLeft *= aoBottomLeft; + renderBlocks.colorGreenBottomLeft *= aoBottomLeft; + renderBlocks.colorBlueBottomLeft *= aoBottomLeft; + renderBlocks.colorRedBottomRight *= aoBottomRight; + renderBlocks.colorGreenBottomRight *= aoBottomRight; + renderBlocks.colorBlueBottomRight *= aoBottomRight; + renderBlocks.colorRedTopRight *= aoTopRight; + renderBlocks.colorGreenTopRight *= aoTopRight; + renderBlocks.colorBlueTopRight *= aoTopRight; + } + + } else { + + if (hasBrightnessOverride) tessellator.setBrightness(brightnessOverride); + tessellator.setColorOpaque_F(rgb[0] * lightness, rgb[1] * lightness, rgb[2] * lightness); + } + } + + /** + * Gets rgb color from integer. + * + * @param color the integer color + * @return a float array with rgb values + */ + public static float[] getRGB(int color) { + float red = (color >> 16 & 0xff) / 255.0F; + float green = (color >> 8 & 0xff) / 255.0F; + float blue = (color & 0xff) / 255.0F; + + return new float[] { red, green, blue }; + } + + /** + * Will apply anaglyph color multipliers to RGB float array. + * <p> + * If {@link EntityRenderer#anaglyphEnable} is false, will do nothing. + * + * @param rgb array containing red, green and blue float values + */ + public void applyAnaglyph(float[] rgb) { + if (EntityRenderer.anaglyphEnable) { + rgb[0] = (rgb[0] * 30.0F + rgb[1] * 59.0F + rgb[2] * 11.0F) / 100.0F; + rgb[1] = (rgb[0] * 30.0F + rgb[1] * 70.0F) / 100.0F; + rgb[2] = (rgb[0] * 30.0F + rgb[2] * 70.0F) / 100.0F; + } + } + + /** + * Gets mixed ambient occlusion value from two inputs, with a ratio applied to the final result. + * + * @param ao1 the first ambient occlusion value + * @param ao2 the second ambient occlusion value + * @param ratio the ratio for mixing + * @return the mixed red, green, blue float values + */ + public static float getMixedAo(float ao1, float ao2, double ratio) { + float diff = (float) (Math.abs(ao1 - ao2) * (1.0F - ratio)); + + return ao1 > ao2 ? ao1 - diff : ao1 + diff; + } + + /** + * @see #setupLightingXNeg(Block, int, int, int) + * @see #setupLightingYNeg(Block, int, int, int) + * @see #setupLightingZNeg(Block, int, int, int) + * @see #setupLightingXPos(Block, int, int, int) + * @see #setupLightingYPos(Block, int, int, int) + * @see #setupLightingZPos(Block, int, int, int) + */ + public LightingHelper setupLighting(Block block, int x, int y, int z, ForgeDirection facing) { + return switch (facing) { + case DOWN -> setupLightingYNeg(block, x, y, z); + case UP -> setupLightingYPos(block, x, y, z); + case NORTH -> setupLightingZNeg(block, x, y, z); + case SOUTH -> setupLightingZPos(block, x, y, z); + case WEST -> setupLightingXNeg(block, x, y, z); + case EAST -> setupLightingXPos(block, x, y, z); + default -> throw new IllegalArgumentException("Unknown side: " + facing); + }; + } + + /** + * Sets up lighting for the West face and returns the {@link LightingHelper}. + * <p> + * This is a consolidated <code>method</code> that sets side shading with respect to the following attributes: + * <p> + * <ul> + * <li>{@link RenderBlocks#enableAO}</li> + * <li>{@link RenderBlocks#partialRenderBounds}</li> + * </ul> + * + * @param block the block {@link Block} + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate + * @return the {@link LightingHelper} + */ + public LightingHelper setupLightingXNeg(Block block, int x, int y, int z) { + + if (renderBlocks.enableAO) { + + int xOffset = renderBlocks.renderMinX > 0.0F + NO_Z_FIGHT_OFFSET ? x : x - 1; + + int mixedBrightness = block.getMixedBrightnessForBlock(renderBlocks.blockAccess, xOffset, y, z); + brightness = mixedBrightness; + + float ratio = (float) (1.0F - renderBlocks.renderMinX); + float aoLightValue = renderBlocks.blockAccess.getBlock(x - 1, y, z) + .getAmbientOcclusionLightValue(); + + renderBlocks.aoBrightnessXYNN = block + .getMixedBrightnessForBlock(renderBlocks.blockAccess, xOffset, y - 1, z); + renderBlocks.aoBrightnessXZNN = block + .getMixedBrightnessForBlock(renderBlocks.blockAccess, xOffset, y, z - 1); + renderBlocks.aoBrightnessXZNP = block + .getMixedBrightnessForBlock(renderBlocks.blockAccess, xOffset, y, z + 1); + renderBlocks.aoBrightnessXYNP = block + .getMixedBrightnessForBlock(renderBlocks.blockAccess, xOffset, y + 1, z); + renderBlocks.aoBrightnessXYZNNN = block + .getMixedBrightnessForBlock(renderBlocks.blockAccess, xOffset, y - 1, z - 1); + renderBlocks.aoBrightnessXYZNNP = block + .getMixedBrightnessForBlock(renderBlocks.blockAccess, xOffset, y - 1, z + 1); + renderBlocks.aoBrightnessXYZNPN = block + .getMixedBrightnessForBlock(renderBlocks.blockAccess, xOffset, y + 1, z - 1); + renderBlocks.aoBrightnessXYZNPP = block + .getMixedBrightnessForBlock(renderBlocks.blockAccess, xOffset, y + 1, z + 1); + renderBlocks.aoLightValueScratchXYNN = getMixedAo( + renderBlocks.blockAccess.getBlock(x - 1, y - 1, z) + .getAmbientOcclusionLightValue(), + renderBlocks.blockAccess.getBlock(x, y - 1, z) + .getAmbientOcclusionLightValue(), + ratio); + renderBlocks.aoLightValueScratchXZNN = getMixedAo( + renderBlocks.blockAccess.getBlock(x - 1, y, z - 1) + .getAmbientOcclusionLightValue(), + renderBlocks.blockAccess.getBlock(x, y, z - 1) + .getAmbientOcclusionLightValue(), + ratio); + renderBlocks.aoLightValueScratchXZNP = getMixedAo( + renderBlocks.blockAccess.getBlock(x - 1, y, z + 1) + .getAmbientOcclusionLightValue(), + renderBlocks.blockAccess.getBlock(x, y, z + 1) + .getAmbientOcclusionLightValue(), + ratio); + renderBlocks.aoLightValueScratchXYNP = getMixedAo( + renderBlocks.blockAccess.getBlock(x - 1, y + 1, z) + .getAmbientOcclusionLightValue(), + renderBlocks.blockAccess.getBlock(x, y + 1, z) + .getAmbientOcclusionLightValue(), + ratio); + renderBlocks.aoLightValueScratchXYZNNN = getMixedAo( + renderBlocks.blockAccess.getBlock(x - 1, y - 1, z - 1) + .getAmbientOcclusionLightValue(), + renderBlocks.blockAccess.getBlock(x, y - 1, z - 1) + .getAmbientOcclusionLightValue(), + ratio); + renderBlocks.aoLightValueScratchXYZNNP = getMixedAo( + renderBlocks.blockAccess.getBlock(x - 1, y - 1, z + 1) + .getAmbientOcclusionLightValue(), + renderBlocks.blockAccess.getBlock(x, y - 1, z + 1) + .getAmbientOcclusionLightValue(), + ratio); + renderBlocks.aoLightValueScratchXYZNPN = getMixedAo( + renderBlocks.blockAccess.getBlock(x - 1, y + 1, z - 1) + .getAmbientOcclusionLightValue(), + renderBlocks.blockAccess.getBlock(x, y + 1, z - 1) + .getAmbientOcclusionLightValue(), + ratio); + renderBlocks.aoLightValueScratchXYZNPP = getMixedAo( + renderBlocks.blockAccess.getBlock(x - 1, y + 1, z + 1) + .getAmbientOcclusionLightValue(), + renderBlocks.blockAccess.getBlock(x, y + 1, z + 1) + .getAmbientOcclusionLightValue(), + ratio); + + int brightnessMixedXYZNPN = renderBlocks.getAoBrightness( + renderBlocks.aoBrightnessXZNN, + renderBlocks.aoBrightnessXYZNPN, + renderBlocks.aoBrightnessXYNP, + mixedBrightness); + int brightnessMixedXYZNNN = renderBlocks.getAoBrightness( + renderBlocks.aoBrightnessXYZNNN, + renderBlocks.aoBrightnessXYNN, + renderBlocks.aoBrightnessXZNN, + mixedBrightness); + int brightnessMixedXYZNNP = renderBlocks.getAoBrightness( + renderBlocks.aoBrightnessXYNN, + renderBlocks.aoBrightnessXYZNNP, + renderBlocks.aoBrightnessXZNP, + mixedBrightness); + int brightnessMixedXYZNPP = renderBlocks.getAoBrightness( + renderBlocks.aoBrightnessXZNP, + renderBlocks.aoBrightnessXYNP, + renderBlocks.aoBrightnessXYZNPP, + mixedBrightness); + + float aoMixedXYZNPN = (renderBlocks.aoLightValueScratchXZNN + aoLightValue + + renderBlocks.aoLightValueScratchXYZNPN + + renderBlocks.aoLightValueScratchXYNP) / 4.0F; + float aoMixedXYZNNN = (renderBlocks.aoLightValueScratchXYZNNN + renderBlocks.aoLightValueScratchXYNN + + renderBlocks.aoLightValueScratchXZNN + + aoLightValue) / 4.0F; + float aoMixedXYZNNP = (renderBlocks.aoLightValueScratchXYNN + renderBlocks.aoLightValueScratchXYZNNP + + aoLightValue + + renderBlocks.aoLightValueScratchXZNP) / 4.0F; + float aoMixedXYZNPP = (aoLightValue + renderBlocks.aoLightValueScratchXZNP + + renderBlocks.aoLightValueScratchXYNP + + renderBlocks.aoLightValueScratchXYZNPP) / 4.0F; + + aoTopLeft = (float) (aoMixedXYZNPP * renderBlocks.renderMaxY * renderBlocks.renderMaxZ + + aoMixedXYZNPN * renderBlocks.renderMaxY * (1.0D - renderBlocks.renderMaxZ) + + aoMixedXYZNNN * (1.0D - renderBlocks.renderMaxY) * (1.0D - renderBlocks.renderMaxZ) + + aoMixedXYZNNP * (1.0D - renderBlocks.renderMaxY) * renderBlocks.renderMaxZ); + aoBottomLeft = (float) (aoMixedXYZNPP * renderBlocks.renderMaxY * renderBlocks.renderMinZ + + aoMixedXYZNPN * renderBlocks.renderMaxY * (1.0D - renderBlocks.renderMinZ) + + aoMixedXYZNNN * (1.0D - renderBlocks.renderMaxY) * (1.0D - renderBlocks.renderMinZ) + + aoMixedXYZNNP * (1.0D - renderBlocks.renderMaxY) * renderBlocks.renderMinZ); + aoBottomRight = (float) (aoMixedXYZNPP * renderBlocks.renderMinY * renderBlocks.renderMinZ + + aoMixedXYZNPN * renderBlocks.renderMinY * (1.0D - renderBlocks.renderMinZ) + + aoMixedXYZNNN * (1.0D - renderBlocks.renderMinY) * (1.0D - renderBlocks.renderMinZ) + + aoMixedXYZNNP * (1.0D - renderBlocks.renderMinY) * renderBlocks.renderMinZ); + aoTopRight = (float) (aoMixedXYZNPP * renderBlocks.renderMinY * renderBlocks.renderMaxZ + + aoMixedXYZNPN * renderBlocks.renderMinY * (1.0D - renderBlocks.renderMaxZ) + + aoMixedXYZNNN * (1.0D - renderBlocks.renderMinY) * (1.0D - renderBlocks.renderMaxZ) + + aoMixedXYZNNP * (1.0D - renderBlocks.renderMinY) * renderBlocks.renderMaxZ); + + renderBlocks.brightnessTopLeft = renderBlocks.mixAoBrightness( + brightnessMixedXYZNPP, + brightnessMixedXYZNPN, + brightnessMixedXYZNNN, + brightnessMixedXYZNNP, + renderBlocks.renderMaxY * renderBlocks.renderMaxZ, + renderBlocks.renderMaxY * (1.0D - renderBlocks.renderMaxZ), + (1.0D - renderBlocks.renderMaxY) * (1.0D - renderBlocks.renderMaxZ), + (1.0D - renderBlocks.renderMaxY) * renderBlocks.renderMaxZ); + renderBlocks.brightnessBottomLeft = renderBlocks.mixAoBrightness( + brightnessMixedXYZNPP, + brightnessMixedXYZNPN, + brightnessMixedXYZNNN, + brightnessMixedXYZNNP, + renderBlocks.renderMaxY * renderBlocks.renderMinZ, + renderBlocks.renderMaxY * (1.0D - renderBlocks.renderMinZ), + (1.0D - renderBlocks.renderMaxY) * (1.0D - renderBlocks.renderMinZ), + (1.0D - renderBlocks.renderMaxY) * renderBlocks.renderMinZ); + renderBlocks.brightnessBottomRight = renderBlocks.mixAoBrightness( + brightnessMixedXYZNPP, + brightnessMixedXYZNPN, + brightnessMixedXYZNNN, + brightnessMixedXYZNNP, + renderBlocks.renderMinY * renderBlocks.renderMinZ, + renderBlocks.renderMinY * (1.0D - renderBlocks.renderMinZ), + (1.0D - renderBlocks.renderMinY) * (1.0D - renderBlocks.renderMinZ), + (1.0D - renderBlocks.renderMinY) * renderBlocks.renderMinZ); + renderBlocks.brightnessTopRight = renderBlocks.mixAoBrightness( + brightnessMixedXYZNPP, + brightnessMixedXYZNPN, + brightnessMixedXYZNNN, + brightnessMixedXYZNNP, + renderBlocks.renderMinY * renderBlocks.renderMaxZ, + renderBlocks.renderMinY * (1.0D - renderBlocks.renderMaxZ), + (1.0D - renderBlocks.renderMinY) * (1.0D - renderBlocks.renderMaxZ), + (1.0D - renderBlocks.renderMinY) * renderBlocks.renderMaxZ); + } + + return this; + } + + /** + * Sets up lighting for the East face and returns the {@link LightingHelper}. + * <p> + * This is a consolidated <code>method</code> that sets side shading with respect to the following attributes: + * <p> + * <ul> + * <li>{@link RenderBlocks#enableAO}</li> + * <li>{@link RenderBlocks#partialRenderBounds}</li> + * </ul> + * + * @param block the block {@link Block} + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate + * @return the {@link LightingHelper} + */ + public LightingHelper setupLightingXPos(Block block, int x, int y, int z) { + + if (renderBlocks.enableAO) { + + int xOffset = renderBlocks.renderMaxX < 1.0F - NO_Z_FIGHT_OFFSET ? x : x + 1; + + int mixedBrightness = block.getMixedBrightnessForBlock(renderBlocks.blockAccess, xOffset, y, z); + brightness = mixedBrightness; + + float aoLightValue = renderBlocks.blockAccess.getBlock(x + 1, y, z) + .getAmbientOcclusionLightValue(); + + renderBlocks.aoBrightnessXYPN = block + .getMixedBrightnessForBlock(renderBlocks.blockAccess, xOffset, y - 1, z); + renderBlocks.aoBrightnessXZPN = block + .getMixedBrightnessForBlock(renderBlocks.blockAccess, xOffset, y, z - 1); + renderBlocks.aoBrightnessXZPP = block + .getMixedBrightnessForBlock(renderBlocks.blockAccess, xOffset, y, z + 1); + renderBlocks.aoBrightnessXYPP = block + .getMixedBrightnessForBlock(renderBlocks.blockAccess, xOffset, y + 1, z); + renderBlocks.aoBrightnessXYZPNN = block + .getMixedBrightnessForBlock(renderBlocks.blockAccess, xOffset, y - 1, z - 1); + renderBlocks.aoBrightnessXYZPNP = block + .getMixedBrightnessForBlock(renderBlocks.blockAccess, xOffset, y - 1, z + 1); + renderBlocks.aoBrightnessXYZPPN = block + .getMixedBrightnessForBlock(renderBlocks.blockAccess, xOffset, y + 1, z - 1); + renderBlocks.aoBrightnessXYZPPP = block + .getMixedBrightnessForBlock(renderBlocks.blockAccess, xOffset, y + 1, z + 1); + renderBlocks.aoLightValueScratchXYPN = getMixedAo( + renderBlocks.blockAccess.getBlock(x + 1, y - 1, z) + .getAmbientOcclusionLightValue(), + renderBlocks.blockAccess.getBlock(x, y - 1, z) + .getAmbientOcclusionLightValue(), + renderBlocks.renderMaxX); + renderBlocks.aoLightValueScratchXZPN = getMixedAo( + renderBlocks.blockAccess.getBlock(x + 1, y, z - 1) + .getAmbientOcclusionLightValue(), + renderBlocks.blockAccess.getBlock(x, y, z - 1) + .getAmbientOcclusionLightValue(), + renderBlocks.renderMaxX); + renderBlocks.aoLightValueScratchXZPP = getMixedAo( + renderBlocks.blockAccess.getBlock(x + 1, y, z + 1) + .getAmbientOcclusionLightValue(), + renderBlocks.blockAccess.getBlock(x, y, z + 1) + .getAmbientOcclusionLightValue(), + renderBlocks.renderMaxX); + renderBlocks.aoLightValueScratchXYPP = getMixedAo( + renderBlocks.blockAccess.getBlock(x + 1, y + 1, z) + .getAmbientOcclusionLightValue(), + renderBlocks.blockAccess.getBlock(x, y + 1, z) + .getAmbientOcclusionLightValue(), + renderBlocks.renderMaxX); + renderBlocks.aoLightValueScratchXYZPNN = getMixedAo( + renderBlocks.blockAccess.getBlock(x + 1, y - 1, z - 1) + .getAmbientOcclusionLightValue(), + renderBlocks.blockAccess.getBlock(x, y - 1, z - 1) + .getAmbientOcclusionLightValue(), + renderBlocks.renderMaxX); + renderBlocks.aoLightValueScratchXYZPNP = getMixedAo( + renderBlocks.blockAccess.getBlock(x + 1, y - 1, z + 1) + .getAmbientOcclusionLightValue(), + renderBlocks.blockAccess.getBlock(x, y - 1, z + 1) + .getAmbientOcclusionLightValue(), + renderBlocks.renderMaxX); + renderBlocks.aoLightValueScratchXYZPPN = getMixedAo( + renderBlocks.blockAccess.getBlock(x + 1, y + 1, z - 1) + .getAmbientOcclusionLightValue(), + renderBlocks.blockAccess.getBlock(x, y + 1, z - 1) + .getAmbientOcclusionLightValue(), + renderBlocks.renderMaxX); + renderBlocks.aoLightValueScratchXYZPPP = getMixedAo( + renderBlocks.blockAccess.getBlock(x + 1, y + 1, z + 1) + .getAmbientOcclusionLightValue(), + renderBlocks.blockAccess.getBlock(x, y + 1, z + 1) + .getAmbientOcclusionLightValue(), + renderBlocks.renderMaxX); + + int brightnessMixedXYZPPP = renderBlocks.getAoBrightness( + renderBlocks.aoBrightnessXZPP, + renderBlocks.aoBrightnessXYPP, + renderBlocks.aoBrightnessXYZPPP, + mixedBrightness); + int brightnessMixedXYZPNP = renderBlocks.getAoBrightness( + renderBlocks.aoBrightnessXYPN, + renderBlocks.aoBrightnessXYZPNP, + renderBlocks.aoBrightnessXZPP, + mixedBrightness); + int brightnessMixedXYZPNN = renderBlocks.getAoBrightness( + renderBlocks.aoBrightnessXYZPNN, + renderBlocks.aoBrightnessXYPN, + renderBlocks.aoBrightnessXZPN, + mixedBrightness); + int brightnessMixedXYZPPN = renderBlocks.getAoBrightness( + renderBlocks.aoBrightnessXZPN, + renderBlocks.aoBrightnessXYZPPN, + renderBlocks.aoBrightnessXYPP, + mixedBrightness); + + float aoMixedXYZPPP = (aoLightValue + renderBlocks.aoLightValueScratchXZPP + + renderBlocks.aoLightValueScratchXYPP + + renderBlocks.aoLightValueScratchXYZPPP) / 4.0F; + float aoMixedXYZPNP = (renderBlocks.aoLightValueScratchXYPN + renderBlocks.aoLightValueScratchXYZPNP + + aoLightValue + + renderBlocks.aoLightValueScratchXZPP) / 4.0F; + float aoMixedXYZPNN = (renderBlocks.aoLightValueScratchXYZPNN + renderBlocks.aoLightValueScratchXYPN + + renderBlocks.aoLightValueScratchXZPN + + aoLightValue) / 4.0F; + float aoMixedXYZPPN = (renderBlocks.aoLightValueScratchXZPN + aoLightValue + + renderBlocks.aoLightValueScratchXYZPPN + + renderBlocks.aoLightValueScratchXYPP) / 4.0F; + + aoTopLeft = (float) (aoMixedXYZPNP * (1.0D - renderBlocks.renderMinY) * renderBlocks.renderMaxZ + + aoMixedXYZPNN * (1.0D - renderBlocks.renderMinY) * (1.0D - renderBlocks.renderMaxZ) + + aoMixedXYZPPN * renderBlocks.renderMinY * (1.0D - renderBlocks.renderMaxZ) + + aoMixedXYZPPP * renderBlocks.renderMinY * renderBlocks.renderMaxZ); + aoBottomLeft = (float) (aoMixedXYZPNP * (1.0D - renderBlocks.renderMinY) * renderBlocks.renderMinZ + + aoMixedXYZPNN * (1.0D - renderBlocks.renderMinY) * (1.0D - renderBlocks.renderMinZ) + + aoMixedXYZPPN * renderBlocks.renderMinY * (1.0D - renderBlocks.renderMinZ) + + aoMixedXYZPPP * renderBlocks.renderMinY * renderBlocks.renderMinZ); + aoBottomRight = (float) (aoMixedXYZPNP * (1.0D - renderBlocks.renderMaxY) * renderBlocks.renderMinZ + + aoMixedXYZPNN * (1.0D - renderBlocks.renderMaxY) * (1.0D - renderBlocks.renderMinZ) + + aoMixedXYZPPN * renderBlocks.renderMaxY * (1.0D - renderBlocks.renderMinZ) + + aoMixedXYZPPP * renderBlocks.renderMaxY * renderBlocks.renderMinZ); + aoTopRight = (float) (aoMixedXYZPNP * (1.0D - renderBlocks.renderMaxY) * renderBlocks.renderMaxZ + + aoMixedXYZPNN * (1.0D - renderBlocks.renderMaxY) * (1.0D - renderBlocks.renderMaxZ) + + aoMixedXYZPPN * renderBlocks.renderMaxY * (1.0D - renderBlocks.renderMaxZ) + + aoMixedXYZPPP * renderBlocks.renderMaxY * renderBlocks.renderMaxZ); + + renderBlocks.brightnessTopLeft = renderBlocks.mixAoBrightness( + brightnessMixedXYZPNP, + brightnessMixedXYZPNN, + brightnessMixedXYZPPN, + brightnessMixedXYZPPP, + (1.0D - renderBlocks.renderMinY) * renderBlocks.renderMaxZ, + (1.0D - renderBlocks.renderMinY) * (1.0D - renderBlocks.renderMaxZ), + renderBlocks.renderMinY * (1.0D - renderBlocks.renderMaxZ), + renderBlocks.renderMinY * renderBlocks.renderMaxZ); + renderBlocks.brightnessBottomLeft = renderBlocks.mixAoBrightness( + brightnessMixedXYZPNP, + brightnessMixedXYZPNN, + brightnessMixedXYZPPN, + brightnessMixedXYZPPP, + (1.0D - renderBlocks.renderMinY) * renderBlocks.renderMinZ, + (1.0D - renderBlocks.renderMinY) * (1.0D - renderBlocks.renderMinZ), + renderBlocks.renderMinY * (1.0D - renderBlocks.renderMinZ), + renderBlocks.renderMinY * renderBlocks.renderMinZ); + renderBlocks.brightnessBottomRight = renderBlocks.mixAoBrightness( + brightnessMixedXYZPNP, + brightnessMixedXYZPNN, + brightnessMixedXYZPPN, + brightnessMixedXYZPPP, + (1.0D - renderBlocks.renderMaxY) * renderBlocks.renderMinZ, + (1.0D - renderBlocks.renderMaxY) * (1.0D - renderBlocks.renderMinZ), + renderBlocks.renderMaxY * (1.0D - renderBlocks.renderMinZ), + renderBlocks.renderMaxY * renderBlocks.renderMinZ); + renderBlocks.brightnessTopRight = renderBlocks.mixAoBrightness( + brightnessMixedXYZPNP, + brightnessMixedXYZPNN, + brightnessMixedXYZPPN, + brightnessMixedXYZPPP, + (1.0D - renderBlocks.renderMaxY) * renderBlocks.renderMaxZ, + (1.0D - renderBlocks.renderMaxY) * (1.0D - renderBlocks.renderMaxZ), + renderBlocks.renderMaxY * (1.0D - renderBlocks.renderMaxZ), + renderBlocks.renderMaxY * renderBlocks.renderMaxZ); + } + + return this; + } + + /** + * Sets up lighting for the bottom face and returns the {@link LightingHelper}. + * <p> + * This is a consolidated <code>method</code> that sets side shading with respect to the following attributes: + * <p> + * <ul> + * <li>{@link RenderBlocks#enableAO}</li> + * <li>{@link RenderBlocks#partialRenderBounds}</li> + * </ul> + * + * @param block the block {@link Block} + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate + * @return the {@link LightingHelper} + */ + public LightingHelper setupLightingYNeg(Block block, int x, int y, int z) { + + if (renderBlocks.enableAO) { + + int yOffset = renderBlocks.renderMinY > 0.0F + NO_Z_FIGHT_OFFSET ? y : y - 1; + + int mixedBrightness = block.getMixedBrightnessForBlock(renderBlocks.blockAccess, x, yOffset, z); + brightness = mixedBrightness; + + float ratio = (float) (1.0F - renderBlocks.renderMinY); + float aoLightValue = renderBlocks.blockAccess.getBlock(x, y - 1, z) + .getAmbientOcclusionLightValue(); + + renderBlocks.aoBrightnessXYNN = block + .getMixedBrightnessForBlock(renderBlocks.blockAccess, x - 1, yOffset, z); + renderBlocks.aoBrightnessYZNN = block + .getMixedBrightnessForBlock(renderBlocks.blockAccess, x, yOffset, z - 1); + renderBlocks.aoBrightnessYZNP = block + .getMixedBrightnessForBlock(renderBlocks.blockAccess, x, yOffset, z + 1); + renderBlocks.aoBrightnessXYPN = block + .getMixedBrightnessForBlock(renderBlocks.blockAccess, x + 1, yOffset, z); + renderBlocks.aoBrightnessXYZNNN = block + .getMixedBrightnessForBlock(renderBlocks.blockAccess, x - 1, yOffset, z - 1); + renderBlocks.aoBrightnessXYZNNP = block + .getMixedBrightnessForBlock(renderBlocks.blockAccess, x - 1, yOffset, z + 1); + renderBlocks.aoBrightnessXYZPNN = block + .getMixedBrightnessForBlock(renderBlocks.blockAccess, x + 1, yOffset, z - 1); + renderBlocks.aoBrightnessXYZPNP = block + .getMixedBrightnessForBlock(renderBlocks.blockAccess, x + 1, yOffset, z + 1); + renderBlocks.aoLightValueScratchXYNN = getMixedAo( + renderBlocks.blockAccess.getBlock(x - 1, y - 1, z) + .getAmbientOcclusionLightValue(), + renderBlocks.blockAccess.getBlock(x - 1, y, z) + .getAmbientOcclusionLightValue(), + ratio); + renderBlocks.aoLightValueScratchYZNN = getMixedAo( + renderBlocks.blockAccess.getBlock(x, y - 1, z - 1) + .getAmbientOcclusionLightValue(), + renderBlocks.blockAccess.getBlock(x, y, z - 1) + .getAmbientOcclusionLightValue(), + ratio); + renderBlocks.aoLightValueScratchYZNP = getMixedAo( + renderBlocks.blockAccess.getBlock(x, y - 1, z + 1) + .getAmbientOcclusionLightValue(), + renderBlocks.blockAccess.getBlock(x, y, z + 1) + .getAmbientOcclusionLightValue(), + ratio); + renderBlocks.aoLightValueScratchXYPN = getMixedAo( + renderBlocks.blockAccess.getBlock(x + 1, y - 1, z) + .getAmbientOcclusionLightValue(), + renderBlocks.blockAccess.getBlock(x + 1, y, z) + .getAmbientOcclusionLightValue(), + ratio); + renderBlocks.aoLightValueScratchXYZNNN = getMixedAo( + renderBlocks.blockAccess.getBlock(x - 1, y - 1, z - 1) + .getAmbientOcclusionLightValue(), + renderBlocks.blockAccess.getBlock(x - 1, y, z - 1) + .getAmbientOcclusionLightValue(), + ratio); + renderBlocks.aoLightValueScratchXYZNNP = getMixedAo( + renderBlocks.blockAccess.getBlock(x - 1, y - 1, z + 1) + .getAmbientOcclusionLightValue(), + renderBlocks.blockAccess.getBlock(x - 1, y, z + 1) + .getAmbientOcclusionLightValue(), + ratio); + renderBlocks.aoLightValueScratchXYZPNN = getMixedAo( + renderBlocks.blockAccess.getBlock(x + 1, y - 1, z - 1) + .getAmbientOcclusionLightValue(), + renderBlocks.blockAccess.getBlock(x + 1, y, z - 1) + .getAmbientOcclusionLightValue(), + ratio); + renderBlocks.aoLightValueScratchXYZPNP = getMixedAo( + renderBlocks.blockAccess.getBlock(x + 1, y - 1, z + 1) + .getAmbientOcclusionLightValue(), + renderBlocks.blockAccess.getBlock(x + 1, y, z + 1) + .getAmbientOcclusionLightValue(), + ratio); + + int brightnessMixedXYZPNP = renderBlocks.getAoBrightness( + renderBlocks.aoBrightnessYZNP, + renderBlocks.aoBrightnessXYZPNP, + renderBlocks.aoBrightnessXYPN, + mixedBrightness); + int brightnessMixedXYZPNN = renderBlocks.getAoBrightness( + renderBlocks.aoBrightnessYZNN, + renderBlocks.aoBrightnessXYPN, + renderBlocks.aoBrightnessXYZPNN, + mixedBrightness); + int brightnessMixedXYZNNN = renderBlocks.getAoBrightness( + renderBlocks.aoBrightnessXYNN, + renderBlocks.aoBrightnessXYZNNN, + renderBlocks.aoBrightnessYZNN, + mixedBrightness); + int brightnessMixedXYZNNP = renderBlocks.getAoBrightness( + renderBlocks.aoBrightnessXYZNNP, + renderBlocks.aoBrightnessXYNN, + renderBlocks.aoBrightnessYZNP, + mixedBrightness); + + float aoMixedXYZPNP = (renderBlocks.aoLightValueScratchYZNP + aoLightValue + + renderBlocks.aoLightValueScratchXYZPNP + + renderBlocks.aoLightValueScratchXYPN) / 4.0F; + float aoMixedXYZPNN = (aoLightValue + renderBlocks.aoLightValueScratchYZNN + + renderBlocks.aoLightValueScratchXYPN + + renderBlocks.aoLightValueScratchXYZPNN) / 4.0F; + float aoMixedXYZNNN = (renderBlocks.aoLightValueScratchXYNN + renderBlocks.aoLightValueScratchXYZNNN + + aoLightValue + + renderBlocks.aoLightValueScratchYZNN) / 4.0F; + float aoMixedXYZNNP = (renderBlocks.aoLightValueScratchXYZNNP + renderBlocks.aoLightValueScratchXYNN + + renderBlocks.aoLightValueScratchYZNP + + aoLightValue) / 4.0F; + + aoTopLeft = (float) (aoMixedXYZNNP * renderBlocks.renderMaxZ * (1.0D - renderBlocks.renderMinX) + + aoMixedXYZPNP * renderBlocks.renderMaxZ * renderBlocks.renderMinX + + aoMixedXYZPNN * (1.0D - renderBlocks.renderMaxZ) * renderBlocks.renderMinX + + aoMixedXYZNNN * (1.0D - renderBlocks.renderMaxZ) * (1.0D - renderBlocks.renderMinX)); + aoBottomLeft = (float) (aoMixedXYZNNP * renderBlocks.renderMinZ * (1.0D - renderBlocks.renderMinX) + + aoMixedXYZPNP * renderBlocks.renderMinZ * renderBlocks.renderMinX + + aoMixedXYZPNN * (1.0D - renderBlocks.renderMinZ) * renderBlocks.renderMinX + + aoMixedXYZNNN * (1.0D - renderBlocks.renderMinZ) * (1.0D - renderBlocks.renderMinX)); + aoBottomRight = (float) (aoMixedXYZNNP * renderBlocks.renderMinZ * (1.0D - renderBlocks.renderMaxX) + + aoMixedXYZPNP * renderBlocks.renderMinZ * renderBlocks.renderMaxX + + aoMixedXYZPNN * (1.0D - renderBlocks.renderMinZ) * renderBlocks.renderMaxX + + aoMixedXYZNNN * (1.0D - renderBlocks.renderMinZ) * (1.0D - renderBlocks.renderMaxX)); + aoTopRight = (float) (aoMixedXYZNNP * renderBlocks.renderMaxZ * (1.0D - renderBlocks.renderMaxX) + + aoMixedXYZPNP * renderBlocks.renderMaxZ * renderBlocks.renderMaxX + + aoMixedXYZPNN * (1.0D - renderBlocks.renderMaxZ) * renderBlocks.renderMaxX + + aoMixedXYZNNN * (1.0D - renderBlocks.renderMaxZ) * (1.0D - renderBlocks.renderMaxX)); + + renderBlocks.brightnessTopLeft = renderBlocks.mixAoBrightness( + brightnessMixedXYZNNP, + brightnessMixedXYZPNP, + brightnessMixedXYZPNN, + brightnessMixedXYZNNN, + renderBlocks.renderMaxZ * (1.0D - renderBlocks.renderMinX), + renderBlocks.renderMaxZ * renderBlocks.renderMinX, + (1.0D - renderBlocks.renderMaxZ) * renderBlocks.renderMinX, + (1.0D - renderBlocks.renderMaxZ) * (1.0D - renderBlocks.renderMinX)); + renderBlocks.brightnessBottomLeft = renderBlocks.mixAoBrightness( + brightnessMixedXYZNNP, + brightnessMixedXYZPNP, + brightnessMixedXYZPNN, + brightnessMixedXYZNNN, + renderBlocks.renderMinZ * (1.0D - renderBlocks.renderMinX), + renderBlocks.renderMinZ * renderBlocks.renderMinX, + (1.0D - renderBlocks.renderMinZ) * renderBlocks.renderMinX, + (1.0D - renderBlocks.renderMinZ) * (1.0D - renderBlocks.renderMinX)); + renderBlocks.brightnessBottomRight = renderBlocks.mixAoBrightness( + brightnessMixedXYZNNP, + brightnessMixedXYZPNP, + brightnessMixedXYZPNN, + brightnessMixedXYZNNN, + renderBlocks.renderMinZ * (1.0D - renderBlocks.renderMaxX), + renderBlocks.renderMinZ * renderBlocks.renderMaxX, + (1.0D - renderBlocks.renderMinZ) * renderBlocks.renderMaxX, + (1.0D - renderBlocks.renderMinZ) * (1.0D - renderBlocks.renderMaxX)); + renderBlocks.brightnessTopRight = renderBlocks.mixAoBrightness( + brightnessMixedXYZNNP, + brightnessMixedXYZPNP, + brightnessMixedXYZPNN, + brightnessMixedXYZNNN, + renderBlocks.renderMaxZ * (1.0D - renderBlocks.renderMaxX), + renderBlocks.renderMaxZ * renderBlocks.renderMaxX, + (1.0D - renderBlocks.renderMaxZ) * renderBlocks.renderMaxX, + (1.0D - renderBlocks.renderMaxZ) * (1.0D - renderBlocks.renderMaxX)); + } + + return this; + } + + /** + * Sets up lighting for the top face and returns the {@link LightingHelper}. + * <p> + * This is a consolidated <code>method</code> that sets side shading with respect to the following attributes: + * <p> + * <ul> + * <li>{@link RenderBlocks#enableAO}</li> + * <li>{@link RenderBlocks#partialRenderBounds}</li> + * </ul> + * + * @param block the block {@link Block} + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate + * @return the {@link LightingHelper} + */ + public LightingHelper setupLightingYPos(Block block, int x, int y, int z) { + + if (renderBlocks.enableAO) { + + int yOffset = renderBlocks.renderMaxY < 1.0F - NO_Z_FIGHT_OFFSET ? y : y + 1; + + int mixedBrightness = block.getMixedBrightnessForBlock(renderBlocks.blockAccess, x, yOffset, z); + brightness = mixedBrightness; + + float aoLightValue = renderBlocks.blockAccess.getBlock(x, y + 1, z) + .getAmbientOcclusionLightValue(); + + renderBlocks.aoBrightnessXYNP = block + .getMixedBrightnessForBlock(renderBlocks.blockAccess, x - 1, yOffset, z); + renderBlocks.aoBrightnessXYPP = block + .getMixedBrightnessForBlock(renderBlocks.blockAccess, x + 1, yOffset, z); + renderBlocks.aoBrightnessYZPN = block + .getMixedBrightnessForBlock(renderBlocks.blockAccess, x, yOffset, z - 1); + renderBlocks.aoBrightnessYZPP = block + .getMixedBrightnessForBlock(renderBlocks.blockAccess, x, yOffset, z + 1); + renderBlocks.aoBrightnessXYZNPN = block + .getMixedBrightnessForBlock(renderBlocks.blockAccess, x - 1, yOffset, z - 1); + renderBlocks.aoBrightnessXYZPPN = block + .getMixedBrightnessForBlock(renderBlocks.blockAccess, x + 1, yOffset, z - 1); + renderBlocks.aoBrightnessXYZNPP = block + .getMixedBrightnessForBlock(renderBlocks.blockAccess, x - 1, yOffset, z + 1); + renderBlocks.aoBrightnessXYZPPP = block + .getMixedBrightnessForBlock(renderBlocks.blockAccess, x + 1, yOffset, z + 1); + renderBlocks.aoLightValueScratchXYNP = getMixedAo( + renderBlocks.blockAccess.getBlock(x - 1, y + 1, z) + .getAmbientOcclusionLightValue(), + renderBlocks.blockAccess.getBlock(x - 1, y, z) + .getAmbientOcclusionLightValue(), + renderBlocks.renderMaxY); + renderBlocks.aoLightValueScratchXYPP = getMixedAo( + renderBlocks.blockAccess.getBlock(x + 1, y + 1, z) + .getAmbientOcclusionLightValue(), + renderBlocks.blockAccess.getBlock(x + 1, y, z) + .getAmbientOcclusionLightValue(), + renderBlocks.renderMaxY); + renderBlocks.aoLightValueScratchYZPN = getMixedAo( + renderBlocks.blockAccess.getBlock(x, y + 1, z - 1) + .getAmbientOcclusionLightValue(), + renderBlocks.blockAccess.getBlock(x, y, z - 1) + .getAmbientOcclusionLightValue(), + renderBlocks.renderMaxY); + renderBlocks.aoLightValueScratchYZPP = getMixedAo( + renderBlocks.blockAccess.getBlock(x, y + 1, z + 1) + .getAmbientOcclusionLightValue(), + renderBlocks.blockAccess.getBlock(x, y, z + 1) + .getAmbientOcclusionLightValue(), + renderBlocks.renderMaxY); + renderBlocks.aoLightValueScratchXYZNPN = getMixedAo( + renderBlocks.blockAccess.getBlock(x - 1, y + 1, z - 1) + .getAmbientOcclusionLightValue(), + renderBlocks.blockAccess.getBlock(x - 1, y, z - 1) + .getAmbientOcclusionLightValue(), + renderBlocks.renderMaxY); + renderBlocks.aoLightValueScratchXYZPPN = getMixedAo( + renderBlocks.blockAccess.getBlock(x + 1, y + 1, z - 1) + .getAmbientOcclusionLightValue(), + renderBlocks.blockAccess.getBlock(x + 1, y, z - 1) + .getAmbientOcclusionLightValue(), + renderBlocks.renderMaxY); + renderBlocks.aoLightValueScratchXYZNPP = getMixedAo( + renderBlocks.blockAccess.getBlock(x - 1, y + 1, z + 1) + .getAmbientOcclusionLightValue(), + renderBlocks.blockAccess.getBlock(x - 1, y, z + 1) + .getAmbientOcclusionLightValue(), + renderBlocks.renderMaxY); + renderBlocks.aoLightValueScratchXYZPPP = getMixedAo( + renderBlocks.blockAccess.getBlock(x + 1, y + 1, z + 1) + .getAmbientOcclusionLightValue(), + renderBlocks.blockAccess.getBlock(x + 1, y, z + 1) + .getAmbientOcclusionLightValue(), + renderBlocks.renderMaxY); + + int brightnessMixedXYZPPP = renderBlocks.getAoBrightness( + renderBlocks.aoBrightnessYZPP, + renderBlocks.aoBrightnessXYZPPP, + renderBlocks.aoBrightnessXYPP, + mixedBrightness); + int brightnessMixedXYZPPN = renderBlocks.getAoBrightness( + renderBlocks.aoBrightnessYZPN, + renderBlocks.aoBrightnessXYPP, + renderBlocks.aoBrightnessXYZPPN, + mixedBrightness); + int brightnessMixedXYZNPN = renderBlocks.getAoBrightness( + renderBlocks.aoBrightnessXYNP, + renderBlocks.aoBrightnessXYZNPN, + renderBlocks.aoBrightnessYZPN, + mixedBrightness); + int brightnessMixedXYZNPP = renderBlocks.getAoBrightness( + renderBlocks.aoBrightnessXYZNPP, + renderBlocks.aoBrightnessXYNP, + renderBlocks.aoBrightnessYZPP, + mixedBrightness); + + float aoMixedXYZPPP = (renderBlocks.aoLightValueScratchYZPP + aoLightValue + + renderBlocks.aoLightValueScratchXYZPPP + + renderBlocks.aoLightValueScratchXYPP) / 4.0F; + float aoMixedXYZPPN = (aoLightValue + renderBlocks.aoLightValueScratchYZPN + + renderBlocks.aoLightValueScratchXYPP + + renderBlocks.aoLightValueScratchXYZPPN) / 4.0F; + float aoMixedXYZNPN = (renderBlocks.aoLightValueScratchXYNP + renderBlocks.aoLightValueScratchXYZNPN + + aoLightValue + + renderBlocks.aoLightValueScratchYZPN) / 4.0F; + float aoMixedXYZNPP = (renderBlocks.aoLightValueScratchXYZNPP + renderBlocks.aoLightValueScratchXYNP + + renderBlocks.aoLightValueScratchYZPP + + aoLightValue) / 4.0F; + + aoTopLeft /* SE */ = (float) (aoMixedXYZNPP * renderBlocks.renderMaxZ * (1.0D - renderBlocks.renderMaxX) + + aoMixedXYZPPP * renderBlocks.renderMaxZ * renderBlocks.renderMaxX + + aoMixedXYZPPN * (1.0D - renderBlocks.renderMaxZ) * renderBlocks.renderMaxX + + aoMixedXYZNPN * (1.0D - renderBlocks.renderMaxZ) * (1.0D - renderBlocks.renderMaxX)); + aoBottomLeft /* NE */ = (float) (aoMixedXYZNPP * renderBlocks.renderMinZ * (1.0D - renderBlocks.renderMaxX) + + aoMixedXYZPPP * renderBlocks.renderMinZ * renderBlocks.renderMaxX + + aoMixedXYZPPN * (1.0D - renderBlocks.renderMinZ) * renderBlocks.renderMaxX + + aoMixedXYZNPN * (1.0D - renderBlocks.renderMinZ) * (1.0D - renderBlocks.renderMaxX)); + aoBottomRight /* NW */ = (float) (aoMixedXYZNPP * renderBlocks.renderMinZ * (1.0D - renderBlocks.renderMinX) + + aoMixedXYZPPP * renderBlocks.renderMinZ * renderBlocks.renderMinX + + aoMixedXYZPPN * (1.0D - renderBlocks.renderMinZ) * renderBlocks.renderMinX + + aoMixedXYZNPN * (1.0D - renderBlocks.renderMinZ) * (1.0D - renderBlocks.renderMinX)); + aoTopRight /* SW */ = (float) (aoMixedXYZNPP * renderBlocks.renderMaxZ * (1.0D - renderBlocks.renderMinX) + + aoMixedXYZPPP * renderBlocks.renderMaxZ * renderBlocks.renderMinX + + aoMixedXYZPPN * (1.0D - renderBlocks.renderMaxZ) * renderBlocks.renderMinX + + aoMixedXYZNPN * (1.0D - renderBlocks.renderMaxZ) * (1.0D - renderBlocks.renderMinX)); + + renderBlocks.brightnessTopLeft = renderBlocks.mixAoBrightness( + brightnessMixedXYZNPP, + brightnessMixedXYZPPP, + brightnessMixedXYZPPN, + brightnessMixedXYZNPN, + renderBlocks.renderMaxZ * (1.0D - renderBlocks.renderMaxX), + renderBlocks.renderMaxZ * renderBlocks.renderMaxX, + (1.0D - renderBlocks.renderMaxZ) * renderBlocks.renderMaxX, + (1.0D - renderBlocks.renderMaxZ) * (1.0D - renderBlocks.renderMaxX)); + renderBlocks.brightnessBottomLeft = renderBlocks.mixAoBrightness( + brightnessMixedXYZNPP, + brightnessMixedXYZPPP, + brightnessMixedXYZPPN, + brightnessMixedXYZNPN, + renderBlocks.renderMinZ * (1.0D - renderBlocks.renderMaxX), + renderBlocks.renderMinZ * renderBlocks.renderMaxX, + (1.0D - renderBlocks.renderMinZ) * renderBlocks.renderMaxX, + (1.0D - renderBlocks.renderMinZ) * (1.0D - renderBlocks.renderMaxX)); + renderBlocks.brightnessBottomRight = renderBlocks.mixAoBrightness( + brightnessMixedXYZNPP, + brightnessMixedXYZPPP, + brightnessMixedXYZPPN, + brightnessMixedXYZNPN, + renderBlocks.renderMinZ * (1.0D - renderBlocks.renderMinX), + renderBlocks.renderMinZ * renderBlocks.renderMinX, + (1.0D - renderBlocks.renderMinZ) * renderBlocks.renderMinX, + (1.0D - renderBlocks.renderMinZ) * (1.0D - renderBlocks.renderMinX)); + renderBlocks.brightnessTopRight = renderBlocks.mixAoBrightness( + brightnessMixedXYZNPP, + brightnessMixedXYZPPP, + brightnessMixedXYZPPN, + brightnessMixedXYZNPN, + renderBlocks.renderMaxZ * (1.0D - renderBlocks.renderMinX), + renderBlocks.renderMaxZ * renderBlocks.renderMinX, + (1.0D - renderBlocks.renderMaxZ) * renderBlocks.renderMinX, + (1.0D - renderBlocks.renderMaxZ) * (1.0D - renderBlocks.renderMinX)); + } + + return this; + } + + /** + * Sets up lighting for the North face and returns the {@link LightingHelper}. + * <p> + * This is a consolidated <code>method</code> that sets side shading with respect to the following attributes: + * <p> + * <ul> + * <li>{@link RenderBlocks#enableAO}</li> + * <li>{@link RenderBlocks#partialRenderBounds}</li> + * </ul> + * + * @param block the block {@link Block} + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate + * @return the {@link LightingHelper} + */ + public LightingHelper setupLightingZNeg(Block block, int x, int y, int z) { + + if (renderBlocks.enableAO) { + + int zOffset = renderBlocks.renderMinZ > 0.0F + NO_Z_FIGHT_OFFSET ? z : z - 1; + + int mixedBrightness = block.getMixedBrightnessForBlock(renderBlocks.blockAccess, x, y, zOffset); + brightness = mixedBrightness; + + float ratio = (float) (1.0F - renderBlocks.renderMinZ); + float aoLightValue = renderBlocks.blockAccess.getBlock(x, y, z - 1) + .getAmbientOcclusionLightValue(); + + renderBlocks.aoBrightnessXZNN = block + .getMixedBrightnessForBlock(renderBlocks.blockAccess, x - 1, y, zOffset); + renderBlocks.aoBrightnessYZNN = block + .getMixedBrightnessForBlock(renderBlocks.blockAccess, x, y - 1, zOffset); + renderBlocks.aoBrightnessYZPN = block + .getMixedBrightnessForBlock(renderBlocks.blockAccess, x, y + 1, zOffset); + renderBlocks.aoBrightnessXZPN = block + .getMixedBrightnessForBlock(renderBlocks.blockAccess, x + 1, y, zOffset); + renderBlocks.aoBrightnessXYZNNN = block + .getMixedBrightnessForBlock(renderBlocks.blockAccess, x - 1, y - 1, zOffset); + renderBlocks.aoBrightnessXYZNPN = block + .getMixedBrightnessForBlock(renderBlocks.blockAccess, x - 1, y + 1, zOffset); + renderBlocks.aoBrightnessXYZPNN = block + .getMixedBrightnessForBlock(renderBlocks.blockAccess, x + 1, y - 1, zOffset); + renderBlocks.aoBrightnessXYZPPN = block + .getMixedBrightnessForBlock(renderBlocks.blockAccess, x + 1, y + 1, zOffset); + renderBlocks.aoLightValueScratchXZNN = getMixedAo( + renderBlocks.blockAccess.getBlock(x - 1, y, z - 1) + .getAmbientOcclusionLightValue(), + renderBlocks.blockAccess.getBlock(x - 1, y, z) + .getAmbientOcclusionLightValue(), + ratio); + renderBlocks.aoLightValueScratchYZNN = getMixedAo( + renderBlocks.blockAccess.getBlock(x, y - 1, z - 1) + .getAmbientOcclusionLightValue(), + renderBlocks.blockAccess.getBlock(x, y - 1, z) + .getAmbientOcclusionLightValue(), + ratio); + renderBlocks.aoLightValueScratchYZPN = getMixedAo( + renderBlocks.blockAccess.getBlock(x, y + 1, z - 1) + .getAmbientOcclusionLightValue(), + renderBlocks.blockAccess.getBlock(x, y + 1, z) + .getAmbientOcclusionLightValue(), + ratio); + renderBlocks.aoLightValueScratchXZPN = getMixedAo( + renderBlocks.blockAccess.getBlock(x + 1, y, z - 1) + .getAmbientOcclusionLightValue(), + renderBlocks.blockAccess.getBlock(x + 1, y, z) + .getAmbientOcclusionLightValue(), + ratio); + renderBlocks.aoLightValueScratchXYZNNN = getMixedAo( + renderBlocks.blockAccess.getBlock(x - 1, y - 1, z - 1) + .getAmbientOcclusionLightValue(), + renderBlocks.blockAccess.getBlock(x - 1, y - 1, z) + .getAmbientOcclusionLightValue(), + ratio); + renderBlocks.aoLightValueScratchXYZNPN = getMixedAo( + renderBlocks.blockAccess.getBlock(x - 1, y + 1, z - 1) + .getAmbientOcclusionLightValue(), + renderBlocks.blockAccess.getBlock(x - 1, y + 1, z) + .getAmbientOcclusionLightValue(), + ratio); + renderBlocks.aoLightValueScratchXYZPNN = getMixedAo( + renderBlocks.blockAccess.getBlock(x + 1, y - 1, z - 1) + .getAmbientOcclusionLightValue(), + renderBlocks.blockAccess.getBlock(x + 1, y - 1, z) + .getAmbientOcclusionLightValue(), + ratio); + renderBlocks.aoLightValueScratchXYZPPN = getMixedAo( + renderBlocks.blockAccess.getBlock(x + 1, y + 1, z - 1) + .getAmbientOcclusionLightValue(), + renderBlocks.blockAccess.getBlock(x + 1, y + 1, z) + .getAmbientOcclusionLightValue(), + ratio); + + int brightnessMixedXYZPPN = renderBlocks.getAoBrightness( + renderBlocks.aoBrightnessYZPN, + renderBlocks.aoBrightnessXZPN, + renderBlocks.aoBrightnessXYZPPN, + mixedBrightness); + int brightnessMixedXYZPNN = renderBlocks.getAoBrightness( + renderBlocks.aoBrightnessYZNN, + renderBlocks.aoBrightnessXYZPNN, + renderBlocks.aoBrightnessXZPN, + mixedBrightness); + int brightnessMixedXYZNNN = renderBlocks.getAoBrightness( + renderBlocks.aoBrightnessXYZNNN, + renderBlocks.aoBrightnessXZNN, + renderBlocks.aoBrightnessYZNN, + mixedBrightness); + int brightnessMixedXYZNPN = renderBlocks.getAoBrightness( + renderBlocks.aoBrightnessXZNN, + renderBlocks.aoBrightnessXYZNPN, + renderBlocks.aoBrightnessYZPN, + mixedBrightness); + + float aoMixedXYZPPN = (aoLightValue + renderBlocks.aoLightValueScratchYZPN + + renderBlocks.aoLightValueScratchXZPN + + renderBlocks.aoLightValueScratchXYZPPN) / 4.0F; + float aoMixedXYZPNN = (renderBlocks.aoLightValueScratchYZNN + aoLightValue + + renderBlocks.aoLightValueScratchXYZPNN + + renderBlocks.aoLightValueScratchXZPN) / 4.0F; + float aoMixedXYZNNN = (renderBlocks.aoLightValueScratchXYZNNN + renderBlocks.aoLightValueScratchXZNN + + renderBlocks.aoLightValueScratchYZNN + + aoLightValue) / 4.0F; + float aoMixedXYZNPN = (renderBlocks.aoLightValueScratchXZNN + renderBlocks.aoLightValueScratchXYZNPN + + aoLightValue + + renderBlocks.aoLightValueScratchYZPN) / 4.0F; + + aoTopLeft = (float) (aoMixedXYZNPN * renderBlocks.renderMaxY * (1.0D - renderBlocks.renderMinX) + + aoMixedXYZPPN * renderBlocks.renderMaxY * renderBlocks.renderMinX + + aoMixedXYZPNN * (1.0D - renderBlocks.renderMaxY) * renderBlocks.renderMinX + + aoMixedXYZNNN * (1.0D - renderBlocks.renderMaxY) * (1.0D - renderBlocks.renderMinX)); + aoBottomLeft = (float) (aoMixedXYZNPN * renderBlocks.renderMaxY * (1.0D - renderBlocks.renderMaxX) + + aoMixedXYZPPN * renderBlocks.renderMaxY * renderBlocks.renderMaxX + + aoMixedXYZPNN * (1.0D - renderBlocks.renderMaxY) * renderBlocks.renderMaxX + + aoMixedXYZNNN * (1.0D - renderBlocks.renderMaxY) * (1.0D - renderBlocks.renderMaxX)); + aoBottomRight = (float) (aoMixedXYZNPN * renderBlocks.renderMinY * (1.0D - renderBlocks.renderMaxX) + + aoMixedXYZPPN * renderBlocks.renderMinY * renderBlocks.renderMaxX + + aoMixedXYZPNN * (1.0D - renderBlocks.renderMinY) * renderBlocks.renderMaxX + + aoMixedXYZNNN * (1.0D - renderBlocks.renderMinY) * (1.0D - renderBlocks.renderMaxX)); + aoTopRight = (float) (aoMixedXYZNPN * renderBlocks.renderMinY * (1.0D - renderBlocks.renderMinX) + + aoMixedXYZPPN * renderBlocks.renderMinY * renderBlocks.renderMinX + + aoMixedXYZPNN * (1.0D - renderBlocks.renderMinY) * renderBlocks.renderMinX + + aoMixedXYZNNN * (1.0D - renderBlocks.renderMinY) * (1.0D - renderBlocks.renderMinX)); + + renderBlocks.brightnessTopLeft = renderBlocks.mixAoBrightness( + brightnessMixedXYZNPN, + brightnessMixedXYZPPN, + brightnessMixedXYZPNN, + brightnessMixedXYZNNN, + renderBlocks.renderMaxY * (1.0D - renderBlocks.renderMinX), + renderBlocks.renderMaxY * renderBlocks.renderMinX, + (1.0D - renderBlocks.renderMaxY) * renderBlocks.renderMinX, + (1.0D - renderBlocks.renderMaxY) * (1.0D - renderBlocks.renderMinX)); + renderBlocks.brightnessBottomLeft = renderBlocks.mixAoBrightness( + brightnessMixedXYZNPN, + brightnessMixedXYZPPN, + brightnessMixedXYZPNN, + brightnessMixedXYZNNN, + renderBlocks.renderMaxY * (1.0D - renderBlocks.renderMaxX), + renderBlocks.renderMaxY * renderBlocks.renderMaxX, + (1.0D - renderBlocks.renderMaxY) * renderBlocks.renderMaxX, + (1.0D - renderBlocks.renderMaxY) * (1.0D - renderBlocks.renderMaxX)); + renderBlocks.brightnessBottomRight = renderBlocks.mixAoBrightness( + brightnessMixedXYZNPN, + brightnessMixedXYZPPN, + brightnessMixedXYZPNN, + brightnessMixedXYZNNN, + renderBlocks.renderMinY * (1.0D - renderBlocks.renderMaxX), + renderBlocks.renderMinY * renderBlocks.renderMaxX, + (1.0D - renderBlocks.renderMinY) * renderBlocks.renderMaxX, + (1.0D - renderBlocks.renderMinY) * (1.0D - renderBlocks.renderMaxX)); + renderBlocks.brightnessTopRight = renderBlocks.mixAoBrightness( + brightnessMixedXYZNPN, + brightnessMixedXYZPPN, + brightnessMixedXYZPNN, + brightnessMixedXYZNNN, + renderBlocks.renderMinY * (1.0D - renderBlocks.renderMinX), + renderBlocks.renderMinY * renderBlocks.renderMinX, + (1.0D - renderBlocks.renderMinY) * renderBlocks.renderMinX, + (1.0D - renderBlocks.renderMinY) * (1.0D - renderBlocks.renderMinX)); + } + + return this; + } + + /** + * Sets up lighting for the South face and returns the {@link LightingHelper}. + * <p> + * This is a consolidated <code>method</code> that sets side shading with respect to the following attributes: + * <p> + * <ul> + * <li>{@link RenderBlocks#enableAO}</li> + * <li>{@link RenderBlocks#partialRenderBounds}</li> + * </ul> + * + * @param block the block {@link Block} + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate + * @return the {@link LightingHelper} + */ + public LightingHelper setupLightingZPos(Block block, int x, int y, int z) { + + if (renderBlocks.enableAO) { + + int zOffset = renderBlocks.renderMaxZ < 1.0F - NO_Z_FIGHT_OFFSET ? z : z + 1; + + int mixedBrightness = block.getMixedBrightnessForBlock(renderBlocks.blockAccess, x, y, zOffset); + brightness = mixedBrightness; + + float aoLightValue = renderBlocks.blockAccess.getBlock(x, y, z + 1) + .getAmbientOcclusionLightValue(); + + renderBlocks.aoBrightnessXZNP = block + .getMixedBrightnessForBlock(renderBlocks.blockAccess, x - 1, y, zOffset); + renderBlocks.aoBrightnessXZPP = block + .getMixedBrightnessForBlock(renderBlocks.blockAccess, x + 1, y, zOffset); + renderBlocks.aoBrightnessYZNP = block + .getMixedBrightnessForBlock(renderBlocks.blockAccess, x, y - 1, zOffset); + renderBlocks.aoBrightnessYZPP = block + .getMixedBrightnessForBlock(renderBlocks.blockAccess, x, y + 1, zOffset); + renderBlocks.aoBrightnessXYZNNP = block + .getMixedBrightnessForBlock(renderBlocks.blockAccess, x - 1, y - 1, zOffset); + renderBlocks.aoBrightnessXYZNPP = block + .getMixedBrightnessForBlock(renderBlocks.blockAccess, x - 1, y + 1, zOffset); + renderBlocks.aoBrightnessXYZPNP = block + .getMixedBrightnessForBlock(renderBlocks.blockAccess, x + 1, y - 1, zOffset); + renderBlocks.aoBrightnessXYZPPP = block + .getMixedBrightnessForBlock(renderBlocks.blockAccess, x + 1, y + 1, zOffset); + renderBlocks.aoLightValueScratchXZNP = getMixedAo( + renderBlocks.blockAccess.getBlock(x - 1, y, z + 1) + .getAmbientOcclusionLightValue(), + renderBlocks.blockAccess.getBlock(x - 1, y, z) + .getAmbientOcclusionLightValue(), + renderBlocks.renderMaxZ); + renderBlocks.aoLightValueScratchXZPP = getMixedAo( + renderBlocks.blockAccess.getBlock(x + 1, y, z + 1) + .getAmbientOcclusionLightValue(), + renderBlocks.blockAccess.getBlock(x + 1, y, z) + .getAmbientOcclusionLightValue(), + renderBlocks.renderMaxZ); + renderBlocks.aoLightValueScratchYZNP = getMixedAo( + renderBlocks.blockAccess.getBlock(x, y - 1, z + 1) + .getAmbientOcclusionLightValue(), + renderBlocks.blockAccess.getBlock(x, y - 1, z) + .getAmbientOcclusionLightValue(), + renderBlocks.renderMaxZ); + renderBlocks.aoLightValueScratchYZPP = getMixedAo( + renderBlocks.blockAccess.getBlock(x, y + 1, z + 1) + .getAmbientOcclusionLightValue(), + renderBlocks.blockAccess.getBlock(x, y + 1, z) + .getAmbientOcclusionLightValue(), + renderBlocks.renderMaxZ); + renderBlocks.aoLightValueScratchXYZNNP = getMixedAo( + renderBlocks.blockAccess.getBlock(x - 1, y - 1, z + 1) + .getAmbientOcclusionLightValue(), + renderBlocks.blockAccess.getBlock(x - 1, y - 1, z) + .getAmbientOcclusionLightValue(), + renderBlocks.renderMaxZ); + renderBlocks.aoLightValueScratchXYZNPP = getMixedAo( + renderBlocks.blockAccess.getBlock(x - 1, y + 1, z + 1) + .getAmbientOcclusionLightValue(), + renderBlocks.blockAccess.getBlock(x - 1, y + 1, z) + .getAmbientOcclusionLightValue(), + renderBlocks.renderMaxZ); + renderBlocks.aoLightValueScratchXYZPNP = getMixedAo( + renderBlocks.blockAccess.getBlock(x + 1, y - 1, z + 1) + .getAmbientOcclusionLightValue(), + renderBlocks.blockAccess.getBlock(x + 1, y - 1, z) + .getAmbientOcclusionLightValue(), + renderBlocks.renderMaxZ); + renderBlocks.aoLightValueScratchXYZPPP = getMixedAo( + renderBlocks.blockAccess.getBlock(x + 1, y + 1, z + 1) + .getAmbientOcclusionLightValue(), + renderBlocks.blockAccess.getBlock(x + 1, y + 1, z) + .getAmbientOcclusionLightValue(), + renderBlocks.renderMaxZ); + + int brightnessMixedXYZNPP = renderBlocks.getAoBrightness( + renderBlocks.aoBrightnessXZNP, + renderBlocks.aoBrightnessXYZNPP, + renderBlocks.aoBrightnessYZPP, + mixedBrightness); + int brightnessMixedXYZNNP = renderBlocks.getAoBrightness( + renderBlocks.aoBrightnessXYZNNP, + renderBlocks.aoBrightnessXZNP, + renderBlocks.aoBrightnessYZNP, + mixedBrightness); + int brightnessMixedXYZPNP = renderBlocks.getAoBrightness( + renderBlocks.aoBrightnessYZNP, + renderBlocks.aoBrightnessXYZPNP, + renderBlocks.aoBrightnessXZPP, + mixedBrightness); + int brightnessMixedXYZPPP = renderBlocks.getAoBrightness( + renderBlocks.aoBrightnessYZPP, + renderBlocks.aoBrightnessXZPP, + renderBlocks.aoBrightnessXYZPPP, + mixedBrightness); + + float aoMixedXYZNPP = (renderBlocks.aoLightValueScratchXZNP + renderBlocks.aoLightValueScratchXYZNPP + + aoLightValue + + renderBlocks.aoLightValueScratchYZPP) / 4.0F; + float aoMixedXYZNNP = (renderBlocks.aoLightValueScratchXYZNNP + renderBlocks.aoLightValueScratchXZNP + + renderBlocks.aoLightValueScratchYZNP + + aoLightValue) / 4.0F; + float aoMixedXYZPNP = (renderBlocks.aoLightValueScratchYZNP + aoLightValue + + renderBlocks.aoLightValueScratchXYZPNP + + renderBlocks.aoLightValueScratchXZPP) / 4.0F; + float aoMixedXYZPPP = (aoLightValue + renderBlocks.aoLightValueScratchYZPP + + renderBlocks.aoLightValueScratchXZPP + + renderBlocks.aoLightValueScratchXYZPPP) / 4.0F; + + aoTopLeft = (float) (aoMixedXYZNPP * renderBlocks.renderMaxY * (1.0D - renderBlocks.renderMinX) + + aoMixedXYZPPP * renderBlocks.renderMaxY * renderBlocks.renderMinX + + aoMixedXYZPNP * (1.0D - renderBlocks.renderMaxY) * renderBlocks.renderMinX + + aoMixedXYZNNP * (1.0D - renderBlocks.renderMaxY) * (1.0D - renderBlocks.renderMinX)); + aoBottomLeft = (float) (aoMixedXYZNPP * renderBlocks.renderMinY * (1.0D - renderBlocks.renderMinX) + + aoMixedXYZPPP * renderBlocks.renderMinY * renderBlocks.renderMinX + + aoMixedXYZPNP * (1.0D - renderBlocks.renderMinY) * renderBlocks.renderMinX + + aoMixedXYZNNP * (1.0D - renderBlocks.renderMinY) * (1.0D - renderBlocks.renderMinX)); + aoBottomRight = (float) (aoMixedXYZNPP * renderBlocks.renderMinY * (1.0D - renderBlocks.renderMaxX) + + aoMixedXYZPPP * renderBlocks.renderMinY * renderBlocks.renderMaxX + + aoMixedXYZPNP * (1.0D - renderBlocks.renderMinY) * renderBlocks.renderMaxX + + aoMixedXYZNNP * (1.0D - renderBlocks.renderMinY) * (1.0D - renderBlocks.renderMaxX)); + aoTopRight = (float) (aoMixedXYZNPP * renderBlocks.renderMaxY * (1.0D - renderBlocks.renderMaxX) + + aoMixedXYZPPP * renderBlocks.renderMaxY * renderBlocks.renderMaxX + + aoMixedXYZPNP * (1.0D - renderBlocks.renderMaxY) * renderBlocks.renderMaxX + + aoMixedXYZNNP * (1.0D - renderBlocks.renderMaxY) * (1.0D - renderBlocks.renderMaxX)); + + renderBlocks.brightnessTopLeft = renderBlocks.mixAoBrightness( + brightnessMixedXYZNPP, + brightnessMixedXYZNNP, + brightnessMixedXYZPNP, + brightnessMixedXYZPPP, + renderBlocks.renderMaxY * (1.0D - renderBlocks.renderMinX), + (1.0D - renderBlocks.renderMaxY) * (1.0D - renderBlocks.renderMinX), + (1.0D - renderBlocks.renderMaxY) * renderBlocks.renderMinX, + renderBlocks.renderMaxY * renderBlocks.renderMinX); + renderBlocks.brightnessBottomLeft = renderBlocks.mixAoBrightness( + brightnessMixedXYZNPP, + brightnessMixedXYZNNP, + brightnessMixedXYZPNP, + brightnessMixedXYZPPP, + renderBlocks.renderMinY * (1.0D - renderBlocks.renderMinX), + (1.0D - renderBlocks.renderMinY) * (1.0D - renderBlocks.renderMinX), + (1.0D - renderBlocks.renderMinY) * renderBlocks.renderMinX, + renderBlocks.renderMinY * renderBlocks.renderMinX); + renderBlocks.brightnessBottomRight = renderBlocks.mixAoBrightness( + brightnessMixedXYZNPP, + brightnessMixedXYZNNP, + brightnessMixedXYZPNP, + brightnessMixedXYZPPP, + renderBlocks.renderMinY * (1.0D - renderBlocks.renderMaxX), + (1.0D - renderBlocks.renderMinY) * (1.0D - renderBlocks.renderMaxX), + (1.0D - renderBlocks.renderMinY) * renderBlocks.renderMaxX, + renderBlocks.renderMinY * renderBlocks.renderMaxX); + renderBlocks.brightnessTopRight = renderBlocks.mixAoBrightness( + brightnessMixedXYZNPP, + brightnessMixedXYZNNP, + brightnessMixedXYZPNP, + brightnessMixedXYZPPP, + renderBlocks.renderMaxY * (1.0D - renderBlocks.renderMaxX), + (1.0D - renderBlocks.renderMaxY) * (1.0D - renderBlocks.renderMaxX), + (1.0D - renderBlocks.renderMaxY) * renderBlocks.renderMaxX, + renderBlocks.renderMaxY * renderBlocks.renderMaxX); + } + + return this; + } +} diff --git a/src/main/java/gregtech/api/util/MethodsReturnNonnullByDefault.java b/src/main/java/gregtech/api/util/MethodsReturnNonnullByDefault.java new file mode 100644 index 0000000000..2b2c310695 --- /dev/null +++ b/src/main/java/gregtech/api/util/MethodsReturnNonnullByDefault.java @@ -0,0 +1,22 @@ +package gregtech.api.util; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import javax.annotation.Nonnull; +import javax.annotation.meta.TypeQualifierDefault; + +/** + * This annotation can be applied to a package or class to indicate that + * the methods in that element are nonnull by default unless there is: + * <ul> + * <li>An explicit nullness annotation + * <li>The method overrides a method in a superclass (in which case the + * annotation of the corresponding method in the superclass applies) + * </ul> + */ +@Nonnull +@TypeQualifierDefault({ ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +public @interface MethodsReturnNonnullByDefault {} diff --git a/src/main/java/gregtech/api/util/OutputHatchWrapper.java b/src/main/java/gregtech/api/util/OutputHatchWrapper.java new file mode 100644 index 0000000000..b2e74d24cf --- /dev/null +++ b/src/main/java/gregtech/api/util/OutputHatchWrapper.java @@ -0,0 +1,65 @@ +package gregtech.api.util; + +import java.util.function.Predicate; + +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.FluidTankInfo; + +import org.jetbrains.annotations.NotNull; + +import gregtech.api.interfaces.fluid.IFluidStore; +import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_Output; + +/** + * Wrapper for output hatch to allow multiblocks to apply specific filter. + */ +public class OutputHatchWrapper implements IFluidStore { + + private final GT_MetaTileEntity_Hatch_Output outputHatch; + private final Predicate<FluidStack> filter; + + public OutputHatchWrapper(GT_MetaTileEntity_Hatch_Output outputHatch, Predicate<FluidStack> filter) { + this.outputHatch = outputHatch; + this.filter = filter; + } + + @Override + public FluidStack getFluid() { + return outputHatch.getFluid(); + } + + @Override + public int getFluidAmount() { + return outputHatch.getFluidAmount(); + } + + @Override + public int getCapacity() { + return outputHatch.getCapacity(); + } + + @Override + public FluidTankInfo getInfo() { + return outputHatch.getInfo(); + } + + @Override + public int fill(FluidStack resource, boolean doFill) { + return outputHatch.fill(resource, doFill); + } + + @Override + public FluidStack drain(int maxDrain, boolean doDrain) { + return outputHatch.drain(maxDrain, doDrain); + } + + @Override + public boolean isEmptyAndAcceptsAnyFluid() { + return outputHatch.isEmptyAndAcceptsAnyFluid(); + } + + @Override + public boolean canStoreFluid(@NotNull FluidStack fluidStack) { + return outputHatch.canStoreFluid(fluidStack) && filter.test(fluidStack); + } +} diff --git a/src/main/java/gregtech/api/util/SemiFluidFuelHandler.java b/src/main/java/gregtech/api/util/SemiFluidFuelHandler.java new file mode 100644 index 0000000000..e3baa9ac90 --- /dev/null +++ b/src/main/java/gregtech/api/util/SemiFluidFuelHandler.java @@ -0,0 +1,136 @@ +package gregtech.api.util; + +import static gtPlusPlus.api.recipe.GTPPRecipeMaps.semiFluidFuels; + +import java.util.HashMap; + +import net.minecraft.item.ItemStack; +import net.minecraftforge.fluids.FluidContainerRegistry; +import net.minecraftforge.fluids.FluidStack; + +import gregtech.api.recipe.RecipeMaps; +import gtPlusPlus.api.objects.Logger; +import gtPlusPlus.api.objects.data.Pair; +import gtPlusPlus.core.util.minecraft.FluidUtils; + +public class SemiFluidFuelHandler { + + public static boolean addSemiFluidFuel(ItemStack aFuelItem, int aFuelValue) { + FluidStack p = FluidContainerRegistry.getFluidForFilledItem(aFuelItem); + if (p != null && aFuelValue > 0) { + return addSemiFluidFuel(p, aFuelValue); + } else { + Logger.INFO("Fuel value for " + aFuelItem.getDisplayName() + " is <= 0, ignoring."); + } + return false; + } + + public static boolean addSemiFluidFuel(FluidStack aFuel, int aFuelValue) { + FluidStack p = aFuel; + if (p != null && aFuelValue > 0) { + GT_Recipe aRecipe = new GT_Recipe( + true, + new ItemStack[] {}, + new ItemStack[] {}, + null, + new int[] {}, + new FluidStack[] { p }, + null, + 0, + 0, + aFuelValue); + if (aRecipe.mSpecialValue > 0) { + Logger.INFO( + "Added " + aRecipe.mFluidInputs[0].getLocalizedName() + + " to the Semi-Fluid Generator fuel map. Fuel Produces " + + (aRecipe.mSpecialValue * 1000) + + "EU per 1000L."); + semiFluidFuels.add(aRecipe); + return true; + } + } else { + Logger.INFO("Fuel value for " + p != null ? p.getLocalizedName() : "NULL Fluid" + " is <= 0, ignoring."); + } + return false; + } + + public static boolean generateFuels() { + final FluidStack aCreosote = FluidUtils.getFluidStack("creosote", 1000); + final FluidStack aHeavyFuel = FluidUtils.getFluidStack("liquid_heavy_fuel", 1000); + final FluidStack aHeavyOil = FluidUtils.getFluidStack("liquid_heavy_oil", 1000); + final HashMap<Integer, Pair<FluidStack, Integer>> aFoundFluidsFromItems = new HashMap<>(); + // Find Fluids From items + for (final GT_Recipe r : RecipeMaps.denseLiquidFuels.getAllRecipes()) { + + GT_Recipe g = r.copy(); + + if (g != null && g.mEnabled && g.mInputs.length > 0 && g.mInputs[0] != null) { + for (ItemStack i : g.mInputs) { + FluidStack f = FluidContainerRegistry.getFluidForFilledItem(i); + if (f != null) { + Pair<FluidStack, Integer> aData = new Pair<>(f, g.mSpecialValue); + aFoundFluidsFromItems.put(aData.hashCode(), aData); + } + } + } else if (g != null && g.mEnabled && g.mFluidInputs.length > 0 && g.mFluidInputs[0] != null) { + boolean aContainsCreosote = false; + for (FluidStack f : g.mFluidInputs) { + if (f.isFluidEqual(aCreosote)) { + aContainsCreosote = true; + } + } + g.mSpecialValue *= aContainsCreosote ? 6 : 3; + Logger.INFO( + "Added " + g.mFluidInputs[0].getLocalizedName() + + " to the Semi-Fluid Generator fuel map. Fuel Produces " + + g.mSpecialValue + + "EU per 1000L."); + semiFluidFuels.add(g); + } + } + for (Pair<FluidStack, Integer> p : aFoundFluidsFromItems.values()) { + if (p != null) { + int aFuelValue = p.getValue(); + if (p.getKey() + .isFluidEqual(aCreosote)) { + aFuelValue *= 6; + } else if (p.getKey() + .isFluidEqual(aHeavyFuel) + || p.getKey() + .isFluidEqual(aHeavyOil)) { + aFuelValue *= 1.5; + } else { + aFuelValue *= 2; + } + + if (aFuelValue <= (128 * 3)) { + GT_Recipe aRecipe = new GT_Recipe( + true, + new ItemStack[] {}, + new ItemStack[] {}, + null, + new int[] {}, + new FluidStack[] { p.getKey() }, + null, + 0, + 0, + aFuelValue); + if (aRecipe.mSpecialValue > 0) { + Logger.INFO( + "Added " + aRecipe.mFluidInputs[0].getLocalizedName() + + " to the Semi-Fluid Generator fuel map. Fuel Produces " + + (aRecipe.mSpecialValue * 1000) + + "EU per 1000L."); + semiFluidFuels.add(aRecipe); + } + } else { + Logger.INFO( + "Boosted Fuel value for " + p.getKey() + .getLocalizedName() + " exceeds 512k, ignoring."); + } + } + } + return !semiFluidFuels.getAllRecipes() + .isEmpty(); + } +} diff --git a/src/main/java/gregtech/api/util/ValidationResult.java b/src/main/java/gregtech/api/util/ValidationResult.java new file mode 100644 index 0000000000..497dfe67e5 --- /dev/null +++ b/src/main/java/gregtech/api/util/ValidationResult.java @@ -0,0 +1,24 @@ +package gregtech.api.util; + +public class ValidationResult<T> { + + private final ValidationType type; + private final T result; + + private ValidationResult(ValidationType type, T result) { + this.type = type; + this.result = result; + } + + public ValidationType getType() { + return this.type; + } + + public T getResult() { + return this.result; + } + + public static <T> ValidationResult<T> of(ValidationType result, T value) { + return new ValidationResult<>(result, value); + } +} diff --git a/src/main/java/gregtech/api/util/ValidationType.java b/src/main/java/gregtech/api/util/ValidationType.java new file mode 100644 index 0000000000..4417834430 --- /dev/null +++ b/src/main/java/gregtech/api/util/ValidationType.java @@ -0,0 +1,6 @@ +package gregtech.api.util; + +public enum ValidationType { + VALID, + INVALID +} diff --git a/src/main/java/gregtech/api/util/VoidProtectionHelper.java b/src/main/java/gregtech/api/util/VoidProtectionHelper.java new file mode 100644 index 0000000000..fdf47d06df --- /dev/null +++ b/src/main/java/gregtech/api/util/VoidProtectionHelper.java @@ -0,0 +1,485 @@ +package gregtech.api.util; + +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.PriorityQueue; +import java.util.function.Function; + +import net.minecraft.item.ItemStack; +import net.minecraftforge.fluids.FluidStack; + +import com.gtnewhorizon.gtnhlib.util.map.ItemStackMap; +import com.gtnewhorizons.modularui.api.fluids.IFluidTankLong; + +import gregtech.api.interfaces.fluid.IFluidStore; +import gregtech.api.interfaces.tileentity.IVoidable; +import gregtech.api.logic.FluidInventoryLogic; +import gregtech.api.logic.ItemInventoryLogic; + +/** + * Helper class to calculate how many parallels of items / fluids can fit in the output buses / hatches. + */ +public class VoidProtectionHelper { + + /** + * Machine used for calculation + */ + private IVoidable machine; + /** + * Does void protection enabled for items + */ + private boolean protectExcessItem; + /** + * Does void protection enabled for fluids + */ + private boolean protectExcessFluid; + /** + * The maximum possible parallel possible for the multiblock + */ + private int maxParallel = 1; + /** + * If item output is full. + */ + private boolean isItemFull; + /** + * If fluid output is full. + */ + private boolean isFluidFull; + /** + * The item outputs to check + */ + private ItemStack[] itemOutputs; + /** + * The fluid outputs to check + */ + private FluidStack[] fluidOutputs; + /** + * The item output inventory + */ + private ItemInventoryLogic itemOutputInventory; + /** + * The fluid output inventory + */ + private FluidInventoryLogic fluidOutputInventory; + /** + * Has this helper been built? + */ + private boolean built; + /** + * Is this helper working for a MuTE? + */ + private boolean muteMode; + /** + * Multiplier by which the output will be multiplied + */ + private int outputMultiplier = 1; + /** + * Multiplier that is applied on the output chances + */ + private double chanceMultiplier = 1; + + private Function<Integer, Integer> chanceGetter = i -> 10000; + + public VoidProtectionHelper() {} + + /** + * Sets machine, with current configuration for void protection mode. + */ + public VoidProtectionHelper setMachine(IVoidable machine) { + return setMachine(machine, machine.protectsExcessItem(), machine.protectsExcessFluid()); + } + + /** + * Sets machine, with void protection mode forcibly. + */ + public VoidProtectionHelper setMachine(IVoidable machine, boolean protectExcessItem, boolean protectExcessFluid) { + this.protectExcessItem = protectExcessItem; + this.protectExcessFluid = protectExcessFluid; + this.machine = machine; + return this; + } + + public VoidProtectionHelper setItemOutputs(ItemStack[] itemOutputs) { + this.itemOutputs = itemOutputs; + return this; + } + + public VoidProtectionHelper setFluidOutputs(FluidStack[] fluidOutputs) { + this.fluidOutputs = fluidOutputs; + return this; + } + + /** + * Sets the MaxParallel a multi can handle + */ + public VoidProtectionHelper setMaxParallel(int maxParallel) { + this.maxParallel = maxParallel; + return this; + } + + public VoidProtectionHelper setItemOutputInventory(ItemInventoryLogic itemOutputInventory) { + this.itemOutputInventory = itemOutputInventory; + return this; + } + + public VoidProtectionHelper setFluidOutputInventory(FluidInventoryLogic fluidOutputInventory) { + this.fluidOutputInventory = fluidOutputInventory; + return this; + } + + public VoidProtectionHelper setMuTEMode(boolean muteMode) { + this.muteMode = muteMode; + return this; + } + + public VoidProtectionHelper setOutputMultiplier(int outputMultiplier) { + this.outputMultiplier = outputMultiplier; + return this; + } + + public VoidProtectionHelper setChanceMultiplier(double chanceMultiplier) { + this.chanceMultiplier = chanceMultiplier; + return this; + } + + public VoidProtectionHelper setChangeGetter(Function<Integer, Integer> getter) { + this.chanceGetter = getter; + return this; + } + + /** + * Finishes the VoidProtectionHelper. Anything changed after this will not affect anything + */ + public VoidProtectionHelper build() { + if (built) { + throw new IllegalStateException("Tried to build twice"); + } + if (machine == null) { + throw new IllegalStateException("Machine is not set"); + } + built = true; + determineParallel(); + return this; + } + + /** + * @return The current parallels possible by the multiblock + */ + public int getMaxParallel() { + if (!built) { + throw new IllegalStateException("Tried to get parallels before building"); + } + return maxParallel; + } + + /** + * @return If the calculation resulted in item output being full. + */ + public boolean isItemFull() { + if (!built) { + throw new IllegalStateException("Tried to get isItemFull before building"); + } + return isItemFull; + } + + /** + * @return If the calculation resulted in fluid output being full. + */ + public boolean isFluidFull() { + if (!built) { + throw new IllegalStateException("Tried to get isFluidFull before building"); + } + return isFluidFull; + } + + /** + * Called by {@link #build()}. Determines the parallels and everything else that needs to be done at build time + */ + private void determineParallel() { + if (itemOutputs == null) { + itemOutputs = new ItemStack[0]; + } + if (fluidOutputs == null) { + fluidOutputs = new FluidStack[0]; + } + + // Don't check IVoidable#protectsExcessItem nor #protectsExcessFluid here, + // to allow more involved setting for void protections (see ComplexParallelProcessingLogic) + if (protectExcessItem && itemOutputs.length > 0 && !machine.canDumpItemToME()) { + maxParallel = Math.min(calculateMaxItemParallels(), maxParallel); + if (maxParallel <= 0) { + isItemFull = true; + return; + } + } + if (protectExcessFluid && fluidOutputs.length > 0 && !machine.canDumpFluidToME()) { + maxParallel = Math.min(calculateMaxFluidParallels(), maxParallel); + if (maxParallel <= 0) { + isFluidFull = true; + } + } + } + + /** + * Calculates the max parallel for fluids if void protection is turned on + */ + private int calculateMaxFluidParallels() { + List<? extends IFluidStore> hatches = machine.getFluidOutputSlots(fluidOutputs); + if (hatches.size() < fluidOutputs.length) { + return 0; + } + + // A map to hold the items we will be 'inputting' into the output hatches. These fluidstacks are actually + // the recipe outputs. + Map<FluidStack, Integer> tFluidOutputMap = new HashMap<>(); + + // Map that keeps track of the number of parallel crafts we can accommodate for each fluid output. + // In the pair, we keep track of number of full crafts plus mb of fluid in a partial craft, to avoid + // issues with floating point math not being completely accurate when summing. + Map<FluidStack, ParallelData> tParallels = new HashMap<>(); + + // Iterate over the outputs, calculating require stack spacing they will require. + for (FluidStack aY : fluidOutputs) { + if (aY == null || aY.amount <= 0) { + continue; + } + tFluidOutputMap.merge(aY, aY.amount, Integer::sum); + tParallels.put(aY, new ParallelData(0, 0)); + } + + if (tFluidOutputMap.isEmpty()) { + // nothing to output, bail early + return maxParallel; + } + + for (IFluidStore tHatch : hatches) { + int tSpaceLeft = tHatch.getCapacity() - tHatch.getFluidAmount(); + + // check if hatch filled + if (tSpaceLeft <= 0) continue; + + // check if hatch is empty and unrestricted + if (tHatch.isEmptyAndAcceptsAnyFluid()) continue; + + for (Map.Entry<FluidStack, ParallelData> entry : tParallels.entrySet()) { + FluidStack tFluidOutput = entry.getKey(); + if (!tHatch.canStoreFluid(tFluidOutput)) continue; + // this fluid is not prevented by restrictions on output hatch + ParallelData tParallel = entry.getValue(); + Integer tCraftSize = tFluidOutputMap.get(tFluidOutput); + tParallel.batch += (tParallel.partial + tSpaceLeft) / tCraftSize; + tParallel.partial = (tParallel.partial + tSpaceLeft) % tCraftSize; + } + } + // now that all partial/restricted hatches have been counted, create a priority queue for our outputs + // the lowest priority fluid is the number of complete parallel crafts we can support + PriorityQueue<ParallelStackInfo<FluidStack>> aParallelQueue = new PriorityQueue<>( + Comparator.comparing(i -> i.batch)); + for (Map.Entry<FluidStack, ParallelData> entry : tParallels.entrySet()) { + aParallelQueue + .add(new ParallelStackInfo<>(entry.getValue().batch, entry.getValue().partial, entry.getKey())); + } + // add extra parallels for open slots as well + for (IFluidStore tHatch : hatches) { + // partially filled or restricted hatch. done in the last pass + if (!tHatch.isEmptyAndAcceptsAnyFluid()) continue; + + ParallelStackInfo<FluidStack> tParallel = aParallelQueue.poll(); + assert tParallel != null; // will always be true, specifying assert here to avoid IDE/compiler warnings + Integer tCraftSize = tFluidOutputMap.get(tParallel.stack); + int tSpaceLeft = tHatch.getCapacity(); + tParallel.batch += (tParallel.partial + tSpaceLeft) / tCraftSize; + tParallel.partial = (tParallel.partial + tSpaceLeft) % tCraftSize; + aParallelQueue.add(tParallel); + } + return aParallelQueue.element().batch; + } + + private int calculateMaxFluidParallelsMuTE() { + if (fluidOutputs.length > fluidOutputInventory.getInventory() + .getTanks()) { + return 0; + } + + // A map to hold the items we will be 'inputting' into the output hatches. These fluidstacks are actually + // the recipe outputs. + Map<FluidStack, Integer> tFluidOutputMap = new HashMap<>(); + + // Map that keeps track of the number of parallel crafts we can accommodate for each fluid output. + // In the pair, we keep track of number of full crafts plus mb of fluid in a partial craft, to avoid + // issues with floating point math not being completely accurate when summing. + Map<FluidStack, ParallelData> tParallels = new HashMap<>(); + + // Iterate over the outputs, calculating require stack spacing they will require. + for (FluidStack aY : fluidOutputs) { + if (aY == null) continue; + int fluidAmount = aY.amount * outputMultiplier; + if (fluidAmount <= 0) continue; + tFluidOutputMap.merge(aY, fluidAmount, Integer::sum); + tParallels.put(aY, new ParallelData(0, 0)); + } + + if (tFluidOutputMap.isEmpty()) { + // nothing to output, bail early + return maxParallel; + } + + for (int i = 0; i < fluidOutputInventory.getInventory() + .getTanks(); i++) { + IFluidTankLong tank = fluidOutputInventory.getInventory() + .getFluidTank(i); + long tSpaceLeft = tank.getCapacityLong() - tank.getFluidAmountLong(); + // check if hatch filled + if (tSpaceLeft <= 0) continue; + // check if hatch is empty and unrestricted + if (tank.getStoredFluid() == null) continue; + + for (Map.Entry<FluidStack, ParallelData> entry : tParallels.entrySet()) { + FluidStack tFluidOutput = entry.getKey(); + if (tank.fill(tFluidOutput.getFluid(), tFluidOutput.amount, false) == tFluidOutput.amount) continue; + // this fluid is not prevented by restrictions on output hatch + ParallelData tParallel = entry.getValue(); + Integer tCraftSize = tFluidOutputMap.get(tFluidOutput); + tParallel.batch += (tParallel.partial + tSpaceLeft) / tCraftSize; + tParallel.partial = (tParallel.partial + tSpaceLeft) % tCraftSize; + } + } + // now that all partial/restricted hatches have been counted, create a priority queue for our outputs + // the lowest priority fluid is the number of complete parallel crafts we can support + PriorityQueue<ParallelStackInfo<FluidStack>> aParallelQueue = new PriorityQueue<>( + Comparator.comparing(i -> i.batch)); + for (Map.Entry<FluidStack, ParallelData> entry : tParallels.entrySet()) { + aParallelQueue + .add(new ParallelStackInfo<>(entry.getValue().batch, entry.getValue().partial, entry.getKey())); + } + // add extra parallels for open slots as well + for (int i = 0; i < fluidOutputInventory.getInventory() + .getTanks(); i++) { + IFluidTankLong tank = fluidOutputInventory.getInventory() + .getFluidTank(i); + // partially filled or restricted hatch. done in the last pass + if (tank.getStoredFluid() != null) continue; + + ParallelStackInfo<FluidStack> tParallel = aParallelQueue.poll(); + assert tParallel != null; // will always be true, specifying assert here to avoid IDE/compiler warnings + Integer tCraftSize = tFluidOutputMap.get(tParallel.stack); + long tSpaceLeft = tank.getCapacityLong(); + tParallel.batch += (tParallel.partial + tSpaceLeft) / tCraftSize; + tParallel.partial = (tParallel.partial + tSpaceLeft) % tCraftSize; + aParallelQueue.add(tParallel); + } + + return aParallelQueue.element().batch; + } + + /** + * Calculates the max parallels one can do with items if void protection is on + */ + private int calculateMaxItemParallels() { + List<ItemStack> busStacks; + + if (muteMode) { + busStacks = itemOutputInventory.getInventory() + .getStacks(); + } else { + busStacks = machine.getItemOutputSlots(itemOutputs); + } + // A map to hold the items we will be 'inputting' into the output buses. These itemstacks are actually the + // recipe outputs. + Map<ItemStack, Integer> tItemOutputMap = new ItemStackMap<>(); + + // Map that keeps track of the number of parallel crafts we can accommodate for each item output. + // In the pair, we keep track of number of full crafts plus number of items in a partial craft, to avoid + // issues with floating point math not being completely accurate when summing. + Map<ItemStack, ParallelData> tParallels = new ItemStackMap<>(); + int tSlotsFree = 0; + int index = 0; + for (ItemStack tItem : itemOutputs) { + // GT_RecipeBuilder doesn't handle null item output + if (tItem == null) continue; + int itemStackSize = (int) (tItem.stackSize * outputMultiplier + * Math.ceil(chanceMultiplier * chanceGetter.apply(index++) / 10000)); + if (itemStackSize <= 0) continue; + tItemOutputMap.merge(tItem, itemStackSize, Integer::sum); + tParallels.put(tItem, new ParallelData(0, 0)); + } + + if (tItemOutputMap.isEmpty()) { + // nothing to output, bail early + return maxParallel; + } + + if (itemOutputs.length > 0) { + for (ItemStack tBusStack : busStacks) { + if (tBusStack == null) { + tSlotsFree++; + } else { + // get the real stack size + // we ignore the bus inventory stack limit here as no one set it to anything other than 64 + int tMaxBusStackSize = tBusStack.getMaxStackSize(); + if (tBusStack.stackSize >= tMaxBusStackSize) + // this bus stack is full. no checking + continue; + int tSpaceLeft = tMaxBusStackSize - tBusStack.stackSize; + Integer tCraftSize = tItemOutputMap.get(tBusStack); + if (tCraftSize == null) { + // we don't have a matching stack to output, ignore this bus stack + continue; + } + ParallelData tParallel = tParallels.get(tBusStack); + tParallel.batch += (tParallel.partial + tSpaceLeft) / tCraftSize; + tParallel.partial = (tParallel.partial + tSpaceLeft) % tCraftSize; + } + + } + // now that all partial stacks have been counted, create a priority queue for our outputs + // the lowest priority item is the number of complete parallel crafts we can support + PriorityQueue<ParallelStackInfo<ItemStack>> aParallelQueue = new PriorityQueue<>( + Comparator.comparing(i -> i.batch)); + for (Map.Entry<ItemStack, ParallelData> entry : tParallels.entrySet()) { + aParallelQueue + .add(new ParallelStackInfo<>(entry.getValue().batch, entry.getValue().partial, entry.getKey())); + } + + while (tSlotsFree > 0) { + ParallelStackInfo<ItemStack> tParallel = aParallelQueue.poll(); + assert tParallel != null; // will always be true, specifying assert here to avoid IDE/compiler warnings + Integer tCraftSize = tItemOutputMap.get(tParallel.stack); + int tStackSize = tParallel.stack.getMaxStackSize(); + tParallel.batch += (tParallel.partial + tStackSize) / tCraftSize; + tParallel.partial = (tParallel.partial + tStackSize) % tCraftSize; + aParallelQueue.add(tParallel); + --tSlotsFree; + } + + return aParallelQueue.element().batch; + } + return 0; + } + + private static class ParallelData { + + private int batch; + private long partial; + + private ParallelData(int batch, long partial) { + this.batch = batch; + this.partial = partial; + } + } + + private static class ParallelStackInfo<T> { + + private int batch; + private long partial; + private final T stack; + + private ParallelStackInfo(int batch, long partial, T stack) { + this.batch = batch; + this.partial = partial; + this.stack = stack; + } + } +} diff --git a/src/main/java/gregtech/api/util/WorldSpawnedEventBuilder.java b/src/main/java/gregtech/api/util/WorldSpawnedEventBuilder.java new file mode 100644 index 0000000000..f2bcce2bf8 --- /dev/null +++ b/src/main/java/gregtech/api/util/WorldSpawnedEventBuilder.java @@ -0,0 +1,678 @@ +package gregtech.api.util; + +import java.util.Objects; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.util.Vec3; +import net.minecraft.world.World; + +@SuppressWarnings("unused") +public abstract class WorldSpawnedEventBuilder implements Runnable { + + private static final String ILLEGAL_STATE_STR1 = "Position, identifier and world must be set"; + /* Variables */ + + private World world; + + /* Getters, Setters */ + + public World getWorld() { + return world; + } + + public WorldSpawnedEventBuilder setWorld(World world) { + this.world = world; + return this; + } + + /* Methods */ + + @SuppressWarnings("unchecked") + public <U extends WorldSpawnedEventBuilder> void times(int times, Consumer<U> action) { + Objects.requireNonNull(action); + for (int i = 0; i < times; i++) { + action.accept((U) this); + } + } + + @SuppressWarnings("unchecked") + public <U extends WorldSpawnedEventBuilder> void times(int times, BiConsumer<U, Integer> action) { + Objects.requireNonNull(action); + for (int i = 0; i < times; i++) { + action.accept((U) this, i); + } + } + + /* Interfaces */ + + private interface IPositionedWorldSpawnedEvent { + + Vec3 getPosition(); + + IPositionedWorldSpawnedEvent setPosition(Vec3 position); + + IPositionedWorldSpawnedEvent setPosition(double x, double y, double z); + } + + private interface IEntityWorldSpawnedEvent { + + Entity getEntity(); + + IEntityWorldSpawnedEvent setEntity(Entity entity); + } + + private interface IEntityPlayerWorldSpawnedEvent { + + EntityPlayer getEntityPlayer(); + + IEntityPlayerWorldSpawnedEvent setEntityPlayer(EntityPlayer entity); + } + + private interface IStringIdentifierWorldSpawnedEvent { + + String getIdentifier(); + + IStringIdentifierWorldSpawnedEvent setIdentifier(String identifier); + + IStringIdentifierWorldSpawnedEvent setIdentifier(Enum<?> identifier); + } + + private interface ISoundWorldSpawnedEvent { + + float getPitch(); + + float getVolume(); + + ISoundWorldSpawnedEvent setPitch(float pitch); + + ISoundWorldSpawnedEvent setVolume(float volume); + } + + /* Abstract Classes */ + + private abstract static class EntityWorldSpawnedEventBuilder extends WorldSpawnedEventBuilder + implements IEntityWorldSpawnedEvent { + + private Entity entity; + + @Override + public Entity getEntity() { + return entity; + } + + @Override + public EntityWorldSpawnedEventBuilder setEntity(Entity entity) { + this.entity = entity; + return this; + } + } + + private abstract static class PositionedEntityWorldSpawnedEventBuilder extends EntityWorldSpawnedEventBuilder + implements IPositionedWorldSpawnedEvent { + + private Vec3 position; + + @Override + public Vec3 getPosition() { + return position; + } + + @Override + public PositionedEntityWorldSpawnedEventBuilder setPosition(Vec3 position) { + this.position = position; + return this; + } + + @Override + public PositionedEntityWorldSpawnedEventBuilder setPosition(double x, double y, double z) { + this.position = Vec3.createVectorHelper(x, y, z); + return this; + } + } + + private abstract static class PositionedWorldSpawnedEventBuilder extends WorldSpawnedEventBuilder + implements IPositionedWorldSpawnedEvent { + + private Vec3 position; + + @Override + public Vec3 getPosition() { + return position; + } + + @Override + public PositionedWorldSpawnedEventBuilder setPosition(Vec3 position) { + this.position = position; + return this; + } + + @Override + public PositionedWorldSpawnedEventBuilder setPosition(double x, double y, double z) { + this.position = Vec3.createVectorHelper(x, y, z); + return this; + } + } + + private abstract static class StringIdentifierPositionedWorldSpawnedEventBuilder + extends PositionedWorldSpawnedEventBuilder implements IStringIdentifierWorldSpawnedEvent { + + private String identifier; + + @Override + public String getIdentifier() { + return identifier; + } + + @Override + public StringIdentifierPositionedWorldSpawnedEventBuilder setIdentifier(String identifier) { + this.identifier = identifier; + return this; + } + + @Override + public StringIdentifierPositionedWorldSpawnedEventBuilder setIdentifier(Enum<?> identifier) { + this.identifier = identifier.toString(); + return this; + } + } + + private abstract static class SoundStringIdentifierPositionedWorldSpawnedEventBuilder + extends StringIdentifierPositionedWorldSpawnedEventBuilder implements ISoundWorldSpawnedEvent { + + private float pitch; + private float volume; + + @Override + public float getPitch() { + return pitch; + } + + @Override + public float getVolume() { + return volume; + } + + @Override + public SoundStringIdentifierPositionedWorldSpawnedEventBuilder setPitch(float pitch) { + this.pitch = pitch; + return this; + } + + @Override + public SoundStringIdentifierPositionedWorldSpawnedEventBuilder setVolume(float volume) { + this.volume = volume; + return this; + } + } + + /* Implementations */ + + public static final class ParticleEventBuilder extends StringIdentifierPositionedWorldSpawnedEventBuilder { + + private Vec3 motion; + + public Vec3 getMotion() { + return motion; + } + + public ParticleEventBuilder setMotion(double x, double y, double z) { + this.motion = Vec3.createVectorHelper(x, y, z); + return this; + } + + public ParticleEventBuilder setMotion(Vec3 motion) { + this.motion = motion; + return this; + } + + @Override + public ParticleEventBuilder setWorld(World world) { + return (ParticleEventBuilder) super.setWorld(world); + } + + @Override + public ParticleEventBuilder setPosition(Vec3 position) { + return (ParticleEventBuilder) super.setPosition(position); + } + + @Override + public ParticleEventBuilder setPosition(double x, double y, double z) { + return (ParticleEventBuilder) super.setPosition(x, y, z); + } + + @Override + public ParticleEventBuilder setIdentifier(String identifier) { + return (ParticleEventBuilder) super.setIdentifier(identifier); + } + + @Override + public ParticleEventBuilder setIdentifier(Enum<?> identifier) { + return (ParticleEventBuilder) super.setIdentifier(identifier); + } + + @Override + public void run() { + if (getPosition() == null || getIdentifier() == null || getMotion() == null || getWorld() == null) + throw new IllegalStateException("Position, identifier, motion and world must be set"); + + getWorld().spawnParticle( + getIdentifier(), + getPosition().xCoord, + getPosition().yCoord, + getPosition().zCoord, + getMotion().xCoord, + getMotion().yCoord, + getMotion().zCoord); + } + } + + public static final class SoundEffectEventBuilder extends SoundStringIdentifierPositionedWorldSpawnedEventBuilder { + + @Override + public SoundEffectEventBuilder setWorld(World world) { + return (SoundEffectEventBuilder) super.setWorld(world); + } + + @Override + public SoundEffectEventBuilder setPosition(Vec3 position) { + return (SoundEffectEventBuilder) super.setPosition(position); + } + + @Override + public SoundEffectEventBuilder setPosition(double x, double y, double z) { + return (SoundEffectEventBuilder) super.setPosition(x, y, z); + } + + @Override + public SoundEffectEventBuilder setIdentifier(String identifier) { + return (SoundEffectEventBuilder) super.setIdentifier(identifier); + } + + @Override + public SoundEffectEventBuilder setIdentifier(Enum<?> identifier) { + return (SoundEffectEventBuilder) super.setIdentifier(identifier); + } + + @Override + public SoundEffectEventBuilder setPitch(float pitch) { + return (SoundEffectEventBuilder) super.setPitch(pitch); + } + + @Override + public SoundEffectEventBuilder setVolume(float volume) { + return (SoundEffectEventBuilder) super.setVolume(volume); + } + + @Override + public void run() { + if (getPosition() == null || getIdentifier() == null || getWorld() == null) + throw new IllegalStateException(ILLEGAL_STATE_STR1); + + getWorld().playSoundEffect( + getPosition().xCoord, + getPosition().yCoord, + getPosition().zCoord, + getIdentifier(), + getPitch(), + getVolume()); + } + } + + public static final class SoundEventBuilder extends SoundStringIdentifierPositionedWorldSpawnedEventBuilder { + + private boolean proximity; + + public boolean isProximity() { + return proximity; + } + + @Override + public SoundEventBuilder setWorld(World world) { + return (SoundEventBuilder) super.setWorld(world); + } + + @Override + public SoundEventBuilder setPosition(Vec3 position) { + return (SoundEventBuilder) super.setPosition(position); + } + + @Override + public SoundEventBuilder setPosition(double x, double y, double z) { + return (SoundEventBuilder) super.setPosition(x, y, z); + } + + @Override + public SoundEventBuilder setIdentifier(String identifier) { + return (SoundEventBuilder) super.setIdentifier(identifier); + } + + @Override + public SoundEventBuilder setPitch(float pitch) { + return (SoundEventBuilder) super.setPitch(pitch); + } + + @Override + public SoundEventBuilder setVolume(float volume) { + return (SoundEventBuilder) super.setVolume(volume); + } + + public SoundEventBuilder setProximity(boolean proximity) { + this.proximity = proximity; + return this; + } + + @Override + public void run() { + if (getPosition() == null || getIdentifier() == null || getWorld() == null) + throw new IllegalStateException(ILLEGAL_STATE_STR1); + + getWorld().playSound( + getPosition().xCoord, + getPosition().yCoord, + getPosition().zCoord, + getIdentifier(), + getPitch(), + getVolume(), + isProximity()); + } + } + + /** + * Positional Data is rounded down due to this targeting a block. + */ + public static final class RecordEffectEventBuilder extends StringIdentifierPositionedWorldSpawnedEventBuilder { + + @Override + public RecordEffectEventBuilder setWorld(World world) { + return (RecordEffectEventBuilder) super.setWorld(world); + } + + @Override + public RecordEffectEventBuilder setPosition(Vec3 position) { + return (RecordEffectEventBuilder) super.setPosition(position); + } + + @Override + public RecordEffectEventBuilder setPosition(double x, double y, double z) { + return (RecordEffectEventBuilder) super.setPosition(x, y, z); + } + + @Override + public RecordEffectEventBuilder setIdentifier(String identifier) { + return (RecordEffectEventBuilder) super.setIdentifier(identifier); + } + + @Override + public void run() { + if (getPosition() == null || getIdentifier() == null || getWorld() == null) + throw new IllegalStateException(ILLEGAL_STATE_STR1); + + getWorld().playRecord( + getIdentifier(), + (int) getPosition().xCoord, + (int) getPosition().yCoord, + (int) getPosition().zCoord); + } + } + + public static final class ExplosionEffectEventBuilder extends PositionedEntityWorldSpawnedEventBuilder { + + private boolean isFlaming, isSmoking; + private float strength; + + public float getStrength() { + return strength; + } + + public ExplosionEffectEventBuilder setStrength(float strength) { + this.strength = strength; + return this; + } + + public boolean isFlaming() { + return isFlaming; + } + + public ExplosionEffectEventBuilder setFlaming(boolean flaming) { + isFlaming = flaming; + return this; + } + + public boolean isSmoking() { + return isSmoking; + } + + public ExplosionEffectEventBuilder setSmoking(boolean smoking) { + isSmoking = smoking; + return this; + } + + @Override + public ExplosionEffectEventBuilder setWorld(World world) { + return (ExplosionEffectEventBuilder) super.setWorld(world); + } + + @Override + public ExplosionEffectEventBuilder setEntity(Entity entity) { + return (ExplosionEffectEventBuilder) super.setEntity(entity); + } + + @Override + public ExplosionEffectEventBuilder setPosition(double x, double y, double z) { + return (ExplosionEffectEventBuilder) super.setPosition(x, y, z); + } + + @Override + public void run() { + if (getPosition() == null || getWorld() == null) + throw new IllegalStateException("Position and world must be set"); + + getWorld().newExplosion( + getEntity(), + getPosition().xCoord, + getPosition().yCoord, + getPosition().zCoord, + strength, + isFlaming, + isSmoking); + } + } + + /** + * Positional Data is rounded down due to this targeting a block. + */ + public static final class ExtinguishFireEffectEventBuilder extends PositionedWorldSpawnedEventBuilder + implements IEntityPlayerWorldSpawnedEvent { + + private int side; + private EntityPlayer entityPlayer; + + public int getSide() { + return side; + } + + public ExtinguishFireEffectEventBuilder setSide(int side) { + this.side = side; + return this; + } + + @Override + public EntityPlayer getEntityPlayer() { + return entityPlayer; + } + + @Override + public ExtinguishFireEffectEventBuilder setEntityPlayer(EntityPlayer entity) { + this.entityPlayer = entity; + return this; + } + + @Override + public ExtinguishFireEffectEventBuilder setWorld(World world) { + return (ExtinguishFireEffectEventBuilder) super.setWorld(world); + } + + @Override + public ExtinguishFireEffectEventBuilder setPosition(Vec3 position) { + return (ExtinguishFireEffectEventBuilder) super.setPosition(position); + } + + @Override + public ExtinguishFireEffectEventBuilder setPosition(double x, double y, double z) { + return (ExtinguishFireEffectEventBuilder) super.setPosition(x, y, z); + } + + @Override + public void run() { + if (getEntityPlayer() == null || getPosition() == null || getWorld() == null) + throw new IllegalStateException("EntityPlayer, position and world must be set"); + + getWorld().extinguishFire( + getEntityPlayer(), + (int) getPosition().xCoord, + (int) getPosition().yCoord, + (int) getPosition().zCoord, + side); + } + } + + public static final class SoundAtEntityEventBuilder extends EntityWorldSpawnedEventBuilder + implements ISoundWorldSpawnedEvent, IStringIdentifierWorldSpawnedEvent { + + private float pitch; + private float volume; + private String identifier; + + @Override + public String getIdentifier() { + return identifier; + } + + @Override + public SoundAtEntityEventBuilder setIdentifier(String identifier) { + this.identifier = identifier; + return this; + } + + @Override + public SoundAtEntityEventBuilder setIdentifier(Enum<?> identifier) { + this.identifier = identifier.toString(); + return this; + } + + @Override + public float getPitch() { + return pitch; + } + + @Override + public float getVolume() { + return volume; + } + + @Override + public SoundAtEntityEventBuilder setPitch(float pitch) { + this.pitch = pitch; + return this; + } + + @Override + public SoundAtEntityEventBuilder setVolume(float volume) { + this.volume = volume; + return this; + } + + @Override + public SoundAtEntityEventBuilder setWorld(World world) { + return (SoundAtEntityEventBuilder) super.setWorld(world); + } + + @Override + public SoundAtEntityEventBuilder setEntity(Entity entity) { + return (SoundAtEntityEventBuilder) super.setEntity(entity); + } + + @Override + public void run() { + if (getWorld() == null || getIdentifier() == null || getEntity() == null) + throw new IllegalStateException("World, Identifier and entity must be set!"); + + getWorld().playSoundAtEntity(getEntity(), getIdentifier(), volume, pitch); + } + } + + public static final class SoundToNearExceptEventBuilder extends WorldSpawnedEventBuilder + implements ISoundWorldSpawnedEvent, IStringIdentifierWorldSpawnedEvent, IEntityPlayerWorldSpawnedEvent { + + private float pitch; + private float volume; + private String identifier; + private EntityPlayer entityPlayer; + + @Override + public String getIdentifier() { + return identifier; + } + + @Override + public SoundToNearExceptEventBuilder setIdentifier(String identifier) { + this.identifier = identifier; + return this; + } + + @Override + public SoundToNearExceptEventBuilder setIdentifier(Enum<?> identifier) { + this.identifier = identifier.toString(); + return this; + } + + @Override + public float getPitch() { + return pitch; + } + + @Override + public float getVolume() { + return volume; + } + + @Override + public SoundToNearExceptEventBuilder setPitch(float pitch) { + this.pitch = pitch; + return this; + } + + @Override + public SoundToNearExceptEventBuilder setVolume(float volume) { + this.volume = volume; + return this; + } + + @Override + public SoundToNearExceptEventBuilder setWorld(World world) { + return (SoundToNearExceptEventBuilder) super.setWorld(world); + } + + @Override + public void run() { + if (getWorld() == null || getIdentifier() == null || getEntityPlayer() == null) + throw new IllegalStateException("World, Identifier and EntityPlayer must be set!"); + + getWorld().playSoundAtEntity(getEntityPlayer(), getIdentifier(), volume, pitch); + } + + @Override + public EntityPlayer getEntityPlayer() { + return entityPlayer; + } + + @Override + public SoundToNearExceptEventBuilder setEntityPlayer(EntityPlayer entity) { + entityPlayer = entity; + return this; + } + } +} diff --git a/src/main/java/gregtech/api/util/extensions/ArrayExt.java b/src/main/java/gregtech/api/util/extensions/ArrayExt.java new file mode 100644 index 0000000000..b6ebb07d38 --- /dev/null +++ b/src/main/java/gregtech/api/util/extensions/ArrayExt.java @@ -0,0 +1,81 @@ +package gregtech.api.util.extensions; + +import java.util.function.IntFunction; + +public class ArrayExt { + + public static int[] of(int... objects) { + return objects; + } + + public static float[] of(float... objects) { + return objects; + } + + public static double[] of(double... objects) { + return objects; + } + + public static char[] of(char... objects) { + return objects; + } + + public static byte[] of(byte... objects) { + return objects; + } + + public static long[] of(long... objects) { + return objects; + } + + @SafeVarargs + public static <T> T[] of(T... objects) { + return objects; + } + + @SuppressWarnings("ForLoopReplaceableByForEach") + public static <T> T[] withoutNulls(T[] array, IntFunction<T[]> arrayFactory) { + int count = 0; + for (int i = 0; i < array.length; i++) { + if (array[i] != null) { + count++; + } + } + + T[] newArr = arrayFactory.apply(count); + if (count == 0) return newArr; + + int j = 0; + for (int i = 0; i < array.length; i++) { + if (array[i] != null) { + newArr[j] = array[i]; + j++; + } + } + + return newArr; + } + + public static <T> T[] withoutTrailingNulls(T[] array, IntFunction<T[]> arrayFactory) { + int firstNull = -1; + for (int i = array.length - 1; i >= 0; i--) { + if (array[i] == null) { + firstNull = i; + } else { + break; + } + } + + if (firstNull == -1) { + T[] newArray = arrayFactory.apply(array.length); + System.arraycopy(array, 0, newArray, 0, array.length); + return newArray; + } else if (firstNull == 0) { + return arrayFactory.apply(0); + } else { + T[] newArray = arrayFactory.apply(firstNull); + System.arraycopy(array, 0, newArray, 0, firstNull); + return newArray; + } + } +} diff --git a/src/main/java/gregtech/api/util/extensions/IteratorExt.java b/src/main/java/gregtech/api/util/extensions/IteratorExt.java new file mode 100644 index 0000000000..3cbae1c598 --- /dev/null +++ b/src/main/java/gregtech/api/util/extensions/IteratorExt.java @@ -0,0 +1,13 @@ +package gregtech.api.util.extensions; + +import java.util.Iterator; + +import gregtech.api.objects.iterators.MergedIterator; + +public class IteratorExt { + + @SafeVarargs + public static <T> Iterator<T> merge(Iterator<T>... iterators) { + return new MergedIterator<>(iterators); + } +} diff --git a/src/main/java/gregtech/api/util/item/ItemHolder.java b/src/main/java/gregtech/api/util/item/ItemHolder.java new file mode 100644 index 0000000000..4675d0ba0e --- /dev/null +++ b/src/main/java/gregtech/api/util/item/ItemHolder.java @@ -0,0 +1,79 @@ +package gregtech.api.util.item; + +import static net.minecraftforge.oredict.OreDictionary.getOreIDs; + +import java.util.Arrays; + +import javax.annotation.Nonnull; + +import net.minecraft.init.Items; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; + +import gregtech.api.util.GT_Utility; + +public class ItemHolder { + + private final Item item; + private final int meta; + private final NBTTagCompound tag; + private final int[] oreIDs; + + public ItemHolder(@Nonnull ItemStack item) { + this.item = item.getItem(); + this.meta = Items.feather.getDamage(item); + this.tag = item.getTagCompound(); + this.oreIDs = getOreIDs(item); + } + + public Item getItem() { + return item; + } + + public int getMeta() { + return meta; + } + + public NBTTagCompound getNBT() { + return tag; + } + + public int[] getOreDictTagIDs() { + return oreIDs; + } + + @Override + public boolean equals(Object other) { + if (other == this) return true; + if (!(other instanceof ItemHolder otherIH)) return false; + if (Arrays.stream(oreIDs) + .anyMatch(id -> { + for (int i = 0; i < otherIH.getOreDictTagIDs().length; i++) { + if (id == otherIH.getOreDictTagIDs()[i]) return true; + } + return false; + })) { + return true; + } + + if (item != otherIH.getItem() || meta != otherIH.getMeta()) { + return false; + } + if (this.tag == null && otherIH.getNBT() == null) return true; + if (this.tag == null || otherIH.getNBT() == null) return false; + return this.tag.equals(otherIH); + } + + @Override + public int hashCode() { + return GT_Utility.stackToInt(toStack()); + } + + @Nonnull + private ItemStack toStack() { + ItemStack item = new ItemStack(this.item, 1, meta); + item.stackTagCompound = tag; + return item; + } +} diff --git a/src/main/java/gregtech/api/util/recipe/RecipeInputRequirements.java b/src/main/java/gregtech/api/util/recipe/RecipeInputRequirements.java new file mode 100644 index 0000000000..590c104101 --- /dev/null +++ b/src/main/java/gregtech/api/util/recipe/RecipeInputRequirements.java @@ -0,0 +1,77 @@ +package gregtech.api.util.recipe; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import javax.annotation.Nonnull; + +import net.minecraft.item.ItemStack; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidStack; + +import gregtech.api.util.GT_Recipe; +import gregtech.api.util.item.ItemHolder; + +public class RecipeInputRequirements { + + protected Map<ItemHolder, Long> itemInputs = new HashMap<>(); + protected Set<ItemHolder> itemInputsMet = new HashSet<>(); + protected boolean metAllItem = false; + protected Map<Fluid, Long> fluidInputs = new HashMap<>(); + protected Set<Fluid> fluidInputsMet = new HashSet<>(); + protected boolean metAllFluid = false; + + public RecipeInputRequirements(@Nonnull GT_Recipe recipe) { + this(recipe.mInputs, recipe.mFluidInputs); + } + + public RecipeInputRequirements(@Nonnull ItemStack[] itemInputs, @Nonnull FluidStack[] fluidInputs) { + for (ItemStack item : itemInputs) { + if (item == null) continue; + ItemHolder itemIH = new ItemHolder(item); + this.itemInputs.put(itemIH, this.itemInputs.getOrDefault(itemIH, 0L) + item.stackSize); + } + + for (FluidStack fluid : fluidInputs) { + if (fluid == null) continue; + this.fluidInputs.put(fluid.getFluid(), this.fluidInputs.getOrDefault(fluid.getFluid(), 0L) + fluid.amount); + } + } + + /** + * + * @param itemInputs we have and want to fill this request + * @return {@code true} when all item inputs are met + */ + public boolean tryToFillItemRequirements(Map<ItemHolder, Long> itemInputs) { + if (metAllItem) return metAllItem; + for (Entry<ItemHolder, Long> entry : itemInputs.entrySet()) { + if (itemInputsMet.contains(entry.getKey())) continue; + if (!this.itemInputs.containsKey(entry.getKey())) continue; + if (this.itemInputs.get(entry.getKey()) > entry.getValue()) continue; + itemInputsMet.add(entry.getKey()); + } + metAllItem = itemInputsMet.containsAll(this.itemInputs.keySet()); + return metAllItem; + } + + /** + * + * @param fluidInputs we have and want to fill this request + * @return {@code true} when all fluid inputs are met + */ + public boolean tryToFillFluidRequirements(Map<Fluid, Long> fluidInputs) { + if (metAllFluid) return metAllFluid; + for (Entry<Fluid, Long> entry : fluidInputs.entrySet()) { + if (fluidInputsMet.contains(entry.getKey())) continue; + if (!this.fluidInputs.containsKey(entry.getKey())) continue; + if (this.fluidInputs.get(entry.getKey()) > entry.getValue()) continue; + fluidInputsMet.add(entry.getKey()); + } + metAllFluid = fluidInputsMet.containsAll(this.fluidInputs.keySet()); + return metAllFluid; + } +} diff --git a/src/main/java/gregtech/api/util/shutdown/ReasonOutOfFluid.java b/src/main/java/gregtech/api/util/shutdown/ReasonOutOfFluid.java new file mode 100644 index 0000000000..29b99a644a --- /dev/null +++ b/src/main/java/gregtech/api/util/shutdown/ReasonOutOfFluid.java @@ -0,0 +1,63 @@ +package gregtech.api.util.shutdown; + +import static gregtech.api.util.GT_ModHandler.getWater; +import static gregtech.api.util.GT_Utility.formatNumbers; + +import java.util.Objects; + +import net.minecraft.network.PacketBuffer; +import net.minecraft.util.StatCollector; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidRegistry; +import net.minecraftforge.fluids.FluidStack; + +import org.jetbrains.annotations.NotNull; + +public class ReasonOutOfFluid implements ShutDownReason { + + private FluidStack requiredFluid; + + ReasonOutOfFluid(@NotNull FluidStack requiredFluid) { + this.requiredFluid = requiredFluid; + } + + @NotNull + @Override + public String getID() { + return "out_of_fluid"; + } + + @NotNull + @Override + public String getDisplayString() { + return Objects.requireNonNull( + StatCollector.translateToLocalFormatted( + "GT5U.gui.text.out_of_fluid", + requiredFluid.getLocalizedName(), + formatNumbers(requiredFluid.amount))); + } + + @NotNull + @Override + public ShutDownReason newInstance() { + return new ReasonOutOfFluid(getWater(0)); + } + + @Override + public void encode(@NotNull PacketBuffer buffer) { + buffer.writeInt(requiredFluid.getFluidID()); + buffer.writeInt(requiredFluid.amount); + } + + @Override + public void decode(PacketBuffer buffer) { + int fluidID = buffer.readInt(); + Fluid fluid = FluidRegistry.getFluid(fluidID); + requiredFluid = new FluidStack(fluid, buffer.readInt()); + } + + @Override + public boolean wasCritical() { + return true; + } +} diff --git a/src/main/java/gregtech/api/util/shutdown/ReasonOutOfItem.java b/src/main/java/gregtech/api/util/shutdown/ReasonOutOfItem.java new file mode 100644 index 0000000000..f4a46f2d30 --- /dev/null +++ b/src/main/java/gregtech/api/util/shutdown/ReasonOutOfItem.java @@ -0,0 +1,62 @@ +package gregtech.api.util.shutdown; + +import static gregtech.api.util.GT_Utility.formatNumbers; + +import java.util.Objects; + +import net.minecraft.init.Items; +import net.minecraft.item.ItemStack; +import net.minecraft.network.PacketBuffer; +import net.minecraft.util.StatCollector; + +import org.jetbrains.annotations.NotNull; + +public class ReasonOutOfItem implements ShutDownReason { + + private ItemStack requiredItem; + + ReasonOutOfItem(@NotNull ItemStack requiredItem) { + this.requiredItem = requiredItem; + } + + @NotNull + @Override + public String getID() { + return "out_of_item"; + } + + @NotNull + @Override + public String getDisplayString() { + return Objects.requireNonNull( + StatCollector.translateToLocalFormatted( + "GT5U.gui.text.out_of_item", + requiredItem.getDisplayName(), + formatNumbers(requiredItem.stackSize))); + } + + @NotNull + @Override + public ShutDownReason newInstance() { + return new ReasonOutOfItem(new ItemStack(Items.feather, 0)); + } + + @Override + public void encode(@NotNull PacketBuffer buffer) { + try { + buffer.writeItemStackToBuffer(requiredItem); + } catch (Exception ignored) {} + } + + @Override + public void decode(PacketBuffer buffer) { + try { + requiredItem = buffer.readItemStackFromBuffer(); + } catch (Exception ignored) {} + } + + @Override + public boolean wasCritical() { + return true; + } +} diff --git a/src/main/java/gregtech/api/util/shutdown/ReasonOutOfStuff.java b/src/main/java/gregtech/api/util/shutdown/ReasonOutOfStuff.java new file mode 100644 index 0000000000..0c3f7e0b64 --- /dev/null +++ b/src/main/java/gregtech/api/util/shutdown/ReasonOutOfStuff.java @@ -0,0 +1,61 @@ +package gregtech.api.util.shutdown; + +import static gregtech.api.util.GT_Utility.formatNumbers; + +import java.util.Objects; + +import net.minecraft.network.PacketBuffer; +import net.minecraft.util.StatCollector; + +import org.jetbrains.annotations.NotNull; + +public class ReasonOutOfStuff implements ShutDownReason { + + private String required; + private int amount; + + ReasonOutOfStuff(@NotNull String required, int amount) { + this.required = required; + this.amount = amount; + } + + @NotNull + @Override + public String getID() { + return "out_of_stuff"; + } + + @NotNull + @Override + public String getDisplayString() { + return Objects.requireNonNull( + StatCollector.translateToLocalFormatted("GT5U.gui.text.out_of_stuff", required, formatNumbers(amount))); + } + + @NotNull + @Override + public ShutDownReason newInstance() { + return new ReasonOutOfStuff("stuff", 1); + } + + @Override + public void encode(@NotNull PacketBuffer buffer) { + buffer.writeInt(amount); + try { + buffer.writeStringToBuffer(required); + } catch (Exception ignored) {} + } + + @Override + public void decode(PacketBuffer buffer) { + amount = buffer.readInt(); + try { + required = buffer.readStringFromBuffer(32768); + } catch (Exception ignored) {} + } + + @Override + public boolean wasCritical() { + return true; + } +} diff --git a/src/main/java/gregtech/api/util/shutdown/ShutDownReason.java b/src/main/java/gregtech/api/util/shutdown/ShutDownReason.java new file mode 100644 index 0000000000..0815936a55 --- /dev/null +++ b/src/main/java/gregtech/api/util/shutdown/ShutDownReason.java @@ -0,0 +1,41 @@ +package gregtech.api.util.shutdown; + +import javax.annotation.Nonnull; + +import net.minecraft.network.PacketBuffer; + +public interface ShutDownReason { + + /** + * @return Unique registry ID + */ + @Nonnull + String getID(); + + /** + * @return Actual text to show on client GUI + */ + @Nonnull + String getDisplayString(); + + /** + * Create new instance to receive packet. + */ + @Nonnull + ShutDownReason newInstance(); + + /** + * Encode value to sync. + */ + void encode(@Nonnull PacketBuffer buffer); + + /** + * Decode synced value. + */ + void decode(PacketBuffer buffer); + + /** + * @return Whether the reason is critical. + */ + boolean wasCritical(); +} diff --git a/src/main/java/gregtech/api/util/shutdown/ShutDownReasonRegistry.java b/src/main/java/gregtech/api/util/shutdown/ShutDownReasonRegistry.java new file mode 100644 index 0000000000..298c5db237 --- /dev/null +++ b/src/main/java/gregtech/api/util/shutdown/ShutDownReasonRegistry.java @@ -0,0 +1,118 @@ +package gregtech.api.util.shutdown; + +import static gregtech.api.util.GT_ModHandler.getWater; + +import java.util.HashMap; +import java.util.Map; + +import javax.annotation.Nonnull; + +import net.minecraft.init.Items; +import net.minecraft.item.ItemStack; +import net.minecraftforge.fluids.FluidStack; + +public class ShutDownReasonRegistry { + + private static final Map<String, ShutDownReason> registry = new HashMap<>(); + + /** + * Registers ShutDownReason. No duplicated IDs are allowed. + * + * @param sample Sample object to register + */ + public static void register(ShutDownReason sample) { + if (isRegistered(sample.getID())) { + throw new IllegalStateException( + String.format( + "ID %s is already registered for %s", + sample.getID(), + registry.get(sample.getID()) + .getClass() + .getCanonicalName())); + } + registry.put(sample.getID(), sample); + } + + public static ShutDownReason getSampleFromRegistry(String id) { + if (!isRegistered(id)) { + throw new RuntimeException("Unknown id: " + id); + } + return registry.get(id); + } + + public static boolean isRegistered(String id) { + return registry.containsKey(id); + } + + /** + * Shut down due to power loss. + */ + @Nonnull + public static final ShutDownReason POWER_LOSS = SimpleShutDownReason.ofCritical("power_loss"); + /** + * Failed to output the pollution. + */ + @Nonnull + public static final ShutDownReason POLLUTION_FAIL = SimpleShutDownReason.ofCritical("pollution_fail"); + /** + * Shut down due to incomplete structure. + */ + @Nonnull + public static final ShutDownReason STRUCTURE_INCOMPLETE = SimpleShutDownReason.ofNormal("structure_incomplete"); + /** + * Shut down due to machine damage. + */ + @Nonnull + public static final ShutDownReason NO_REPAIR = SimpleShutDownReason.ofNormal("no_repair"); + /** + * No valid turbine found. + */ + @Nonnull + public static final ShutDownReason NO_TURBINE = SimpleShutDownReason.ofNormal("no_turbine"); + /** + * No correct machine part in controller slot. + */ + @Nonnull + public static final ShutDownReason NO_MACHINE_PART = SimpleShutDownReason.ofNormal("no_machine_part"); + /** + * Default unknown state. + */ + @Nonnull + public static final ShutDownReason NONE = SimpleShutDownReason.ofNormal("none"); + /** + * Critical unknown state. + */ + @Nonnull + public static final ShutDownReason CRITICAL_NONE = SimpleShutDownReason.ofCritical("none"); + + /** + * Fluid that needs to be constantly supplied are out. E.g. PCB coolant with cooling upgrades enabled. + */ + @Nonnull + public static ShutDownReason outOfFluid(@Nonnull FluidStack required) { + return new ReasonOutOfFluid(required); + } + + /** + * Item that needs to be constantly supplied are out. + */ + @Nonnull + public static ShutDownReason outOfItem(@Nonnull ItemStack required) { + return new ReasonOutOfItem(required); + } + + /** + * Stuff that needs to be constantly supplied are out. + */ + @Nonnull + public static ShutDownReason outOfStuff(@Nonnull String required, int amount) { + return new ReasonOutOfStuff(required, amount); + } + + static { + register(new SimpleShutDownReason("", false)); + register(new ReasonOutOfFluid(getWater(0))); + register(new ReasonOutOfItem(new ItemStack(Items.feather, 1))); + register(new ReasonOutOfStuff("stuff", 1)); + } +} diff --git a/src/main/java/gregtech/api/util/shutdown/SimpleShutDownReason.java b/src/main/java/gregtech/api/util/shutdown/SimpleShutDownReason.java new file mode 100644 index 0000000000..92763fa431 --- /dev/null +++ b/src/main/java/gregtech/api/util/shutdown/SimpleShutDownReason.java @@ -0,0 +1,79 @@ +package gregtech.api.util.shutdown; + +import java.util.Objects; + +import javax.annotation.Nonnull; + +import net.minecraft.network.PacketBuffer; +import net.minecraft.util.StatCollector; + +import org.jetbrains.annotations.NotNull; + +import com.gtnewhorizons.modularui.common.internal.network.NetworkUtils; + +/** + * Simple implementation of {@link ShutDownReason}. You can create new object without registering it. + */ +public class SimpleShutDownReason implements ShutDownReason { + + private String key; + private boolean wasCritical; + + public SimpleShutDownReason(String key, boolean isCritical) { + this.key = key; + this.wasCritical = isCritical; + } + + @NotNull + @Override + public String getID() { + return "simple_result"; + } + + @NotNull + @Override + public String getDisplayString() { + return Objects.requireNonNull(StatCollector.translateToLocal("GT5U.gui.text." + key)); + } + + @NotNull + @Override + public ShutDownReason newInstance() { + return new SimpleShutDownReason("", false); + } + + @Override + public void encode(@NotNull PacketBuffer buffer) { + buffer.writeBoolean(wasCritical); + NetworkUtils.writeStringSafe(buffer, key); + } + + @Override + public void decode(PacketBuffer buffer) { + wasCritical = buffer.readBoolean(); + key = NetworkUtils.readStringSafe(buffer); + } + + @Override + public boolean wasCritical() { + return wasCritical; + } + + /** + * Creates new reason with critical state. Add your localized description with `GT5U.gui.text.{key}`. + * This is already registered to registry. + */ + @Nonnull + public static ShutDownReason ofCritical(String key) { + return new SimpleShutDownReason(key, true); + } + + /** + * Creates new reason with normal state. Add your localized description with `GT5U.gui.text.{key}`. + * This is already registered to registry. + */ + @Nonnull + public static ShutDownReason ofNormal(String key) { + return new SimpleShutDownReason(key, false); + } +} diff --git a/src/main/java/gregtech/api/world/GT_Worldgen.java b/src/main/java/gregtech/api/world/GT_Worldgen.java new file mode 100644 index 0000000000..4e9ed6229b --- /dev/null +++ b/src/main/java/gregtech/api/world/GT_Worldgen.java @@ -0,0 +1,94 @@ +package gregtech.api.world; + +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.concurrent.ConcurrentHashMap; + +import net.minecraft.world.World; +import net.minecraft.world.chunk.IChunkProvider; + +import gregtech.api.GregTech_API; + +public abstract class GT_Worldgen { + + public final String mWorldGenName; + public final boolean mEnabled; + private final Map<String, Boolean> mDimensionMap = new ConcurrentHashMap<>(); + + @SuppressWarnings({ "unchecked", "rawtypes" }) // The adding of "this" needs a List<this> which does not exist + public GT_Worldgen(String aName, List aList, boolean aDefault) { + mWorldGenName = aName; + mEnabled = GregTech_API.sWorldgenFile.get("worldgen", mWorldGenName, aDefault); + if (mEnabled) aList.add(this); + } + + /** + * @param aWorld The World Object + * @param aRandom The Random Generator to use + * @param aBiome The Name of the Biome (always != null) + * @param aDimensionType The Type of Worldgeneration to add. -1 = Nether, 0 = Overworld, +1 = End + * @param aChunkX xCoord of the Chunk + * @param aChunkZ zCoord of the Chunk + * @return if the Worldgeneration has been successfully completed + */ + public boolean executeWorldgen(World aWorld, Random aRandom, String aBiome, int aDimensionType, int aChunkX, + int aChunkZ, IChunkProvider aChunkGenerator, IChunkProvider aChunkProvider) { + return false; + } + + public int executeWorldgenChunkified(World aWorld, Random aRandom, String aBiome, int aDimensionType, int aChunkX, + int aChunkZ, int seedX, int seedZ, IChunkProvider aChunkGenerator, IChunkProvider aChunkProvider) { + return 4; // This is for the empty Orevein + } + + /** + * @param aWorld The World Object + * @param aRandom The Random Generator to use + * @param aBiome The Name of the Biome (always != null) + * @param aDimensionType The Type of Worldgeneration to add. -1 = Nether, 0 = Overworld, +1 = End + * @param aChunkX xCoord of the Chunk + * @param aChunkZ zCoord of the Chunk + * @return if the Worldgeneration has been successfully completed + */ + public boolean executeCavegen(World aWorld, Random aRandom, String aBiome, int aDimensionType, int aChunkX, + int aChunkZ, IChunkProvider aChunkGenerator, IChunkProvider aChunkProvider) { + return false; + } + + /** + * + * @param aWorld The World Object + * @param aDimensionType The Type of Worldgeneration to add. -1 = Nether, 0 = Overworld, +1 = End + * @param aAllowedDimensionType The Type of allowed Worldgeneration + * @return if generation for this world is allowed for MoronTech (tm) OreGen (ATM (2.0.3.1Dev) only End, Nether, + * Overworld, Twilight Forest and Deep Dark) + */ + public boolean isGenerationAllowed(World aWorld, int aDimensionType, int aAllowedDimensionType) { + return isGenerationAllowed(aWorld.provider.getDimensionName(), aDimensionType, aAllowedDimensionType); + } + + /** + * + * @param aDimName The Dimension Name + * @param aDimensionType The Type of Worldgeneration to add. -1 = Nether, 0 = Overworld, +1 = End + * @param aAllowedDimensionType The Type of allowed Worldgeneration + * @return if generation for this world is allowed for MoronTech (tm) OreGen (ATM (2.0.3.1Dev) only End, Nether, + * Overworld, Twilight Forest and Deep Dark) + */ + public boolean isGenerationAllowed(String aDimName, int aDimensionType, int aAllowedDimensionType) { + if (!(aDimName.equalsIgnoreCase("Overworld") || aDimName.equalsIgnoreCase("Nether") + || aDimName.equalsIgnoreCase("The End") + || aDimName.equalsIgnoreCase("Twilight Forest") + || aDimName.equalsIgnoreCase("Underdark"))) return false; + + Boolean tAllowed = mDimensionMap.get(aDimName); + if (tAllowed == null) { + boolean tValue = GregTech_API.sWorldgenFile + .get("worldgen." + mWorldGenName, aDimName, aDimensionType == aAllowedDimensionType); + mDimensionMap.put(aDimName, tValue); + return tValue; + } + return tAllowed; + } +} diff --git a/src/main/java/gregtech/api/world/GT_Worldgen_Ore.java b/src/main/java/gregtech/api/world/GT_Worldgen_Ore.java new file mode 100644 index 0000000000..958adfad54 --- /dev/null +++ b/src/main/java/gregtech/api/world/GT_Worldgen_Ore.java @@ -0,0 +1,34 @@ +package gregtech.api.world; + +import java.util.ArrayList; +import java.util.Collection; + +import net.minecraft.block.Block; + +import gregtech.api.GregTech_API; + +public abstract class GT_Worldgen_Ore extends GT_Worldgen { + + public final int mBlockMeta, mAmount, mSize, mMinY, mMaxY, mProbability, mDimensionType; + public final Block mBlock; + public final Collection<String> mBiomeList; + public final boolean mAllowToGenerateinVoid; + private final String aTextWorldgen = "worldgen."; + + public GT_Worldgen_Ore(String aName, boolean aDefault, Block aBlock, int aBlockMeta, int aDimensionType, + int aAmount, int aSize, int aProbability, int aMinY, int aMaxY, Collection<String> aBiomeList, + boolean aAllowToGenerateinVoid) { + super(aName, GregTech_API.sWorldgenList, aDefault); + mDimensionType = aDimensionType; + mBlock = aBlock; + mBlockMeta = Math.min(Math.max(aBlockMeta, 0), 15); + mProbability = GregTech_API.sWorldgenFile.get(aTextWorldgen + mWorldGenName, "Probability", aProbability); + mAmount = GregTech_API.sWorldgenFile.get(aTextWorldgen + mWorldGenName, "Amount", aAmount); + mSize = GregTech_API.sWorldgenFile.get(aTextWorldgen + mWorldGenName, "Size", aSize); + mMinY = GregTech_API.sWorldgenFile.get(aTextWorldgen + mWorldGenName, "MinHeight", aMinY); + mMaxY = GregTech_API.sWorldgenFile.get(aTextWorldgen + mWorldGenName, "MaxHeight", aMaxY); + if (aBiomeList == null) mBiomeList = new ArrayList<>(); + else mBiomeList = aBiomeList; + mAllowToGenerateinVoid = aAllowToGenerateinVoid; + } +} diff --git a/src/main/java/gregtech/api/world/GT_Worldgen_Ore_SingleBlock.java b/src/main/java/gregtech/api/world/GT_Worldgen_Ore_SingleBlock.java new file mode 100644 index 0000000000..900f7808b1 --- /dev/null +++ b/src/main/java/gregtech/api/world/GT_Worldgen_Ore_SingleBlock.java @@ -0,0 +1,53 @@ +package gregtech.api.world; + +import java.util.Collection; +import java.util.Random; + +import net.minecraft.block.Block; +import net.minecraft.init.Blocks; +import net.minecraft.world.World; +import net.minecraft.world.chunk.IChunkProvider; + +public class GT_Worldgen_Ore_SingleBlock extends GT_Worldgen_Ore { + + public GT_Worldgen_Ore_SingleBlock(String aName, boolean aDefault, Block aBlock, int aBlockMeta, int aDimensionType, + int aAmount, int aSize, int aProbability, int aMinY, int aMaxY, Collection<String> aBiomeList, + boolean aAllowToGenerateinVoid) { + super( + aName, + aDefault, + aBlock, + aBlockMeta, + aDimensionType, + aAmount, + aSize, + aProbability, + aMinY, + aMaxY, + aBiomeList, + aAllowToGenerateinVoid); + } + + @Override + public boolean executeWorldgen(World aWorld, Random aRandom, String aBiome, int aDimensionType, int aChunkX, + int aChunkZ, IChunkProvider aChunkGenerator, IChunkProvider aChunkProvider) { + if (isGenerationAllowed(aWorld, aDimensionType, mDimensionType) + && (mBiomeList.isEmpty() || mBiomeList.contains(aBiome)) + && (mProbability <= 1 || aRandom.nextInt(mProbability) == 0)) { + for (int i = 0; i < mAmount; i++) { + int tX = aChunkX + aRandom.nextInt(16), tY = mMinY + aRandom.nextInt(mMaxY - mMinY), + tZ = aChunkZ + aRandom.nextInt(16); + Block tBlock = aWorld.getBlock(tX, tY, tZ); + if (((mAllowToGenerateinVoid && aWorld.getBlock(tX, tY, tZ) + .isAir(aWorld, tX, tY, tZ)) + || (tBlock != null && (tBlock.isReplaceableOreGen(aWorld, tX, tY, tZ, Blocks.stone) + || tBlock.isReplaceableOreGen(aWorld, tX, tY, tZ, Blocks.end_stone) + || tBlock.isReplaceableOreGen(aWorld, tX, tY, tZ, Blocks.netherrack))))) { + aWorld.setBlock(tX, tY, tZ, mBlock, mBlockMeta, 0); + } + } + return true; + } + return false; + } +} diff --git a/src/main/java/gregtech/api/world/GT_Worldgen_Ore_SingleBlock_UnderLava.java b/src/main/java/gregtech/api/world/GT_Worldgen_Ore_SingleBlock_UnderLava.java new file mode 100644 index 0000000000..956cd0eb4c --- /dev/null +++ b/src/main/java/gregtech/api/world/GT_Worldgen_Ore_SingleBlock_UnderLava.java @@ -0,0 +1,55 @@ +package gregtech.api.world; + +import java.util.Collection; +import java.util.Random; + +import net.minecraft.block.Block; +import net.minecraft.init.Blocks; +import net.minecraft.world.World; +import net.minecraft.world.chunk.IChunkProvider; + +public class GT_Worldgen_Ore_SingleBlock_UnderLava extends GT_Worldgen_Ore { + + public GT_Worldgen_Ore_SingleBlock_UnderLava(String aName, boolean aDefault, Block aBlock, int aBlockMeta, + int aDimensionType, int aAmount, int aSize, int aProbability, int aMinY, int aMaxY, + Collection<String> aBiomeList, boolean aAllowToGenerateinVoid) { + super( + aName, + aDefault, + aBlock, + aBlockMeta, + aDimensionType, + aAmount, + aSize, + aProbability, + aMinY, + aMaxY, + aBiomeList, + aAllowToGenerateinVoid); + } + + @Override + public boolean executeCavegen(World aWorld, Random aRandom, String aBiome, int aDimensionType, int aChunkX, + int aChunkZ, IChunkProvider aChunkGenerator, IChunkProvider aChunkProvider) { + if (isGenerationAllowed(aWorld, aDimensionType, mDimensionType) + && (mBiomeList.isEmpty() || mBiomeList.contains(aBiome)) + && (mProbability <= 1 || aRandom.nextInt(mProbability) == 0)) { + for (int i = 0; i < mAmount; i++) { + int tX = aChunkX + aRandom.nextInt(16), tY = mMinY + aRandom.nextInt(mMaxY - mMinY), + tZ = aChunkZ + aRandom.nextInt(16); + Block tBlock = aWorld.getBlock(tX, tY, tZ); + if (((mAllowToGenerateinVoid && aWorld.getBlock(tX, tY, tZ) + .isAir(aWorld, tX, tY, tZ)) + || (tBlock != null && (tBlock.isReplaceableOreGen(aWorld, tX, tY, tZ, Blocks.stone) + || tBlock.isReplaceableOreGen(aWorld, tX, tY, tZ, Blocks.end_stone) + || tBlock.isReplaceableOreGen(aWorld, tX, tY, tZ, Blocks.netherrack))))) { + if (aWorld.getBlock(tX, tY + 1, tZ) == Blocks.lava + || aWorld.getBlock(tX, tY, tZ) == Blocks.flowing_lava) + aWorld.setBlock(tX, tY, tZ, mBlock, mBlockMeta, 0); + } + } + return true; + } + return false; + } +} |