aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/tectech/recipe
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/tectech/recipe')
-rw-r--r--src/main/java/tectech/recipe/EyeOfHarmonyFrontend.java206
-rw-r--r--src/main/java/tectech/recipe/EyeOfHarmonyRecipe.java465
-rw-r--r--src/main/java/tectech/recipe/EyeOfHarmonyRecipeStorage.java186
-rw-r--r--src/main/java/tectech/recipe/GodforgeExoticFrontend.java82
-rw-r--r--src/main/java/tectech/recipe/GodforgePlasmaFrontend.java79
-rw-r--r--src/main/java/tectech/recipe/ResearchStationFrontend.java101
-rw-r--r--src/main/java/tectech/recipe/TTRecipeAdder.java251
-rw-r--r--src/main/java/tectech/recipe/TecTechRecipeMaps.java78
8 files changed, 1448 insertions, 0 deletions
diff --git a/src/main/java/tectech/recipe/EyeOfHarmonyFrontend.java b/src/main/java/tectech/recipe/EyeOfHarmonyFrontend.java
new file mode 100644
index 0000000000..4e1c40eb41
--- /dev/null
+++ b/src/main/java/tectech/recipe/EyeOfHarmonyFrontend.java
@@ -0,0 +1,206 @@
+package tectech.recipe;
+
+import static com.google.common.math.LongMath.pow;
+import static gregtech.api.util.GTUtility.formatNumbers;
+import static java.lang.Math.min;
+import static net.minecraft.util.EnumChatFormatting.BOLD;
+import static net.minecraft.util.EnumChatFormatting.DARK_RED;
+import static net.minecraft.util.EnumChatFormatting.RESET;
+import static net.minecraft.util.StatCollector.translateToLocal;
+import static net.minecraft.util.StatCollector.translateToLocalFormatted;
+import static tectech.util.CommonValues.EOH_TIER_FANCY_NAMES;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.EnumChatFormatting;
+
+import com.gtnewhorizons.modularui.api.math.Alignment;
+import com.gtnewhorizons.modularui.api.math.Pos2d;
+import com.gtnewhorizons.modularui.api.math.Size;
+
+import appeng.util.ReadableNumberConverter;
+import codechicken.nei.PositionedStack;
+import gregtech.api.recipe.BasicUIPropertiesBuilder;
+import gregtech.api.recipe.NEIRecipePropertiesBuilder;
+import gregtech.api.recipe.RecipeMapFrontend;
+import gregtech.api.util.GTLanguageManager;
+import gregtech.api.util.MethodsReturnNonnullByDefault;
+import gregtech.common.gui.modularui.UIHelper;
+import gregtech.nei.GTNEIDefaultHandler;
+import gregtech.nei.RecipeDisplayInfo;
+import gregtech.nei.formatter.INEISpecialInfoFormatter;
+
+@ParametersAreNonnullByDefault
+@MethodsReturnNonnullByDefault
+public class EyeOfHarmonyFrontend extends RecipeMapFrontend {
+
+ private static final int xDirMaxCount = 9;
+ private static final int itemRows = 9, fluidRows = 2;
+ public static final int maxItemInputs = 1, maxItemOutputs = xDirMaxCount * itemRows, maxFluidInputs = 0,
+ maxFluidOutputs = xDirMaxCount * fluidRows;
+ private static final int yOrigin = 8;
+ private static final long TRILLION = pow(10, 12);
+
+ public EyeOfHarmonyFrontend(BasicUIPropertiesBuilder uiPropertiesBuilder,
+ NEIRecipePropertiesBuilder neiPropertiesBuilder) {
+ super(
+ uiPropertiesBuilder.logoPos(new Pos2d(8, yOrigin)),
+ neiPropertiesBuilder.recipeBackgroundSize(new Size(170, 117 + (itemRows + fluidRows - 4) * 18))
+ .neiSpecialInfoFormatter(new EyeOfHarmonySpecialValueFormatter()));
+ }
+
+ @Override
+ public List<Pos2d> getItemInputPositions(int itemInputCount) {
+ return UIHelper.getGridPositions(itemInputCount, 79, yOrigin, 1, 1);
+ }
+
+ public static final int maxItemsToRender = 80;
+
+ @Override
+ public List<Pos2d> getItemOutputPositions(int itemOutputCount) {
+ return UIHelper.getGridPositions(min(itemOutputCount, maxItemsToRender + 1), 7, yOrigin + 36, xDirMaxCount, 12);
+ }
+
+ @Override
+ public List<Pos2d> getFluidInputPositions(int fluidInputCount) {
+ return UIHelper.getGridPositions(fluidInputCount, 0, 0, 0, 0);
+ }
+
+ @Override
+ public List<Pos2d> getFluidOutputPositions(int fluidOutputCount) {
+ return UIHelper.getGridPositions(fluidOutputCount, 7, yOrigin + 13 * 17 - 7 - 16, xDirMaxCount, 3);
+ }
+
+ @Override
+ public List<String> handleNEIItemTooltip(ItemStack stack, List<String> currentTip,
+ GTNEIDefaultHandler.CachedDefaultRecipe neiCachedRecipe) {
+ super.handleNEIItemTooltip(stack, currentTip, neiCachedRecipe);
+ EyeOfHarmonyRecipe currentRecipe = (EyeOfHarmonyRecipe) neiCachedRecipe.mRecipe.mSpecialItems;
+
+ // Draw tooltip on planet item.
+ if (stack.isItemEqual(currentRecipe.getRecipeTriggerItem())) {
+ currentTip.add(
+ EnumChatFormatting.GRAY + translateToLocalFormatted(
+ "tt.nei.eoh.total_items",
+ formatNumbers(currentRecipe.getSumOfItems())));
+ return currentTip;
+ }
+
+ // Draw tooltip on other items.
+ double percentage = currentRecipe.getItemStackToProbabilityMap()
+ .getOrDefault(stack, -1.0);
+
+ if (percentage != -1.0) {
+ currentTip.add(EnumChatFormatting.GRAY + translateToLocalFormatted("tt.nei.eoh.solid_mass", percentage));
+ currentTip.add(
+ EnumChatFormatting.GRAY + translateToLocalFormatted(
+ "tt.nei.eoh.item_count",
+ formatNumbers(
+ currentRecipe.getItemStackToTrueStackSizeMap()
+ .get(stack))));
+ }
+
+ return currentTip;
+ }
+
+ @Override
+ public void drawNEIOverlays(GTNEIDefaultHandler.CachedDefaultRecipe neiCachedRecipe) {
+ EyeOfHarmonyRecipe EOHRecipe = (EyeOfHarmonyRecipe) neiCachedRecipe.mRecipe.mSpecialItems;
+ for (PositionedStack stack : neiCachedRecipe.mInputs) {
+ if (stack instanceof GTNEIDefaultHandler.FixedPositionedStack) {
+ if (stack.item.isItemEqual(EOHRecipe.getRecipeTriggerItem())) {
+ drawNEIOverlayText(translateToLocal("NC"), stack);
+ }
+ }
+ }
+ for (PositionedStack stack : neiCachedRecipe.mOutputs) {
+ if (stack instanceof GTNEIDefaultHandler.FixedPositionedStack) {
+ if (EOHRecipe.getItemStackToTrueStackSizeMap()
+ .containsKey(stack.item)) {
+ long stackSize = EOHRecipe.getItemStackToTrueStackSizeMap()
+ .get(stack.item);
+ String displayString;
+ if (stackSize > 9999) {
+ displayString = ReadableNumberConverter.INSTANCE.toWideReadableForm(stackSize);
+ } else {
+ displayString = String.valueOf(stackSize);
+ }
+
+ drawNEIOverlayText(displayString, stack, 0xffffff, 0.5f, true, Alignment.BottomRight);
+ }
+ }
+ }
+ }
+
+ private static class EyeOfHarmonySpecialValueFormatter implements INEISpecialInfoFormatter {
+
+ @Override
+ public List<String> format(RecipeDisplayInfo recipeInfo) {
+ EyeOfHarmonyRecipe recipe = (EyeOfHarmonyRecipe) recipeInfo.recipe.mSpecialItems;
+ List<String> result = new ArrayList<>();
+
+ result.add(
+ GTLanguageManager.addStringLocalization("EOH.Recipe.Hydrogen.In", "Hydrogen") + ": "
+ + formatNumbers(recipe.getHydrogenRequirement())
+ + " L");
+ result.add(
+ GTLanguageManager.addStringLocalization("EOH.Recipe.Helium.In", "Helium") + ": "
+ + formatNumbers(recipe.getHydrogenRequirement())
+ + " L");
+ result.add(
+ GTLanguageManager.addStringLocalization("EOH.Recipe.SpacetimeTier", "Spacetime Tier") + ": "
+ + EOH_TIER_FANCY_NAMES[(int) recipe.getSpacetimeCasingTierRequired()]);
+
+ if (recipe.getEUOutput() < TRILLION) {
+ result.add(
+ GTLanguageManager.addStringLocalization("EOH.Recipe.EU.Out", "EU Output") + ": "
+ + formatNumbers(recipe.getEUOutput())
+ + " EU");
+ } else {
+ result.add(
+ GTLanguageManager.addStringLocalization("EOH.Recipe.EU.Out", "EU Output") + ": "
+ + ReadableNumberConverter.INSTANCE.toWideReadableForm(recipe.getEUOutput())
+ + " EU");
+ }
+
+ if (recipe.getEUOutput() < TRILLION) {
+ result.add(
+ GTLanguageManager.addStringLocalization("EOH.Recipe.EU.In", "EU Input") + ": "
+ + formatNumbers(recipe.getEUStartCost())
+ + " EU");
+ } else {
+ result.add(
+ GTLanguageManager.addStringLocalization("EOH.Recipe.EU.In", "EU Input") + ": "
+ + ReadableNumberConverter.INSTANCE.toWideReadableForm(recipe.getEUStartCost())
+ + " EU");
+ }
+
+ result.add(
+ GTLanguageManager.addStringLocalization("EOH.Recipe.BaseRecipeChance", "Base Recipe Chance") + ": "
+ + formatNumbers(100 * recipe.getBaseRecipeSuccessChance())
+ + "%");
+ result.add(
+ GTLanguageManager.addStringLocalization("EOH.Recipe.RecipeEnergyEfficiency", "Recipe Energy Efficiency")
+ + ": "
+ + formatNumbers(100 * recipe.getRecipeEnergyEfficiency())
+ + "%");
+
+ if (recipe.getOutputItems()
+ .size() > maxItemsToRender) {
+ result.add(
+ "" + DARK_RED
+ + BOLD
+ + GTLanguageManager.addStringLocalization("EOH.Recipe.Warning.0", "Warning")
+ + RESET
+ + ": "
+ + GTLanguageManager.addStringLocalization("EOH.Recipe.Warning.1", "Not all items displayed."));
+ }
+
+ return result;
+ }
+ }
+}
diff --git a/src/main/java/tectech/recipe/EyeOfHarmonyRecipe.java b/src/main/java/tectech/recipe/EyeOfHarmonyRecipe.java
new file mode 100644
index 0000000000..90acd63048
--- /dev/null
+++ b/src/main/java/tectech/recipe/EyeOfHarmonyRecipe.java
@@ -0,0 +1,465 @@
+package tectech.recipe;
+
+import static com.google.common.math.IntMath.pow;
+import static gregtech.api.GregTechAPI.getUnificatedOreDictStack;
+import static gregtech.api.enums.Mods.NewHorizonsCoreMod;
+import static gregtech.api.util.GTModHandler.getModItem;
+import static gregtech.api.util.GTUtility.getPlasmaFuelValueInEUPerLiterFromMaterial;
+import static java.lang.Math.min;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.fluids.FluidStack;
+
+import org.apache.commons.lang3.tuple.Pair;
+
+import gnu.trove.map.TMap;
+import gnu.trove.map.hash.TCustomHashMap;
+import gnu.trove.strategy.HashingStrategy;
+import gregtech.api.enums.Materials;
+import gregtech.api.enums.MaterialsUEVplus;
+import gregtech.api.enums.OrePrefixes;
+import gregtech.api.util.GTOreDictUnificator;
+import gtneioreplugin.plugin.block.BlockDimensionDisplay;
+import gtneioreplugin.util.GT5OreLayerHelper;
+import gtneioreplugin.util.GT5OreSmallHelper;
+import tectech.util.FluidStackLong;
+import tectech.util.ItemStackLong;
+
+@SuppressWarnings("SpellCheckingInspection")
+public class EyeOfHarmonyRecipe {
+
+ static final FluidStackLong[] SPECIAL_FLUIDS = new FluidStackLong[] {
+ new FluidStackLong(MaterialsUEVplus.WhiteDwarfMatter.getMolten(1_152), 1_152),
+ new FluidStackLong(MaterialsUEVplus.WhiteDwarfMatter.getMolten(1_152), 1_152),
+ new FluidStackLong(MaterialsUEVplus.WhiteDwarfMatter.getMolten(4_608), 4_608),
+ new FluidStackLong(MaterialsUEVplus.WhiteDwarfMatter.getMolten(18_432), 18_432),
+ new FluidStackLong(MaterialsUEVplus.BlackDwarfMatter.getMolten(1_152), 1_152),
+ new FluidStackLong(MaterialsUEVplus.BlackDwarfMatter.getMolten(4_608), 4_608),
+ new FluidStackLong(MaterialsUEVplus.BlackDwarfMatter.getMolten(18_432), 18_432),
+ new FluidStackLong(MaterialsUEVplus.Universium.getMolten(1_152), 1_152),
+ new FluidStackLong(MaterialsUEVplus.Universium.getMolten(4_608), 4_608),
+ new FluidStackLong(MaterialsUEVplus.Universium.getMolten(18_432), 18_432) };
+
+ HashingStrategy<ItemStack> itemStackHashingStrategy = new HashingStrategy<>() {
+
+ private static final long serialVersionUID = -3966004160368229212L;
+
+ @Override
+ public int computeHashCode(ItemStack stack) {
+ // Not really sure how this works or if it is "unique enough".
+ int result = stack.getItem()
+ .hashCode();
+ result = 31 * result + stack.getItemDamage();
+ return result;
+ }
+
+ @Override
+ public boolean equals(ItemStack item1, ItemStack item2) {
+ return item1.getUnlocalizedName()
+ .equals(item2.getUnlocalizedName());
+ }
+ };
+
+ private final TMap<ItemStack, Double> itemStackToProbabilityMap = new TCustomHashMap<>(itemStackHashingStrategy);
+ private final TMap<ItemStack, Long> itemStackToTrueStackSizeMap = new TCustomHashMap<>(itemStackHashingStrategy);
+
+ private final ArrayList<ItemStackLong> outputItems;
+ private final ArrayList<FluidStackLong> outputFluids;
+
+ private final long hydrogenRequirement;
+ private final long heliumRequirement;
+
+ private final long euOutput;
+ private final long euStartCost;
+
+ private final double baseSuccessChance;
+
+ private final long spacetimeCasingTierRequired;
+
+ private final long miningTimeSeconds;
+
+ private final double recipeEnergyEfficiency;
+
+ private final ItemStack recipeTriggerItem;
+
+ private final long sumOfItems;
+ private final long rocketTier;
+
+ public TMap<ItemStack, Double> getItemStackToProbabilityMap() {
+ return itemStackToProbabilityMap;
+ }
+
+ public TMap<ItemStack, Long> getItemStackToTrueStackSizeMap() {
+ return itemStackToTrueStackSizeMap;
+ }
+
+ public double getRecipeEnergyEfficiency() {
+ return recipeEnergyEfficiency;
+ }
+
+ @SuppressWarnings("FieldCanBeLocal")
+ private final long standardRecipeEUOutPerTick = 100 * EyeOfHarmonyRecipeStorage.BILLION;
+
+ public long getSumOfItems() {
+ return sumOfItems;
+ }
+
+ public long getRocketTier() {
+ return rocketTier;
+ }
+
+ public EyeOfHarmonyRecipe(final ArrayList<Pair<Materials, Long>> materialList, final BlockDimensionDisplay block,
+ final double recipeEnergyEfficiency, final long hydrogenRequirement, final long heliumRequirement,
+ final long miningTimeSeconds, final long rocketTierOfRecipe, final double baseSuccessChance) {
+
+ this.rocketTier = rocketTierOfRecipe;
+ this.spacetimeCasingTierRequired = min(8, rocketTierOfRecipe);
+
+ this.recipeTriggerItem = new ItemStack(block);
+
+ this.outputItems = validDustGenerator(materialList);
+
+ this.sumOfItems = this.outputItems.stream()
+ .map(ItemStackLong::getStackSize)
+ .reduce(0L, Long::sum);
+
+ this.outputItems.add(new ItemStackLong(getStoneDustType(block.getDimension()), this.sumOfItems * 3L));
+ this.outputItems.sort(Comparator.comparingLong(ItemStackLong::getStackSize));
+ Collections.reverse(this.outputItems);
+
+ for (ItemStackLong itemStackLong : outputItems) {
+ double stackSize = (double) itemStackLong.getStackSize();
+ double probability = Math.round(100_000 * stackSize / sumOfItems) / 1000.0;
+
+ itemStackToProbabilityMap.put(itemStackLong.itemStack, probability);
+ itemStackToTrueStackSizeMap.put(itemStackLong.itemStack, itemStackLong.stackSize);
+ }
+ // End item processing.
+
+ // --- Fluid handling ---
+ ArrayList<FluidStackLong> fluidStackLongArrayList = new ArrayList<>();
+
+ int plasmaAmount = (int) ((this.spacetimeCasingTierRequired + 1) * 8_000_000L);
+
+ // If DeepDark then it should output all plasmas involved in making exotic catalyst.
+ if (rocketTier == 9) {
+ for (Materials material : VALID_PLASMAS) {
+ fluidStackLongArrayList.add(new FluidStackLong(material.getPlasma(plasmaAmount), plasmaAmount));
+ }
+ } else {
+ // --- Output and process fluids of the recipe.
+ ArrayList<FluidStack> fluidStackArrayList = new ArrayList<>(validPlasmaGenerator(materialList));
+ for (FluidStack fluidStack : fluidStackArrayList) {
+ fluidStack = new FluidStack(fluidStack, plasmaAmount);
+ fluidStackLongArrayList.add(new FluidStackLong(fluidStack, plasmaAmount));
+ }
+ }
+
+ // Add a bonus fluid of compressed star matter.
+ fluidStackLongArrayList.add(
+ new FluidStackLong(
+ MaterialsUEVplus.RawStarMatter.getFluid((this.spacetimeCasingTierRequired + 1) * 100_000),
+ (this.spacetimeCasingTierRequired + 1) * 100_000));
+
+ // Tier 0 & 1 - 576 White
+ // Tier 2 - 2304 White
+ // Tier 3 - 9216 White
+
+ // Tier 4 - 576 Black
+ // Tier 5 - 2304 Black
+ // Tier 6 - 9216 Black
+
+ // Tier 7 - 576 Universium
+ // Tier 8 - 2304 Universium
+ // Tier 9 - 9216 Universium
+ int spacetimeTier = (int) rocketTierOfRecipe;
+ if (spacetimeTier == 0 || spacetimeTier == 9) {
+ spacetimeTier -= 1;
+ }
+ fluidStackLongArrayList.add(SPECIAL_FLUIDS[spacetimeTier + 1]);
+
+ outputFluids = fluidStackLongArrayList;
+ // --- End fluid handling ---.
+
+ this.hydrogenRequirement = hydrogenRequirement;
+ this.heliumRequirement = heliumRequirement;
+
+ this.baseSuccessChance = baseSuccessChance;
+
+ this.miningTimeSeconds = miningTimeSeconds;
+ this.recipeEnergyEfficiency = recipeEnergyEfficiency;
+
+ long plasmaEU = plasmaCostCalculator(outputFluids);
+ long VM3EU = miningTimeSeconds * pow(2, 19) * 20;
+ this.euStartCost = (plasmaEU + VM3EU + standardRecipeEUOutPerTick * 20 * miningTimeSeconds);
+ this.euOutput = (long) (euStartCost * recipeEnergyEfficiency);
+ }
+
+ private ItemStack getStoneDustType(String key) {
+ ItemStack placeholder = GTOreDictUnificator.get(OrePrefixes.dust, Materials.Stone, 1);
+ return switch (key) {
+ case "Ne" -> GTOreDictUnificator.get(OrePrefixes.dust, Materials.Netherrack, 1);
+ case "ED", "VA", "EA" -> GTOreDictUnificator.get(OrePrefixes.dust, Materials.Endstone, 1);
+ case "Mo" -> getModItem(NewHorizonsCoreMod.ID, "item.MoonStoneDust", 1, placeholder);
+ case "De" -> getModItem(NewHorizonsCoreMod.ID, "item.DeimosStoneDust", 1, placeholder);
+ case "Ma" -> getModItem(NewHorizonsCoreMod.ID, "item.MarsStoneDust", 1, placeholder);
+ case "Ph" -> getModItem(NewHorizonsCoreMod.ID, "item.PhobosStoneDust", 1, placeholder);
+ case "As", "KB" -> getModItem(NewHorizonsCoreMod.ID, "item.AsteroidsStoneDust", 1, placeholder);
+ case "Ca" -> getModItem(NewHorizonsCoreMod.ID, "item.CallistoStoneDust", 1, placeholder);
+ case "Ce" -> getModItem(NewHorizonsCoreMod.ID, "item.CeresStoneDust", 1, placeholder);
+ case "Eu" -> getModItem(NewHorizonsCoreMod.ID, "item.EuropaStoneDust", 1, placeholder);
+ case "Ga" -> getModItem(NewHorizonsCoreMod.ID, "item.GanymedeStoneDust", 1, placeholder);
+ case "Io" -> getModItem(NewHorizonsCoreMod.ID, "item.IoStoneDust", 1, placeholder);
+ case "Me" -> getModItem(NewHorizonsCoreMod.ID, "item.MercuryStoneDust", 1, placeholder);
+ case "Ve" -> getModItem(NewHorizonsCoreMod.ID, "item.VenusStoneDust", 1, placeholder);
+ case "En" -> getModItem(NewHorizonsCoreMod.ID, "item.EnceladusStoneDust", 1, placeholder);
+ case "Mi" -> getModItem(NewHorizonsCoreMod.ID, "item.MirandaStoneDust", 1, placeholder);
+ case "Ob" -> getModItem(NewHorizonsCoreMod.ID, "item.OberonStoneDust", 1, placeholder);
+ case "Ti" -> getModItem(NewHorizonsCoreMod.ID, "item.TitanStoneDust", 1, placeholder);
+ case "Pr" -> getModItem(NewHorizonsCoreMod.ID, "item.ProteusStoneDust", 1, placeholder);
+ case "Tr" -> getModItem(NewHorizonsCoreMod.ID, "item.TritonStoneDust", 1, placeholder);
+ case "Ha" -> getModItem(NewHorizonsCoreMod.ID, "item.HaumeaStoneDust", 1, placeholder);
+ case "MM" -> getModItem(NewHorizonsCoreMod.ID, "item.MakeMakeStoneDust", 1, placeholder);
+ case "Pl" -> getModItem(NewHorizonsCoreMod.ID, "item.PlutoStoneDust", 1, placeholder);
+ case "BE" -> getModItem(NewHorizonsCoreMod.ID, "item.BarnardaEStoneDust", 1, placeholder);
+ case "BF" -> getModItem(NewHorizonsCoreMod.ID, "item.BarnardaFStoneDust", 1, placeholder);
+ case "CB" -> getModItem(NewHorizonsCoreMod.ID, "item.CentauriAStoneDust", 1, placeholder);
+ case "TE" -> getModItem(NewHorizonsCoreMod.ID, "item.TCetiEStoneDust", 1, placeholder);
+ case "VB" -> getModItem(NewHorizonsCoreMod.ID, "item.VegaBStoneDust", 1, placeholder);
+ default -> placeholder;
+ };
+ }
+
+ public EyeOfHarmonyRecipe(final GT5OreLayerHelper.NormalOreDimensionWrapper normalOreDimensionWrapper,
+ final GT5OreSmallHelper.SmallOreDimensionWrapper smallOreDimensionWrapper, final BlockDimensionDisplay block,
+ final double recipeEnergyEfficiency, final long hydrogenRequirement, final long heliumRequirement,
+ final long miningTimeSeconds, final long spacetimeCasingTierRequired, final double baseSuccessChance) {
+
+ // Process recipes output items.
+ // 6 * 64 = 6 stacks/second for VM tier 3 + Og gas.
+ this(
+ processDimension(normalOreDimensionWrapper, smallOreDimensionWrapper, miningTimeSeconds),
+ block,
+ recipeEnergyEfficiency,
+ hydrogenRequirement,
+ heliumRequirement,
+ miningTimeSeconds,
+ spacetimeCasingTierRequired,
+ baseSuccessChance);
+ }
+
+ // Return clone of list. Deep copy. Maybe a better way to do this?
+ public ArrayList<ItemStackLong> getOutputItems() {
+ ArrayList<ItemStackLong> copyOutputList = new ArrayList<>();
+ for (ItemStackLong itemStackLong : outputItems) {
+ copyOutputList.add(new ItemStackLong(itemStackLong));
+ }
+
+ return copyOutputList;
+ }
+
+ // Deep copy.
+ public ArrayList<FluidStackLong> getOutputFluids() {
+ ArrayList<FluidStackLong> copyOutputList = new ArrayList<>();
+
+ for (FluidStackLong fluidStackLong : outputFluids) {
+ copyOutputList.add(new FluidStackLong(fluidStackLong));
+ }
+
+ return copyOutputList;
+ }
+
+ public long getHydrogenRequirement() {
+ return hydrogenRequirement;
+ }
+
+ public long getHeliumRequirement() {
+ return heliumRequirement;
+ }
+
+ public long getEUOutput() {
+ return euOutput;
+ }
+
+ public long getEUStartCost() {
+ return euStartCost;
+ }
+
+ public long getRecipeTimeInTicks() {
+ return miningTimeSeconds * 20;
+ }
+
+ public double getBaseRecipeSuccessChance() {
+ return baseSuccessChance;
+ }
+
+ public long getSpacetimeCasingTierRequired() {
+ return spacetimeCasingTierRequired;
+ }
+
+ public ItemStack getRecipeTriggerItem() {
+ return recipeTriggerItem;
+ }
+
+ private static final double PRIMARY_MULTIPLIER = (0.1 + 1.0 / 9.0); // Byproduct from macerating/washing chance.
+ private static final double SECONDARY_MULTIPLIER = (1.0 / 9.0); // Thermal centrifuge byproduct chance.
+ private static final double TERTIARY_MULTIPLIER = (0.1); // Macerating thermal centrifuged byproduct chance.
+ private static final double QUATERNARY_MULTIPLIER = (0.7); // Mercury/chem bath processing chance.
+
+ private static final double[] ORE_MULTIPLIER = { PRIMARY_MULTIPLIER, SECONDARY_MULTIPLIER, TERTIARY_MULTIPLIER,
+ QUATERNARY_MULTIPLIER };
+
+ public static class HashMapHelper extends HashMap<Materials, Double> {
+
+ private static final long serialVersionUID = 2297018142561480614L;
+
+ void add(Materials material, double value) {
+
+ // If key already exists.
+ if (this.containsKey(material)) {
+ this.put(material, value + this.get(material));
+ return;
+ }
+
+ // Otherwise, add value to hashmap entry.
+ this.put(material, value);
+ }
+ }
+
+ public static void processHelper(HashMapHelper outputMap, Materials material, double mainMultiplier,
+ double probability) {
+ if (material == null) return;
+ outputMap.add(material.mDirectSmelting, (material.mOreMultiplier * 2) * mainMultiplier * probability);
+
+ int index = 0;
+ for (Materials byProductMaterial : material.mOreByProducts) {
+ outputMap
+ .add(byProductMaterial.mDirectSmelting, mainMultiplier * (ORE_MULTIPLIER[index++] * 2) * probability);
+ }
+ }
+
+ private static ArrayList<Pair<Materials, Long>> processDimension(
+ GT5OreLayerHelper.NormalOreDimensionWrapper normalOreDimWrapper,
+ GT5OreSmallHelper.SmallOreDimensionWrapper smallOreDimWrapper, long timeInSeconds) {
+ HashMapHelper outputMap = new HashMapHelper();
+
+ double mainMultiplier = timeInSeconds * 384.0;
+
+ if (normalOreDimWrapper != null) {
+ normalOreDimWrapper.oreVeinToProbabilityInDimension.forEach((veinInfo, probability) -> {
+ processHelper(outputMap, veinInfo.mPrimaryVeinMaterial, mainMultiplier, probability);
+ processHelper(outputMap, veinInfo.mSecondaryMaterial, mainMultiplier, probability);
+ // 8.0 to replicate void miner getDropsVanillaVeins method yields.
+ processHelper(outputMap, veinInfo.mBetweenMaterial, mainMultiplier / 8.0, probability);
+ processHelper(outputMap, veinInfo.mSporadicMaterial, mainMultiplier / 8.0, probability);
+ });
+ }
+
+ // Iterate over small ores in dimension and add them, kinda hacky but works and is close enough.
+ if (smallOreDimWrapper != null) {
+ smallOreDimWrapper.oreVeinToProbabilityInDimension.forEach(
+ (veinInfo,
+ probability) -> processHelper(outputMap, veinInfo.getOreMaterial(), mainMultiplier, probability));
+ }
+
+ ArrayList<Pair<Materials, Long>> outputList = new ArrayList<>();
+
+ outputMap.forEach((material, quantity) -> outputList.add(Pair.of(material, (long) Math.floor(quantity))));
+
+ return outputList;
+ }
+
+ private static ArrayList<FluidStack> validPlasmaGenerator(final List<Pair<Materials, Long>> planetList) {
+
+ ArrayList<FluidStack> plasmaList = new ArrayList<>();
+
+ for (Pair<Materials, Long> pair : planetList) {
+ if (VALID_PLASMAS.contains(pair.getLeft())) {
+ plasmaList.add(
+ pair.getLeft()
+ .getPlasma(1));
+ }
+ }
+
+ return plasmaList;
+ }
+
+ private static ArrayList<ItemStackLong> validDustGenerator(final ArrayList<Pair<Materials, Long>> planetList) {
+
+ ArrayList<ItemStackLong> dustList = new ArrayList<>();
+
+ for (Pair<Materials, Long> pair : planetList) {
+ ItemStack dust = getUnificatedOreDictStack(
+ pair.getLeft()
+ .getDust(1));
+ if (dust != null) {
+ dustList.add(new ItemStackLong(dust, pair.getRight()));
+ }
+ }
+ return dustList;
+ }
+
+ private static long plasmaCostCalculator(ArrayList<FluidStackLong> plasmas) {
+ long total = 0;
+
+ for (FluidStackLong plasma : plasmas) {
+ FluidStack plasmaFluid = plasma.getRegularFluidStack(plasma, 1);
+ try {
+ String plasmaName = plasmaFluid.getFluid()
+ .getUnlocalizedName();
+ total += plasmaEnergyMap.getOrDefault(plasmaName, 0L) * plasma.amount;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ return (long) (total * getMaxPlasmaTurbineEfficiency());
+ }
+
+ private static double getMaxPlasmaTurbineEfficiency() {
+ // I hate Shirabon.
+ return 3.85;
+ }
+
+ private static final List<Materials> VALID_PLASMAS = Stream
+ .of(
+ Materials.Helium,
+ Materials.Iron,
+ Materials.Calcium,
+ Materials.Niobium,
+ Materials.Nitrogen,
+ Materials.Zinc,
+ Materials.Silver,
+ Materials.Titanium,
+ Materials.Radon,
+ Materials.Nickel,
+ Materials.Boron,
+ Materials.Sulfur,
+ Materials.Americium,
+ Materials.Bismuth,
+ Materials.Oxygen,
+ Materials.Tin)
+ .collect(Collectors.toList());
+
+ private static final HashMap<String, Long> plasmaEnergyMap = new HashMap<>() {
+
+ private static final long serialVersionUID = 7933945171103801933L;
+
+ {
+ VALID_PLASMAS.forEach(
+ (material -> put(
+ material.getPlasma(1)
+ .getFluid()
+ .getUnlocalizedName(),
+ (long) (getPlasmaFuelValueInEUPerLiterFromMaterial(material) * getMaxPlasmaTurbineEfficiency()))));
+ }
+ };
+}
diff --git a/src/main/java/tectech/recipe/EyeOfHarmonyRecipeStorage.java b/src/main/java/tectech/recipe/EyeOfHarmonyRecipeStorage.java
new file mode 100644
index 0000000000..30671dc919
--- /dev/null
+++ b/src/main/java/tectech/recipe/EyeOfHarmonyRecipeStorage.java
@@ -0,0 +1,186 @@
+package tectech.recipe;
+
+import static java.lang.Math.pow;
+import static tectech.recipe.EyeOfHarmonyRecipe.processHelper;
+import static tectech.recipe.TecTechRecipeMaps.eyeOfHarmonyRecipes;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+
+import net.minecraft.block.Block;
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.fluids.FluidStack;
+
+import org.apache.commons.lang3.tuple.Pair;
+
+import com.google.common.math.LongMath;
+
+import gregtech.api.enums.GTValues;
+import gregtech.api.enums.Materials;
+import gregtech.api.enums.MaterialsUEVplus;
+import gregtech.api.enums.OrePrefixes;
+import gregtech.api.util.GTOreDictUnificator;
+import gtneioreplugin.plugin.block.BlockDimensionDisplay;
+import gtneioreplugin.plugin.block.ModBlocks;
+import gtneioreplugin.util.DimensionHelper;
+import gtneioreplugin.util.GT5OreLayerHelper;
+import gtneioreplugin.util.GT5OreSmallHelper;
+import tectech.util.FluidStackLong;
+import tectech.util.ItemStackLong;
+
+public class EyeOfHarmonyRecipeStorage {
+
+ public static final long BILLION = LongMath.pow(10, 9);
+ private static final double CHANCE_DECREASE_PER_DIMENSION = 0.05;
+
+ // Map is unique so this is fine.
+ HashMap<Block, String> blocksMapInverted = new HashMap<>() {
+
+ private static final long serialVersionUID = -1634011860327553337L;
+
+ {
+ ModBlocks.blocks.forEach((dimString, dimBlock) -> { put(dimBlock, dimString); });
+ }
+ };
+
+ private final HashMap<String, EyeOfHarmonyRecipe> recipeHashMap = new HashMap<String, EyeOfHarmonyRecipe>() {
+
+ private static final long serialVersionUID = -3501819612517400500L;
+
+ {
+ for (String dimAbbreviation : DimensionHelper.DimNameDisplayed) {
+ BlockDimensionDisplay blockDimensionDisplay = (BlockDimensionDisplay) ModBlocks.blocks
+ .get(dimAbbreviation);
+
+ if (dimAbbreviation.equals("DD")) {
+ specialDeepDarkRecipe(this, blockDimensionDisplay);
+ } else {
+
+ GT5OreLayerHelper.NormalOreDimensionWrapper normalOre = GT5OreLayerHelper.dimToOreWrapper
+ .getOrDefault(dimAbbreviation, null);
+ GT5OreSmallHelper.SmallOreDimensionWrapper smallOre = GT5OreSmallHelper.dimToSmallOreWrapper
+ .getOrDefault(dimAbbreviation, null);
+ if (normalOre == null && smallOre == null) {
+ // No ores are generated in this dimension. Fail silently.
+ continue;
+ }
+
+ long spacetimeTier = blockDimensionDisplay.getDimensionRocketTier();
+ if (spacetimeTier == 0) {
+ spacetimeTier += 1;
+ }
+
+ put(
+ dimAbbreviation,
+ new EyeOfHarmonyRecipe(
+ normalOre,
+ smallOre,
+ blockDimensionDisplay,
+ 0.6 + blockDimensionDisplay.getDimensionRocketTier() / 10.0,
+ BILLION * (blockDimensionDisplay.getDimensionRocketTier() + 1),
+ BILLION * (blockDimensionDisplay.getDimensionRocketTier() + 1),
+ timeCalculator(blockDimensionDisplay.getDimensionRocketTier()),
+ spacetimeTier - 1,
+ 1.0 - CHANCE_DECREASE_PER_DIMENSION * blockDimensionDisplay.getDimensionRocketTier()));
+ }
+ }
+ }
+ };
+
+ public EyeOfHarmonyRecipe recipeLookUp(final ItemStack aStack) {
+ String dimAbbreviation = blocksMapInverted.get(Block.getBlockFromItem(aStack.getItem()));
+ return recipeHashMap.get(dimAbbreviation);
+ }
+
+ public EyeOfHarmonyRecipeStorage() {
+
+ for (EyeOfHarmonyRecipe recipe : recipeHashMap.values()) {
+
+ ArrayList<ItemStack> outputItems = new ArrayList<>();
+ for (ItemStackLong itemStackLong : recipe.getOutputItems()) {
+ outputItems.add(itemStackLong.itemStack);
+ }
+
+ ArrayList<FluidStack> outputFluids = new ArrayList<>();
+ for (FluidStackLong fluidStackLong : recipe.getOutputFluids()) {
+ outputFluids.add(fluidStackLong.fluidStack);
+ }
+
+ ItemStack planetItem = recipe.getRecipeTriggerItem()
+ .copy();
+ planetItem.stackSize = 0;
+
+ GTValues.RA.stdBuilder()
+ .itemInputs(planetItem)
+ .itemOutputs(outputItems.toArray(new ItemStack[0]))
+ .fluidInputs(
+ Materials.Hydrogen.getGas(0),
+ Materials.Helium.getGas(0),
+ MaterialsUEVplus.RawStarMatter.getFluid(0))
+ .fluidOutputs(outputFluids.toArray(new FluidStack[0]))
+ .duration(recipe.getRecipeTimeInTicks())
+ .eut(0)
+ .special(recipe)
+ .noOptimize()
+ .addTo(eyeOfHarmonyRecipes);
+ }
+ }
+
+ private void specialDeepDarkRecipe(final HashMap<String, EyeOfHarmonyRecipe> hashMap,
+ final BlockDimensionDisplay planetItem) {
+
+ HashSet<Materials> validMaterialSet = new HashSet<>();
+
+ for (Materials material : Materials.values()) {
+
+ ItemStack normalOre = GTOreDictUnificator.get(OrePrefixes.ore, material, 1);
+
+ if ((normalOre != null)) {
+ validMaterialSet.add(material);
+ }
+
+ ItemStack smallOre = GTOreDictUnificator.get(OrePrefixes.oreSmall, material, 1);
+
+ if ((smallOre != null)) {
+ validMaterialSet.add(material);
+ }
+ }
+
+ ArrayList<Materials> validMaterialList = new ArrayList<>(validMaterialSet);
+
+ long rocketTier = 9;
+
+ hashMap.put(
+ "DD",
+ new EyeOfHarmonyRecipe(
+ processDD(validMaterialList),
+ planetItem,
+ 0.6 + rocketTier / 10.0,
+ BILLION * (rocketTier + 1),
+ BILLION * (rocketTier + 1),
+ timeCalculator(rocketTier),
+ rocketTier, // -1 so that we avoid out of bounds exception on NEI render.
+ 1.0 - rocketTier * CHANCE_DECREASE_PER_DIMENSION));
+ }
+
+ private static long timeCalculator(final long rocketTier) {
+ return (long) (18_000L * pow(1.4, rocketTier));
+ }
+
+ private ArrayList<Pair<Materials, Long>> processDD(final ArrayList<Materials> validMaterialList) {
+ EyeOfHarmonyRecipe.HashMapHelper outputMap = new EyeOfHarmonyRecipe.HashMapHelper();
+
+ // 10 from rocketTier + 1, 6 * 64 = VM3 + Og, 1.4 = time increase per tier.
+ double mainMultiplier = (timeCalculator(10) * (6 * 64));
+ double probability = 1.0 / validMaterialList.size();
+
+ validMaterialList.forEach((material) -> { processHelper(outputMap, material, mainMultiplier, probability); });
+
+ ArrayList<Pair<Materials, Long>> outputList = new ArrayList<>();
+
+ outputMap.forEach((material, quantity) -> outputList.add(Pair.of(material, (long) Math.floor(quantity))));
+
+ return outputList;
+ }
+}
diff --git a/src/main/java/tectech/recipe/GodforgeExoticFrontend.java b/src/main/java/tectech/recipe/GodforgeExoticFrontend.java
new file mode 100644
index 0000000000..cb19f8862d
--- /dev/null
+++ b/src/main/java/tectech/recipe/GodforgeExoticFrontend.java
@@ -0,0 +1,82 @@
+package tectech.recipe;
+
+import static gregtech.api.util.GTUtility.trans;
+import static tectech.loader.recipe.Godforge.magmatterItemsForNEI;
+import static tectech.loader.recipe.Godforge.magmatterSpaceFluidItemsForNEI;
+import static tectech.loader.recipe.Godforge.magmatterTimeFluidItemsForNEI;
+import static tectech.loader.recipe.Godforge.quarkGluonFluidItemsForNEI;
+import static tectech.loader.recipe.Godforge.quarkGluonItemsForNEI;
+
+import java.util.Collections;
+import java.util.List;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+
+import com.gtnewhorizons.modularui.api.math.Pos2d;
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.common.widget.DrawableWidget;
+
+import codechicken.nei.PositionedStack;
+import gregtech.api.enums.MaterialsUEVplus;
+import gregtech.api.recipe.BasicUIPropertiesBuilder;
+import gregtech.api.recipe.NEIRecipePropertiesBuilder;
+import gregtech.api.recipe.RecipeMapFrontend;
+import gregtech.api.util.GTUtility;
+import gregtech.api.util.MethodsReturnNonnullByDefault;
+import gregtech.nei.GTNEIDefaultHandler;
+import gregtech.nei.RecipeDisplayInfo;
+import tectech.thing.gui.TecTechUITextures;
+
+@ParametersAreNonnullByDefault
+@MethodsReturnNonnullByDefault
+public class GodforgeExoticFrontend extends RecipeMapFrontend {
+
+ public GodforgeExoticFrontend(BasicUIPropertiesBuilder uiPropertiesBuilder,
+ NEIRecipePropertiesBuilder neiPropertiesBuilder) {
+ super(uiPropertiesBuilder, neiPropertiesBuilder);
+ }
+
+ @Override
+ public void addGregTechLogo(ModularWindow.Builder builder, Pos2d windowOffset) {
+ builder.widget(
+ new DrawableWidget().setDrawable(TecTechUITextures.PICTURE_GODFORGE_LOGO)
+ .setSize(18, 18)
+ .setPos(new Pos2d(151, 63).add(windowOffset)));
+ }
+
+ @Override
+ public void drawNEIOverlays(GTNEIDefaultHandler.CachedDefaultRecipe neiCachedRecipe) {
+ if (neiCachedRecipe.mRecipe.mFluidOutputs[0].equals(MaterialsUEVplus.QuarkGluonPlasma.getFluid(1000))) {
+ neiCachedRecipe.mInputs.set(0, new PositionedStack(quarkGluonItemsForNEI, 48, 23, true));
+ neiCachedRecipe.mInputs.set(1, new PositionedStack(quarkGluonFluidItemsForNEI, 48, 52, true));
+ } else {
+ neiCachedRecipe.mInputs.set(0, new PositionedStack(magmatterItemsForNEI, 48, 23, true));
+ neiCachedRecipe.mInputs.set(1, new PositionedStack(magmatterSpaceFluidItemsForNEI, 30, 52, true));
+ neiCachedRecipe.mInputs.set(2, new PositionedStack(magmatterTimeFluidItemsForNEI, 48, 52, true));
+ }
+ }
+
+ @Override
+ public List<Pos2d> getItemInputPositions(int itemInputCount) {
+ return Collections.singletonList(new Pos2d(52, 33));
+ }
+
+ @Override
+ public List<Pos2d> getItemOutputPositions(int itemOutputCount) {
+ return Collections.singletonList(new Pos2d(106, 33));
+ }
+
+ @Override
+ protected void drawEnergyInfo(RecipeDisplayInfo recipeInfo) {
+ long eut = recipeInfo.recipe.mEUt;
+ long duration = recipeInfo.recipe.mDuration;
+ recipeInfo.drawText(trans("152", "Total: ") + GTUtility.formatNumbers(eut * duration) + " EU");
+ recipeInfo.drawText(trans("153", "Usage: ") + GTUtility.formatNumbers(eut) + " EU/t");
+ recipeInfo.drawText(trans("158", "Time: ") + GTUtility.formatNumbers(duration / 20) + " secs");
+
+ }
+
+ @Override
+ protected void drawDurationInfo(RecipeDisplayInfo recipeInfo) {}
+
+}
diff --git a/src/main/java/tectech/recipe/GodforgePlasmaFrontend.java b/src/main/java/tectech/recipe/GodforgePlasmaFrontend.java
new file mode 100644
index 0000000000..5dfc46b1cf
--- /dev/null
+++ b/src/main/java/tectech/recipe/GodforgePlasmaFrontend.java
@@ -0,0 +1,79 @@
+package tectech.recipe;
+
+import static gregtech.api.util.GTUtility.trans;
+import static net.minecraft.util.StatCollector.translateToLocal;
+
+import java.util.Collections;
+import java.util.List;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+
+import com.gtnewhorizons.modularui.api.math.Pos2d;
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.common.widget.DrawableWidget;
+
+import gregtech.api.recipe.BasicUIPropertiesBuilder;
+import gregtech.api.recipe.NEIRecipePropertiesBuilder;
+import gregtech.api.recipe.RecipeMapFrontend;
+import gregtech.api.util.GTUtility;
+import gregtech.api.util.MethodsReturnNonnullByDefault;
+import gregtech.nei.RecipeDisplayInfo;
+import tectech.thing.gui.TecTechUITextures;
+
+@ParametersAreNonnullByDefault
+@MethodsReturnNonnullByDefault
+public class GodforgePlasmaFrontend extends RecipeMapFrontend {
+
+ public GodforgePlasmaFrontend(BasicUIPropertiesBuilder uiPropertiesBuilder,
+ NEIRecipePropertiesBuilder neiPropertiesBuilder) {
+ super(uiPropertiesBuilder, neiPropertiesBuilder);
+ }
+
+ @Override
+ public void addGregTechLogo(ModularWindow.Builder builder, Pos2d windowOffset) {
+ builder.widget(
+ new DrawableWidget().setDrawable(TecTechUITextures.PICTURE_GODFORGE_LOGO)
+ .setSize(18, 18)
+ .setPos(new Pos2d(151, 63).add(windowOffset)));
+ }
+
+ @Override
+ public List<Pos2d> getItemInputPositions(int itemInputCount) {
+ return Collections.singletonList(new Pos2d(52, 33));
+ }
+
+ @Override
+ public List<Pos2d> getItemOutputPositions(int itemOutputCount) {
+ return Collections.singletonList(new Pos2d(106, 33));
+ }
+
+ @Override
+ protected void drawEnergyInfo(RecipeDisplayInfo recipeInfo) {
+ long eut = recipeInfo.recipe.mEUt;
+ long duration = recipeInfo.recipe.mDuration;
+ String multistep = "No";
+ if (recipeInfo.recipe.mSpecialItems.toString()
+ .equals("true")) {
+ multistep = "Yes";
+ }
+ String requiredUpgrade = switch (recipeInfo.recipe.mSpecialValue) {
+ case 1 -> "T4-T5";
+ case 2 -> "Exotic";
+ default -> "T1-T3";
+ };
+
+ recipeInfo.drawText(trans("152", "Total: ") + GTUtility.formatNumbers(eut * duration) + " EU");
+ recipeInfo.drawText(trans("153", "Usage: ") + GTUtility.formatNumbers(eut) + " EU/t");
+ recipeInfo.drawText(
+ trans("158", "Time: ") + GTUtility.formatNumbers(duration / 20d)
+ + " secs"
+ + (duration < 20 ? " (" + duration + " ticks)" : ""));
+ recipeInfo.drawText(translateToLocal("gt.blockmachines.multimachine.FOG.plasmamultistep") + ": " + multistep);
+ recipeInfo
+ .drawText(translateToLocal("gt.blockmachines.multimachine.FOG.plasmarecipetier") + ": " + requiredUpgrade);
+ }
+
+ @Override
+ protected void drawDurationInfo(RecipeDisplayInfo recipeInfo) {}
+
+}
diff --git a/src/main/java/tectech/recipe/ResearchStationFrontend.java b/src/main/java/tectech/recipe/ResearchStationFrontend.java
new file mode 100644
index 0000000000..ce26215fbb
--- /dev/null
+++ b/src/main/java/tectech/recipe/ResearchStationFrontend.java
@@ -0,0 +1,101 @@
+package tectech.recipe;
+
+import static gregtech.api.util.GTUtility.trans;
+import static net.minecraft.util.StatCollector.translateToLocalFormatted;
+
+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.recipe.BasicUIPropertiesBuilder;
+import gregtech.api.recipe.NEIRecipePropertiesBuilder;
+import gregtech.api.recipe.RecipeMapFrontend;
+import gregtech.api.util.GTUtility;
+import gregtech.api.util.MethodsReturnNonnullByDefault;
+import gregtech.nei.RecipeDisplayInfo;
+import tectech.thing.gui.TecTechUITextures;
+
+@ParametersAreNonnullByDefault
+@MethodsReturnNonnullByDefault
+public class ResearchStationFrontend extends RecipeMapFrontend {
+
+ public ResearchStationFrontend(BasicUIPropertiesBuilder uiPropertiesBuilder,
+ NEIRecipePropertiesBuilder neiPropertiesBuilder) {
+ super(uiPropertiesBuilder, neiPropertiesBuilder);
+ }
+
+ @Override
+ public List<Pos2d> getItemInputPositions(int itemInputCount) {
+ return Collections.singletonList(new Pos2d(52, 33));
+ }
+
+ @Override
+ public List<Pos2d> getItemOutputPositions(int itemOutputCount) {
+ return Collections.singletonList(new Pos2d(106, 33));
+ }
+
+ @Override
+ public Pos2d getSpecialItemPosition() {
+ return new Pos2d(124, 62);
+ }
+
+ @Override
+ protected void drawEnergyInfo(RecipeDisplayInfo recipeInfo) {
+ long eut = recipeInfo.recipe.mEUt;
+ int computation = recipeInfo.recipe.mDuration;
+ short ampere = (short) (recipeInfo.recipe.mSpecialValue & 0xFFFF);
+ short minComputationPerSec = (short) (recipeInfo.recipe.mSpecialValue >>> 16);
+ recipeInfo.drawText(
+ translateToLocalFormatted(
+ "tt.nei.research.max_eu",
+ GTUtility.formatNumbers(
+ (1 + (computation - minComputationPerSec) / minComputationPerSec) * eut * ampere * 20)));
+ recipeInfo.drawText(trans("153", "Usage: ") + GTUtility.formatNumbers(eut * ampere) + " EU/t");
+ recipeInfo.drawText(trans("154", "Voltage: ") + GTUtility.formatNumbers(eut) + " EU/t");
+ recipeInfo.drawText(trans("155", "Amperage: ") + GTUtility.formatNumbers(ampere));
+ recipeInfo
+ .drawText(translateToLocalFormatted("tt.nei.research.computation", GTUtility.formatNumbers(computation)));
+ recipeInfo.drawText(
+ translateToLocalFormatted(
+ "tt.nei.research.min_computation",
+ GTUtility.formatNumbers(minComputationPerSec)));
+ }
+
+ @Override
+ protected void drawDurationInfo(RecipeDisplayInfo recipeInfo) {}
+
+ @Override
+ public void addProgressBar(ModularWindow.Builder builder, Supplier<Float> progressSupplier, Pos2d windowOffset) {
+ int bar1Width = 25;
+ int bar2Width = 11;
+ int bar3Height = 18;
+ List<Supplier<Float>> splitProgress = splitProgress(progressSupplier, bar1Width, bar2Width, bar3Height);
+ builder.widget(
+ new ProgressBar().setTexture(TecTechUITextures.PROGRESSBAR_RESEARCH_STATION_1, bar1Width)
+ .setDirection(ProgressBar.Direction.RIGHT)
+ .setProgress(splitProgress.get(0))
+ .setSynced(false, false)
+ .setPos(new Pos2d(81, 40).add(windowOffset))
+ .setSize(bar1Width, 5));
+ builder.widget(
+ new ProgressBar().setTexture(TecTechUITextures.PROGRESSBAR_RESEARCH_STATION_2, bar2Width)
+ .setDirection(ProgressBar.Direction.RIGHT)
+ .setProgress(splitProgress.get(1))
+ .setSynced(false, false)
+ .setPos(new Pos2d(124, 40).add(windowOffset))
+ .setSize(bar2Width, 5));
+ builder.widget(
+ new ProgressBar().setTexture(TecTechUITextures.PROGRESSBAR_RESEARCH_STATION_3, bar3Height)
+ .setDirection(ProgressBar.Direction.DOWN)
+ .setProgress(splitProgress.get(2))
+ .setSynced(false, false)
+ .setPos(new Pos2d(128, 44).add(windowOffset))
+ .setSize(10, bar3Height));
+ }
+}
diff --git a/src/main/java/tectech/recipe/TTRecipeAdder.java b/src/main/java/tectech/recipe/TTRecipeAdder.java
new file mode 100644
index 0000000000..69068afce0
--- /dev/null
+++ b/src/main/java/tectech/recipe/TTRecipeAdder.java
@@ -0,0 +1,251 @@
+package tectech.recipe;
+
+import static gregtech.api.recipe.RecipeMaps.assemblylineVisualRecipes;
+import static gregtech.api.util.GTRecipeConstants.RESEARCH_STATION_DATA;
+import static tectech.recipe.TecTechRecipeMaps.researchStationFakeRecipes;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+
+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.GTValues;
+import gregtech.api.enums.ItemList;
+import gregtech.api.util.AssemblyLineUtils;
+import gregtech.api.util.GTOreDictUnificator;
+import gregtech.api.util.GTRecipe;
+import gregtech.api.util.GTRecipe.RecipeAssemblyLine;
+import gregtech.api.util.GTUtility;
+import gregtech.common.RecipeAdder;
+import tectech.TecTech;
+import tectech.thing.CustomItemList;
+
+public class TTRecipeAdder extends RecipeAdder {
+
+ public static final ItemStack[] nullItem = new ItemStack[0];
+ public static final FluidStack[] nullFluid = new FluidStack[0];
+
+ @Deprecated
+ public static boolean addResearchableAssemblylineRecipe(ItemStack aResearchItem, int totalComputationRequired,
+ int computationRequiredPerSec, int researchEUt, int researchAmperage, ItemStack[] aInputs,
+ FluidStack[] aFluidInputs, ItemStack aOutput, int assDuration, int assEUt) {
+ if (aInputs == null) {
+ aInputs = nullItem;
+ }
+ if (aFluidInputs == null) {
+ aFluidInputs = nullFluid;
+ }
+ if (aResearchItem == null || totalComputationRequired <= 0 || aOutput == null || aInputs.length > 16) {
+ return false;
+ }
+ for (ItemStack tItem : aInputs) {
+ if (tItem == null) {
+ TecTech.LOGGER.error(
+ "addResearchableAssemblingLineRecipe " + aResearchItem.getDisplayName()
+ + " --> "
+ + aOutput.getUnlocalizedName()
+ + " there is some null item in that recipe");
+ }
+ }
+ researchAmperage = GTUtility.clamp(researchAmperage, 1, Short.MAX_VALUE);
+ computationRequiredPerSec = GTUtility.clamp(computationRequiredPerSec, 1, Short.MAX_VALUE);
+
+ GTRecipe.RecipeAssemblyLine recipeGT = new GTRecipe.RecipeAssemblyLine(
+ CustomItemList.UnusedStuff.get(1),
+ totalComputationRequired / computationRequiredPerSec,
+ aInputs,
+ aFluidInputs,
+ aOutput,
+ assDuration,
+ assEUt);
+ RecipeAssemblyLine recipeTT = new GTRecipe.RecipeAssemblyLine(
+ aResearchItem,
+ totalComputationRequired / computationRequiredPerSec,
+ aInputs,
+ aFluidInputs,
+ aOutput,
+ assDuration,
+ assEUt);
+ GTRecipe.RecipeAssemblyLine.sAssemblylineRecipes.add(recipeGT);
+ TecTechRecipeMaps.researchableALRecipeList.add(recipeTT);
+
+ ItemStack writesDataStick = ItemList.Tool_DataStick.getWithName(1L, "Writes Research result");
+ AssemblyLineUtils.setAssemblyLineRecipeOnDataStick(writesDataStick, recipeTT, false);
+ GTValues.RA.stdBuilder()
+ .itemInputs(aResearchItem)
+ .itemOutputs(aOutput)
+ .special(writesDataStick)
+ .duration(totalComputationRequired)
+ .eut(researchEUt)
+ .metadata(RESEARCH_STATION_DATA, researchAmperage | computationRequiredPerSec << 16)
+ .noOptimize()
+ .ignoreCollision()
+ .fake()
+ .addTo(researchStationFakeRecipes);
+
+ ItemStack readsDataStick = ItemList.Tool_DataStick.getWithName(1L, "Reads Research result");
+ AssemblyLineUtils.setAssemblyLineRecipeOnDataStick(readsDataStick, recipeTT, false);
+ GTValues.RA.stdBuilder()
+ .itemInputs(aInputs)
+ .itemOutputs(aOutput)
+ .fluidInputs(aFluidInputs)
+ .special(readsDataStick)
+ .duration(assDuration)
+ .eut(assEUt)
+ .ignoreCollision()
+ .fake()
+ .addTo(assemblylineVisualRecipes);
+ return true;
+ }
+
+ @Deprecated
+ public static boolean addResearchableAssemblylineRecipe(ItemStack aResearchItem, int totalComputationRequired,
+ int computationRequiredPerSec, int researchEUt, int researchAmperage, Object[] aInputs,
+ FluidStack[] aFluidInputs, ItemStack aOutput, int assDuration, int assEUt) {
+ if (aInputs == null) {
+ aInputs = nullItem;
+ }
+ if (aFluidInputs == null) {
+ aFluidInputs = nullFluid;
+ }
+ if (aResearchItem == null || totalComputationRequired <= 0
+ || aOutput == null
+ || aInputs.length > 16
+ || aFluidInputs.length > 4
+ || assDuration <= 0
+ || assEUt <= 0) {
+ return false;
+ }
+
+ ItemStack[] tInputs = new ItemStack[aInputs.length];
+ ItemStack[][] tAlts = new ItemStack[aInputs.length][];
+ int tPersistentHash = 1;
+ for (int i = 0; i < aInputs.length; i++) {
+ Object obj = aInputs[i];
+ if (obj instanceof ItemStack) {
+ tInputs[i] = (ItemStack) obj;
+ tAlts[i] = null;
+ tPersistentHash = tPersistentHash * 31 + GTUtility.persistentHash(tInputs[i], true, false);
+ continue;
+ } else if (obj instanceof ItemStack[]aStacks) {
+ if (aStacks.length > 0) {
+ tInputs[i] = aStacks[0];
+ tAlts[i] = Arrays.copyOf(aStacks, aStacks.length);
+ for (ItemStack tAlt : tAlts[i]) {
+ tPersistentHash = tPersistentHash * 31 + GTUtility.persistentHash(tAlt, true, false);
+ }
+ tPersistentHash *= 31;
+ continue;
+ }
+ } else if (obj instanceof Object[]objs) {
+ List<ItemStack> tList;
+ if (objs.length >= 2 && !(tList = GTOreDictUnificator.getOres(objs[0])).isEmpty()) {
+ try {
+ // sort the output, so the hash code is stable across launches
+ tList.sort(
+ Comparator
+ .<ItemStack, String>comparing(
+ s -> GameRegistry.findUniqueIdentifierFor(s.getItem()).modId)
+ .thenComparing(s -> GameRegistry.findUniqueIdentifierFor(s.getItem()).modId)
+ .thenComparingInt(Items.feather::getDamage)
+ .thenComparingInt(s -> s.stackSize));
+ int tAmount = ((Number) objs[1]).intValue();
+ List<ItemStack> uList = new ArrayList<>();
+ for (ItemStack tStack : tList) {
+ ItemStack uStack = GTUtility.copyAmount(tAmount, tStack);
+ if (GTUtility.isStackValid(uStack)) {
+ uList.add(uStack);
+ if (tInputs[i] == null) tInputs[i] = uStack;
+ }
+ }
+ tAlts[i] = uList.toArray(new ItemStack[0]);
+ tPersistentHash = tPersistentHash * 31 + (objs[0] == null ? "" : objs[0].toString()).hashCode();
+ tPersistentHash = tPersistentHash * 31 + tAmount;
+ continue;
+ } catch (Exception t) {
+ TecTech.LOGGER.error(
+ "addAssemblingLineRecipe " + aResearchItem.getDisplayName()
+ + " --> there is some ... in that recipe");
+ }
+ }
+ }
+ TecTech.LOGGER.error(
+ "addAssemblingLineRecipe " + aResearchItem.getDisplayName()
+ + " --> "
+ + aOutput.getUnlocalizedName()
+ + " there is some null item in that recipe");
+ }
+ tPersistentHash = tPersistentHash * 31 + GTUtility.persistentHash(aResearchItem, true, false);
+ tPersistentHash = tPersistentHash * 31 + GTUtility.persistentHash(aOutput, true, false);
+ for (FluidStack tFluidInput : aFluidInputs) {
+ if (tFluidInput == null) continue;
+ tPersistentHash = tPersistentHash * 31 + GTUtility.persistentHash(tFluidInput, true, false);
+ }
+ researchAmperage = GTUtility.clamp(researchAmperage, 1, Short.MAX_VALUE);
+ computationRequiredPerSec = GTUtility.clamp(computationRequiredPerSec, 1, Short.MAX_VALUE);
+ tPersistentHash = tPersistentHash * 31 + totalComputationRequired;
+ tPersistentHash = tPersistentHash * 31 + computationRequiredPerSec;
+ tPersistentHash = tPersistentHash * 31 + researchAmperage;
+ tPersistentHash = tPersistentHash * 31 + researchEUt;
+ tPersistentHash = tPersistentHash * 31 + assDuration;
+ tPersistentHash = tPersistentHash * 31 + assEUt;
+
+ GTRecipe.RecipeAssemblyLine recipeGT = new GTRecipe.RecipeAssemblyLine(
+ CustomItemList.UnusedStuff.get(1),
+ totalComputationRequired / computationRequiredPerSec,
+ tInputs,
+ aFluidInputs,
+ aOutput,
+ assDuration,
+ assEUt,
+ tAlts);
+ recipeGT.setPersistentHash(tPersistentHash);
+ GTRecipe.RecipeAssemblyLine.sAssemblylineRecipes.add(recipeGT);
+ GTRecipe.RecipeAssemblyLine recipeTT = new GTRecipe.RecipeAssemblyLine(
+ aResearchItem,
+ totalComputationRequired / computationRequiredPerSec,
+ tInputs,
+ aFluidInputs,
+ aOutput,
+ assDuration,
+ assEUt,
+ tAlts);
+ recipeTT.setPersistentHash(tPersistentHash);
+ TecTechRecipeMaps.researchableALRecipeList.add(recipeTT);
+
+ ItemStack writesDataStick = ItemList.Tool_DataStick.getWithName(1L, "Writes Research result");
+ AssemblyLineUtils.setAssemblyLineRecipeOnDataStick(writesDataStick, recipeTT, false);
+ GTValues.RA.stdBuilder()
+ .itemInputs(aResearchItem)
+ .itemOutputs(aOutput)
+ .special(writesDataStick)
+ .duration(totalComputationRequired)
+ .eut(researchEUt)
+ .metadata(RESEARCH_STATION_DATA, researchAmperage | computationRequiredPerSec << 16)
+ .noOptimize()
+ .ignoreCollision()
+ .fake()
+ .addTo(researchStationFakeRecipes);
+
+ ItemStack readsDataStick = ItemList.Tool_DataStick.getWithName(1L, "Reads Research result");
+ AssemblyLineUtils.setAssemblyLineRecipeOnDataStick(readsDataStick, recipeTT, false);
+ assemblylineVisualRecipes.addFakeRecipe(
+ false,
+ tInputs,
+ new ItemStack[] { aOutput },
+ new ItemStack[] { readsDataStick },
+ aFluidInputs,
+ null,
+ assDuration,
+ assEUt,
+ 0,
+ tAlts,
+ false);
+ return true;
+ }
+}
diff --git a/src/main/java/tectech/recipe/TecTechRecipeMaps.java b/src/main/java/tectech/recipe/TecTechRecipeMaps.java
new file mode 100644
index 0000000000..b1c3608f45
--- /dev/null
+++ b/src/main/java/tectech/recipe/TecTechRecipeMaps.java
@@ -0,0 +1,78 @@
+package tectech.recipe;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.gtnewhorizons.modularui.common.widget.ProgressBar;
+
+import gregtech.api.gui.modularui.GTUITextures;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.RecipeMapBackend;
+import gregtech.api.recipe.RecipeMapBuilder;
+import gregtech.api.util.GTRecipe;
+import tectech.thing.CustomItemList;
+import tectech.thing.gui.TecTechUITextures;
+
+public class TecTechRecipeMaps {
+
+ public static void init() {}
+
+ public static final List<GTRecipe.RecipeAssemblyLine> researchableALRecipeList = new ArrayList<>();
+
+ public static final RecipeMap<RecipeMapBackend> eyeOfHarmonyRecipes = RecipeMapBuilder.of("gt.recipe.eyeofharmony")
+ .maxIO(
+ EyeOfHarmonyFrontend.maxItemInputs,
+ EyeOfHarmonyFrontend.maxItemOutputs,
+ EyeOfHarmonyFrontend.maxFluidInputs,
+ EyeOfHarmonyFrontend.maxFluidOutputs)
+ .minInputs(1, 0)
+ .progressBar(GTUITextures.PROGRESSBAR_HAMMER, ProgressBar.Direction.DOWN)
+ .progressBarPos(78, 24 + 2)
+ .logoPos(10, 10)
+ .neiHandlerInfo(
+ builder -> builder.setDisplayStack(CustomItemList.Machine_Multi_EyeOfHarmony.get(1))
+ .setHeight(314)
+ .setMaxRecipesPerPage(1))
+ .frontend(EyeOfHarmonyFrontend::new)
+ .build();
+ public static final RecipeMap<RecipeMapBackend> researchStationFakeRecipes = RecipeMapBuilder
+ .of("gt.recipe.researchStation")
+ .maxIO(1, 1, 0, 0)
+ .useSpecialSlot()
+ .slotOverlays((index, isFluid, isOutput, isSpecial) -> {
+ if (isSpecial) {
+ return GTUITextures.OVERLAY_SLOT_DATA_ORB;
+ }
+ if (isOutput) {
+ return TecTechUITextures.OVERLAY_SLOT_MESH;
+ }
+ return GTUITextures.OVERLAY_SLOT_MICROSCOPE;
+ })
+ .addSpecialTexture(19, 12, 84, 60, TecTechUITextures.PICTURE_HEAT_SINK)
+ .addSpecialTexture(41, 22, 40, 40, TecTechUITextures.PICTURE_RACK_LARGE)
+ .logo(TecTechUITextures.PICTURE_TECTECH_LOGO)
+ .logoSize(18, 18)
+ .logoPos(151, 63)
+ .neiTransferRect(81, 33, 25, 18)
+ .neiTransferRect(124, 33, 18, 29)
+ .frontend(ResearchStationFrontend::new)
+ .neiHandlerInfo(builder -> builder.setDisplayStack(CustomItemList.Machine_Multi_Research.get(1)))
+ .build();
+
+ public static final RecipeMap<RecipeMapBackend> godforgePlasmaRecipes = RecipeMapBuilder.of("gt.recipe.fog_plasma")
+ .maxIO(1, 1, 1, 1)
+ .progressBar(TecTechUITextures.PROGRESSBAR_GODFORGE_PLASMA, ProgressBar.Direction.RIGHT)
+ .progressBarPos(78, 33)
+ .neiTransferRect(78, 33, 20, 20)
+ .frontend(GodforgePlasmaFrontend::new)
+ .build();
+ public static final RecipeMap<RecipeMapBackend> godforgeExoticMatterRecipes = RecipeMapBuilder
+ .of("gt.recipe.fog_exotic")
+ .maxIO(1, 1, 2, 1)
+ .progressBar(TecTechUITextures.PROGRESSBAR_GODFORGE_PLASMA, ProgressBar.Direction.RIGHT)
+ .progressBarPos(78, 33)
+ .neiTransferRect(78, 33, 20, 20)
+ .frontend(GodforgeExoticFrontend::new)
+ .build();
+
+}