From f74c7cc297d1d19d38a19683cd277ad9ce605d3a Mon Sep 17 00:00:00 2001 From: miozune Date: Mon, 4 Dec 2023 05:34:27 +0900 Subject: Refactor RecipeMap (#2345) * Remove deprecated and unused things * Move recipemap subclasses * Move GT_Recipe_Map to outside and rename to RecipeMap * Move recipemap instances to separated class & remove prepending s * Remove useless GT_Recipe constructors * Always use ModularUI * Rename IGT_RecipeMap -> IRecipeMap * Add RecipeMapBuilder * Remove more deprecated and unused things * Fix RecipeMap type parameters * Use multimap for recipe index * Fix bending recipe error in dev env * Remove mUniqueIdentifier * Update AE2FC * Less edgy texture for NEI recipe background * Add replicator fluid output slot for NEI and machine GUI * Fix fluid fuels not having fuel value in large boilers * Remove GT_RectHandler and NEI_TransferRectHost * Remove RecipeMapHandler * Move NEI energy description from RecipeMapFrontend to Power * Refactor the way to filter fusion recipes * Check restriction for some properties * Remove showVoltageAmperage * Make Power accept GT_Recipe * Fix NPE * Move NEI duration description to Power from Frontend * Directly implement IRecipeProcessingAwareHatch for GT_MetaTileEntity_Hatch_InputBus_ME * Make Power integrated with GT_OverclockCalculator * Rename Power -> OverclockDescriber * Don't modify recipe find logic until postload finishes * Reformat reserved MTE ids * Fix check for too few inputs on recipe addition * Move replicator logic to backend * Stop un-hiding assline recipes * Allow setting custom recipe comparator & implement for fusion * Update AE2FC * Rename getRecipeList and getRecipes -> getRecipeMap * Automatically register recipe catalysts * Cleanup the way to detect recipe collision * Make use of BasicUIProperties for basic machines * Make use of BasicUIProperties for UIHelper * Rename specialHandler -> recipeTransformer * Add way to automatically register handler info * Add recipe category * Add some APIs for addons * Rename blastRecipes -> blastFurnaceRecipes * Remove GT_MetaTileEntity_BasicMachine_GT_Recipe#mSharedTank and #mRequiresFluidForFiltering * Don't require setting duration and EU/t for fuel recipes * Don't require setting EU/t for primitive blast furnace recipes * Revert change to addMultiblockChemicalRecipe * Fix large boiler general desc recipe not being added * Hide duration and EU/t from large boiler * Cleanup recipe stacktrace draw * Extend metadata usage of recipe builder to recipe itself * Implement metadata handling & NEI comparator for PCB factory * Some rename around NEIRecipeInfo * Some toString implementations * Add more APIs for addons & some rename * Infer handler icon from recipe catalyst if one is not set * Also shrink recipe title when OC is not used * Remove rare earth centrifuge recipe * Use metadata for replicator backend * Adjust geothermal generator output slot * Allow having multiple transferrects * Store recipemap reference in backend * Rename vacuumRecipes -> vacuumFreezerRecipes * Add config to tweak visibility of recipe categories * Remove mHideRecyclingRecipes in favor of recipe category config * Fix typo fluidSolidfierRecipes -> fluidSolidifierRecipes * Refactor findRecipe and ProcessingLogic to use Stream * Fix BBF handler icon & remove bronze blast furnace * Add fluent API for findRecipe * Add way to stop adding progressbar * Change arg order for special texture * Avoid overwriting interesting failure with NO_RECIPE * Some changes for FuelBackend * Set space project icon * Remove localization from TT * Remove CNC recipe adder * Move recipe extractor from AE2FC * Minor internal change for ProcessingLogic#applyRecipe * More javadoc on #getAvailableRecipeMaps * Better implementation of #ofSupplier * Move replicator exponent config to GT_Proxy * Remove RC & IC2 macerator handling * Rename StreamUtil -> GT_StreamUtil * Refactor code around RecipeMetadataStorage * Revise #compileRecipe javadoc * Switch extreme diesel recipe loader to downstream recipe map * Optimize #reMap * Rename reload -> reloadNEICache * Minor tweak for drawEnergyInfo * a bit more doc * Adjust recipe catalysts * Add toString implementation for GT_Fluid for debug * Minor revision for OilCrackerBackend * Index replicator recipes by material --------- Co-authored-by: Glease <4586901+Glease@users.noreply.github.com> --- .../gregtech/api/recipe/BasicUIProperties.java | 251 +++++ .../api/recipe/BasicUIPropertiesBuilder.java | 264 +++++ .../java/gregtech/api/recipe/FindRecipeQuery.java | 178 +++ .../gregtech/api/recipe/NEIRecipeProperties.java | 89 ++ .../api/recipe/NEIRecipePropertiesBuilder.java | 100 ++ .../java/gregtech/api/recipe/RecipeCategories.java | 56 + .../java/gregtech/api/recipe/RecipeCategory.java | 81 ++ .../gregtech/api/recipe/RecipeCategoryHolder.java | 13 + .../gregtech/api/recipe/RecipeCategorySetting.java | 52 + src/main/java/gregtech/api/recipe/RecipeMap.java | 395 +++++++ .../java/gregtech/api/recipe/RecipeMapBackend.java | 461 ++++++++ .../api/recipe/RecipeMapBackendProperties.java | 77 ++ .../recipe/RecipeMapBackendPropertiesBuilder.java | 119 ++ .../java/gregtech/api/recipe/RecipeMapBuilder.java | 522 +++++++++ .../gregtech/api/recipe/RecipeMapFrontend.java | 395 +++++++ src/main/java/gregtech/api/recipe/RecipeMaps.java | 1177 ++++++++++++++++++++ .../gregtech/api/recipe/RecipeMetadataKey.java | 84 ++ .../api/recipe/check/FindRecipeResult.java | 125 --- .../gregtech/api/recipe/check/RecipeValidator.java | 80 -- .../api/recipe/check/SingleRecipeCheck.java | 25 +- .../gregtech/api/recipe/maps/AssemblerBackend.java | 35 + .../api/recipe/maps/AssemblyLineFrontend.java | 76 ++ .../api/recipe/maps/DistillationTowerFrontend.java | 38 + .../api/recipe/maps/FluidCannerBackend.java | 73 ++ .../api/recipe/maps/FluidOnlyFrontend.java | 33 + .../api/recipe/maps/FormingPressBackend.java | 92 ++ .../java/gregtech/api/recipe/maps/FuelBackend.java | 75 ++ .../gregtech/api/recipe/maps/FurnaceBackend.java | 52 + .../api/recipe/maps/LargeBoilerFuelBackend.java | 132 +++ .../api/recipe/maps/LargeBoilerFuelFrontend.java | 25 + .../gregtech/api/recipe/maps/LargeNEIFrontend.java | 65 ++ .../gregtech/api/recipe/maps/MicrowaveBackend.java | 145 +++ .../gregtech/api/recipe/maps/NonGTBackend.java | 52 + .../api/recipe/maps/OilCrackerBackend.java | 41 + .../gregtech/api/recipe/maps/PrinterBackend.java | 142 +++ .../gregtech/api/recipe/maps/RecyclerBackend.java | 51 + .../api/recipe/maps/ReplicatorBackend.java | 100 ++ .../api/recipe/maps/SpaceProjectFrontend.java | 131 +++ .../maps/TranscendentPlasmaMixerFrontend.java | 56 + .../api/recipe/maps/UnpackagerBackend.java | 53 + .../metadata/EmptyRecipeMetadataStorage.java | 50 + .../recipe/metadata/IRecipeMetadataStorage.java | 34 + .../api/recipe/metadata/PCBFactoryTierKey.java | 30 + .../api/recipe/metadata/PCBFactoryUpgrade.java | 7 + .../api/recipe/metadata/PCBFactoryUpgradeKey.java | 32 + .../api/recipe/metadata/RecipeMetadataStorage.java | 56 + .../recipe/metadata/SimpleRecipeMetadataKey.java | 27 + 47 files changed, 6030 insertions(+), 217 deletions(-) create mode 100644 src/main/java/gregtech/api/recipe/BasicUIProperties.java create mode 100644 src/main/java/gregtech/api/recipe/BasicUIPropertiesBuilder.java create mode 100644 src/main/java/gregtech/api/recipe/FindRecipeQuery.java create mode 100644 src/main/java/gregtech/api/recipe/NEIRecipeProperties.java create mode 100644 src/main/java/gregtech/api/recipe/NEIRecipePropertiesBuilder.java create mode 100644 src/main/java/gregtech/api/recipe/RecipeCategories.java create mode 100644 src/main/java/gregtech/api/recipe/RecipeCategory.java create mode 100644 src/main/java/gregtech/api/recipe/RecipeCategoryHolder.java create mode 100644 src/main/java/gregtech/api/recipe/RecipeCategorySetting.java create mode 100644 src/main/java/gregtech/api/recipe/RecipeMap.java create mode 100644 src/main/java/gregtech/api/recipe/RecipeMapBackend.java create mode 100644 src/main/java/gregtech/api/recipe/RecipeMapBackendProperties.java create mode 100644 src/main/java/gregtech/api/recipe/RecipeMapBackendPropertiesBuilder.java create mode 100644 src/main/java/gregtech/api/recipe/RecipeMapBuilder.java create mode 100644 src/main/java/gregtech/api/recipe/RecipeMapFrontend.java create mode 100644 src/main/java/gregtech/api/recipe/RecipeMaps.java create mode 100644 src/main/java/gregtech/api/recipe/RecipeMetadataKey.java delete mode 100644 src/main/java/gregtech/api/recipe/check/FindRecipeResult.java delete mode 100644 src/main/java/gregtech/api/recipe/check/RecipeValidator.java create mode 100644 src/main/java/gregtech/api/recipe/maps/AssemblerBackend.java create mode 100644 src/main/java/gregtech/api/recipe/maps/AssemblyLineFrontend.java create mode 100644 src/main/java/gregtech/api/recipe/maps/DistillationTowerFrontend.java create mode 100644 src/main/java/gregtech/api/recipe/maps/FluidCannerBackend.java create mode 100644 src/main/java/gregtech/api/recipe/maps/FluidOnlyFrontend.java create mode 100644 src/main/java/gregtech/api/recipe/maps/FormingPressBackend.java create mode 100644 src/main/java/gregtech/api/recipe/maps/FuelBackend.java create mode 100644 src/main/java/gregtech/api/recipe/maps/FurnaceBackend.java create mode 100644 src/main/java/gregtech/api/recipe/maps/LargeBoilerFuelBackend.java create mode 100644 src/main/java/gregtech/api/recipe/maps/LargeBoilerFuelFrontend.java create mode 100644 src/main/java/gregtech/api/recipe/maps/LargeNEIFrontend.java create mode 100644 src/main/java/gregtech/api/recipe/maps/MicrowaveBackend.java create mode 100644 src/main/java/gregtech/api/recipe/maps/NonGTBackend.java create mode 100644 src/main/java/gregtech/api/recipe/maps/OilCrackerBackend.java create mode 100644 src/main/java/gregtech/api/recipe/maps/PrinterBackend.java create mode 100644 src/main/java/gregtech/api/recipe/maps/RecyclerBackend.java create mode 100644 src/main/java/gregtech/api/recipe/maps/ReplicatorBackend.java create mode 100644 src/main/java/gregtech/api/recipe/maps/SpaceProjectFrontend.java create mode 100644 src/main/java/gregtech/api/recipe/maps/TranscendentPlasmaMixerFrontend.java create mode 100644 src/main/java/gregtech/api/recipe/maps/UnpackagerBackend.java create mode 100644 src/main/java/gregtech/api/recipe/metadata/EmptyRecipeMetadataStorage.java create mode 100644 src/main/java/gregtech/api/recipe/metadata/IRecipeMetadataStorage.java create mode 100644 src/main/java/gregtech/api/recipe/metadata/PCBFactoryTierKey.java create mode 100644 src/main/java/gregtech/api/recipe/metadata/PCBFactoryUpgrade.java create mode 100644 src/main/java/gregtech/api/recipe/metadata/PCBFactoryUpgradeKey.java create mode 100644 src/main/java/gregtech/api/recipe/metadata/RecipeMetadataStorage.java create mode 100644 src/main/java/gregtech/api/recipe/metadata/SimpleRecipeMetadataKey.java (limited to 'src/main/java/gregtech/api/recipe') 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}. + *

+ * 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 slotOverlays; + private final SlotOverlayGetter 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 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>> specialTextures; + /** + * Additional textures shown on steam machine GUI. + */ + public final List>> 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> itemInputPositionsGetter; + public final IntFunction> itemOutputPositionsGetter; + public final Supplier specialItemPositionGetter; + public final IntFunction> fluidInputPositionsGetter; + public final IntFunction> 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 slotOverlays, SlotOverlayGetter slotOverlaysSteam, + @Nullable FallbackableUITexture progressBarTexture, @Nullable FallbackableSteamTexture progressBarTextureSteam, + ProgressBar.Direction progressBarDirection, Size progressBarSize, Pos2d progressBarPos, boolean useProgressBar, + boolean useSpecialSlot, List neiTransferRect, @Nullable String neiTransferRectId, + List>> specialTextures, + List>> specialTexturesSteam, IDrawable logo, Size logoSize, Pos2d logoPos, + IntFunction> itemInputPositionsGetter, IntFunction> itemOutputPositionsGetter, + Supplier specialItemPositionGetter, IntFunction> fluidInputPositionsGetter, + IntFunction> 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 { + + @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 slotOverlays = (index, isFluid, isOutput, isSpecial) -> null; + private BasicUIProperties.SlotOverlayGetter 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 neiTransferRect = ImmutableList.builder(); + @Nullable + private String neiTransferRectId; + + private final ImmutableList.Builder>> specialTextures = ImmutableList.builder(); + private final ImmutableList.Builder>> 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> itemInputPositionsGetter = UIHelper::getItemInputPositions; + private IntFunction> itemOutputPositionsGetter = UIHelper::getItemOutputPositions; + private Supplier specialItemPositionGetter = UIHelper::getSpecialItemPosition; + private IntFunction> fluidInputPositionsGetter = UIHelper::getFluidInputPositions; + private IntFunction> 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 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 slotOverlays) { + this.slotOverlays = slotOverlays; + return this; + } + + public BasicUIPropertiesBuilder slotOverlaysSteam( + BasicUIProperties.SlotOverlayGetter 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 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>> 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>> 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> itemInputPositionsGetter) { + this.itemInputPositionsGetter = itemInputPositionsGetter; + return this; + } + + public BasicUIPropertiesBuilder itemOutputPositionsGetter(IntFunction> itemOutputPositionsGetter) { + this.itemOutputPositionsGetter = itemOutputPositionsGetter; + return this; + } + + public BasicUIPropertiesBuilder specialItemPositionGetter(Supplier specialItemPositionGetter) { + this.specialItemPositionGetter = specialItemPositionGetter; + return this; + } + + public BasicUIPropertiesBuilder fluidInputPositionsGetter(IntFunction> fluidInputPositionsGetter) { + this.fluidInputPositionsGetter = fluidInputPositionsGetter; + return this; + } + + public BasicUIPropertiesBuilder fluidOutputPositionsGetter(IntFunction> 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}. + *

+ * It features fluent API, so you can find recipes like this: + * + *

+ * {@code
+ *     GT_Recipe recipe = recipeMap.findRecipeQuery()
+ *         .items(inputItems)
+ *         .fluids(inputFluids)
+ *         .find();
+ * }
+ * 
+ */ +// spotless:on +@SuppressWarnings("unused") +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +public final class FindRecipeQuery { + + private static final Predicate ALWAYS = r -> true; + + private final RecipeMap recipeMap; + + @Nullable + private ItemStack[] items; + @Nullable + private FluidStack[] fluids; + @Nullable + private ItemStack specialSlot; + private Predicate 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 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 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. + *

+ * 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 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 comparator; + + NEIRecipeProperties(boolean registerNEI, @Nullable UnaryOperator handlerInfoCreator, + Size recipeBackgroundSize, Pos2d recipeBackgroundOffset, INEISpecialInfoFormatter neiSpecialInfoFormatter, + boolean unificateOutput, boolean useCustomFilter, boolean renderRealStackSizes, + Comparator 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 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 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 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 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..c2de890793 --- /dev/null +++ b/src/main/java/gregtech/api/recipe/RecipeCategories.java @@ -0,0 +1,56 @@ +package gregtech.api.recipe; + +import static gregtech.api.enums.Mods.GregTech; +import static gregtech.api.recipe.RecipeCategory.createIcon; + +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")))); +} 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..d7729ac493 --- /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; + +/** + * Used to display recipes on NEI on different tabs. + *

+ * 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 ALL_RECIPE_CATEGORIES = new HashMap<>(); + + public final String unlocalizedName; + public final RecipeMap recipeMap; + public final ModContainer ownerMod; + @Nullable + public final UnaryOperator 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 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() + + '}'; + } + + /** + * Creates 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 Type of {@link RecipeMapBackend} + */ +@SuppressWarnings({ "unused", "UnusedReturnValue" }) +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +@FieldsAreNonnullByDefault +public final class RecipeMap implements IRecipeMap { + + /** + * All the recipemap instances. key=unlocalized name, value=instance. + */ + public static final Map> 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 getAllRecipes() { + return backend.getAllRecipes(); + } + + /** + * @return List of registered recipe categories associated with this recipemap. + */ + public List 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 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, @N