diff --git a/src/main/java/com/gtnewhorizon/cropsnh/CropsNH.java b/src/main/java/com/gtnewhorizon/cropsnh/CropsNH.java index b2060d95..58ad3051 100644 --- a/src/main/java/com/gtnewhorizon/cropsnh/CropsNH.java +++ b/src/main/java/com/gtnewhorizon/cropsnh/CropsNH.java @@ -8,6 +8,7 @@ import com.gtnewhorizon.cropsnh.compatibility.mclib.MCLibCompatHandler; import com.gtnewhorizon.cropsnh.compatibility.waila.WailaRegistry; import com.gtnewhorizon.cropsnh.farming.registries.MutationRegistry; +import com.gtnewhorizon.cropsnh.farming.requirements.SubSoilRequirement; import com.gtnewhorizon.cropsnh.handler.ConfigurationHandler; import com.gtnewhorizon.cropsnh.handler.CropsNHFurnaceFuelHandler; import com.gtnewhorizon.cropsnh.handler.MigrationHandler; @@ -22,7 +23,7 @@ import com.gtnewhorizon.cropsnh.loaders.MutationLoader; import com.gtnewhorizon.cropsnh.loaders.OreDictLoader; import com.gtnewhorizon.cropsnh.loaders.SoilLoader; -import com.gtnewhorizon.cropsnh.loaders.SubSoilRequirementLoader; +import com.gtnewhorizon.cropsnh.loaders.SubSoilLoader; import com.gtnewhorizon.cropsnh.proxy.IProxy; import com.gtnewhorizon.cropsnh.reference.Constants; import com.gtnewhorizon.cropsnh.reference.Reference; @@ -121,11 +122,11 @@ public static void postInit(FMLPostInitializationEvent event) { LogHelper.debug("Starting Post-Initialization"); FertilizerLoader.postInit(); SoilLoader.postInit(); - SubSoilRequirementLoader.postInit(); + SubSoilLoader.postInit(); CropLoader.postInit(); MutationLoader.postInit(); AspectLoader.postInit(); - GTRecipeLoader.PostInit(); + GTRecipeLoader.postInit(); MigrationHandler.postInit(); ForestryCompatHandler.onPostInit(); ExUWateringCanHandler.postInit(); @@ -138,8 +139,10 @@ public static void postInit(FMLPostInitializationEvent event) { @SuppressWarnings("unused") public void onLoadComplete(FMLLoadCompleteEvent event) { LogHelper.debug("Starting Load-Complete"); + SubSoilRequirement.onLoadComplete(); CropLoader.loadComplete(); MigrationHandler.loadComplete(); + GTRecipeLoader.loadComplete(); LogHelper.debug("Load-Complete Complete"); } diff --git a/src/main/java/com/gtnewhorizon/cropsnh/compatibility/NEI/NEICropsNHCropHandler.java b/src/main/java/com/gtnewhorizon/cropsnh/compatibility/NEI/NEICropsNHCropHandler.java index 61960805..00c3dca4 100644 --- a/src/main/java/com/gtnewhorizon/cropsnh/compatibility/NEI/NEICropsNHCropHandler.java +++ b/src/main/java/com/gtnewhorizon/cropsnh/compatibility/NEI/NEICropsNHCropHandler.java @@ -238,7 +238,7 @@ protected void loadUsageRecipesDo(ItemStack item) { // register mutations for which this is a sub-soil. for (IGrowthRequirement req : cropCard.getGrowthRequirements()) { if (!(req instanceof SubSoilRequirement subSoilRequirement)) continue; - if (subSoilRequirement.canGrow(block, CropsNHUtils.getItemMeta(item), null)) { + if (subSoilRequirement.isValidSubSoil(block, CropsNHUtils.getItemMeta(item), null, true)) { arecipes.add(new CachedCropRecipe(null, cropCard)); // crops shouldn't be getting registered more than once continue outer; diff --git a/src/main/java/com/gtnewhorizon/cropsnh/compatibility/NEI/NEICropsNHCropstickBreedingHandler.java b/src/main/java/com/gtnewhorizon/cropsnh/compatibility/NEI/NEICropsNHCropstickBreedingHandler.java index e30c2734..d7995ca3 100644 --- a/src/main/java/com/gtnewhorizon/cropsnh/compatibility/NEI/NEICropsNHCropstickBreedingHandler.java +++ b/src/main/java/com/gtnewhorizon/cropsnh/compatibility/NEI/NEICropsNHCropstickBreedingHandler.java @@ -221,7 +221,7 @@ protected void loadUsageRecipesDo(ItemStack item) { for (IBreedingRequirement req : mutation.getRequirements()) { if (!(req instanceof SubSoilRequirement)) continue; // The canBreed methods are more for machines and dim checks, canGrow fits our use-case better here. - if (((SubSoilRequirement) req).canGrow(block, CropsNHUtils.getItemMeta(item), null)) { + if (((SubSoilRequirement) req).isValidSubSoil(block, CropsNHUtils.getItemMeta(item), null, true)) { arecipes.add(new CachedBreedingRecipe(mutation)); // mutations shouldn't be getting registered more than once continue outer; diff --git a/src/main/java/com/gtnewhorizon/cropsnh/farming/requirements/SubSoilRequirement.java b/src/main/java/com/gtnewhorizon/cropsnh/farming/requirements/SubSoilRequirement.java index 1f528702..7db8a859 100644 --- a/src/main/java/com/gtnewhorizon/cropsnh/farming/requirements/SubSoilRequirement.java +++ b/src/main/java/com/gtnewhorizon/cropsnh/farming/requirements/SubSoilRequirement.java @@ -5,6 +5,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Set; +import java.util.function.Predicate; import java.util.stream.Collectors; import net.minecraft.block.Block; @@ -31,6 +32,7 @@ import com.gtnewhorizon.cropsnh.api.IWorldGrowthRequirement; import com.gtnewhorizon.cropsnh.reference.Reference; import com.gtnewhorizon.cropsnh.utility.CropsNHUtils; +import com.gtnewhorizon.cropsnh.utility.LogHelper; import com.gtnewhorizon.cropsnh.utility.MetaSet; import gregtech.api.GregTechAPI; @@ -51,8 +53,26 @@ public class SubSoilRequirement implements IWorldGrowthRequirement, IWorldBreedingRequirement, IMachineBreedingRequirement, IMachineGrowthRequirement { + // region registry + private static final Object2ObjectOpenHashMap registrations = new Object2ObjectOpenHashMap<>(); + /** + * Should only be called internally, do not use if you aren't part of crops nh or did a dumb move and added crops + * after the post-load event. + */ + public static void onLoadComplete() { + registrations.values() + .parallelStream() + .forEach(SubSoilRequirement::buildPredicate); + } + + /** + * Gets a sub-soil requirement type, or creates it if it doesn't exist. + * + * @param name The unique identifier for the sub-soil type. + * @return The sub-soil type to use. + */ public static SubSoilRequirement get(String name) { if (!registrations.containsKey(name)) { registrations.put(name, new SubSoilRequirement(name)); @@ -60,118 +80,305 @@ public static SubSoilRequirement get(String name) { return registrations.get(name); } + // endregion registry + + /** The individual key used to identify this sub-soil type. Also used to generate lang keys. */ private final String materialDescription; - private final Set materials = new ObjectOpenHashSet<>(); - private final Set oreDictionaries = new ObjectOpenHashSet<>(); - private final MetaSet blocks = new MetaSet<>(); + /** key value pair used to generate localized display strings. */ private final Pair unlocalizedDesc; + /** The list of whitelisted gt materials */ + private @Nullable Set materialWhitelist = null; + /** The list of whitelisted ore dictionaries */ + private @Nullable Set oreDictWhitelist = null; + /** The list of whitelisted blocks */ + private @Nullable MetaSet blockWhitelist = null; + /** The list of blacklisted blocks */ + private @Nullable MetaSet blockBlacklist = null; + /** Custom predicates in case the provided ones aren't good enough, always appended as an OR before the ANDs. */ + private @Nullable Predicate customOrPredicates = null; + /** Custom predicates in case the provided ones aren't good enough, always appended as an AND after the ORs. */ + private @Nullable Predicate customAndPredicates = null; + /** Used to contain runtime checks, if null, it means no blocks were assigned to this group (eg mising mods) */ + private @Nullable Predicate predicate = null; + + /** + * @param materialDescription The individual key used to identify this sub-soil type. Also used to generate lang + * keys. + */ private SubSoilRequirement(String materialDescription) { this.materialDescription = materialDescription; this.unlocalizedDesc = Pair.of(Reference.MOD_ID + "_growthReq.subSoil." + this.materialDescription, null); } + /** + * Builds an optimized predicate to use at runtime for checks. + */ + private void buildPredicate() { + Predicate newPredicate = null; + if (this.blockWhitelist != null) { + this.predicate = this::isWhitelistedBlock; + } + if (this.oreDictWhitelist != null) { + this.predicate = predicate == null ? this::isWhitelistedOreDict : predicate.or(this::isWhitelistedOreDict); + } + if (this.materialWhitelist != null) { + this.predicate = predicate == null ? this::isWhitelistedMaterial + : predicate.or(this::isWhitelistedMaterial); + } + if (this.customOrPredicates != null) { + this.predicate = this.predicate == null ? this.customOrPredicates + : this.predicate.or(this.customOrPredicates); + } + if (this.predicate != null && this.blockBlacklist != null) { + this.predicate = predicate.and(this::isNotBlacklistedBlock); + } + if (this.customAndPredicates != null) { + this.predicate = this.predicate == null ? this.customAndPredicates + : this.predicate.and(this.customAndPredicates); + } + } + + // region builder api + + /** + * Whitelists a set of GT Material. + * + * @param args The materials to allow. + * @return The calling object, this is a builder pattern. + */ public SubSoilRequirement addMaterial(Materials... args) { - this.materials.addAll(Arrays.asList(args)); + // ignore silently if nothing is provided + if (args.length <= 0) return this; + // check for null args + for (int i = 0; i < args.length; i++) { + // should never be null + if (args[i] == null) { + throw new IllegalStateException( + "Attemted to whitelist a null gt material to " + this.materialDescription + " @ " + i); + } + } + // initialize if needed + if (this.materialWhitelist == null) { + this.materialWhitelist = new ObjectOpenHashSet<>(); + } + // add values + this.materialWhitelist.addAll(Arrays.asList(args)); return this; } + /** + * Whitelists a set of ore dictionaries. + * + * @param args The ore dictionaries to allow. + * @return The calling object, this is a builder pattern. + */ public SubSoilRequirement addOreDict(String... args) { - this.oreDictionaries.addAll(Arrays.asList(args)); + // ignore silently if nothing is provided + if (args.length <= 0) return this; + // check for null args + for (int i = 0; i < args.length; i++) { + // should never be null + if (args[i] == null) { + throw new IllegalStateException( + "Attemted to whitelist a null ore dictl to " + this.materialDescription + " @ " + i); + } + } + // initialize if needed + if (this.oreDictWhitelist == null) { + this.oreDictWhitelist = new ObjectOpenHashSet<>(); + } + // add values + this.oreDictWhitelist.addAll(Arrays.asList(args)); return this; } + /** + * Whitelists the block and ore dictionaries associated with the requirement's key, eg iron adds {@code blockIron} + * and {@code oreIron} + * + * @return The calling object, this is a builder pattern. + */ public SubSoilRequirement addBlockAndOreDict() { - // just capitalize the mat description - return this - .addOreDict(Character.toUpperCase(this.materialDescription.charAt(0)) + materialDescription.substring(1)); + // needs to be capitalized, assume is camel or pascal cased by default + this.addBlockAndOreDict( + Character.toUpperCase(this.materialDescription.charAt(0)) + materialDescription.substring(1)); + return this; } + /** + * Whitelists the block and ore dictionaries associated with the given names, eg iron adds {@code blockIron} and + * {@code oreIron}. + * + * @param names all values should be in {@code PascalCase}. + * + * @return The calling object, this is a builder pattern. + */ public SubSoilRequirement addBlockAndOreDict(String... names) { - for (String name : names) { + // check for nulls + for (int i = 0; i < names.length; i++) { + String name = names[i]; + // should never be null + if (name == null) { + throw new IllegalStateException( + "Attempted to whitelist null block as a sub-soil for " + this.materialDescription + " @ " + i); + } + // add values this.addOreDict("block" + name, "ore" + name); } return this; } + /** + * Whitelists a set of blocks. + * + * @param args The blocks to allow, use {@link OreDictionary#WILDCARD_VALUE} to whitelist all metas. + * @return The calling object, this is a builder pattern. + */ public SubSoilRequirement addBlock(BlockWithMeta... args) { + // ignore silently if nothing is passed + if (args.length <= 0) return this; + // check for nulls + for (int i = 0; i < args.length; i++) { + if (args[i] == null) { + if (CropsNHUtils.shouldPanicIfNullFound()) { + throw new IllegalStateException( + "Attempted to whitelist null block as a sub-soil for " + this.materialDescription + " @ " + i); + } else { + try { + throw new Exception( + "CROPS NH ATTEMPTED TO WHITELIST NULL BLOCK AS A SUB-SOIL FOR " + this.materialDescription + + " @ " + + i); + } catch (Exception e) { + LogHelper.warn(e.getMessage()); + e.printStackTrace(); + } + } + } + } + // initialize block whitelist if not set + if (this.blockWhitelist == null) { + this.blockWhitelist = new MetaSet<>(); + } + // whitelist blocks for (BlockWithMeta arg : args) { - this.blocks.add(arg.getBlock(), arg.ignoreMeta() ? OreDictionary.WILDCARD_VALUE : arg.getMeta()); + if (arg == null) continue; + this.blockWhitelist.add(arg.getBlock(), arg.ignoreMeta() ? OreDictionary.WILDCARD_VALUE : arg.getMeta()); } return this; } - @Override - public @NotNull String getDescription() { - return StatCollector - .translateToLocalFormatted(this.unlocalizedDesc.getLeft(), (Object[]) unlocalizedDesc.getRight()); + /** + * Blacklists a set of blocks. + * + * @param args The blocks to allow, use {@link OreDictionary#WILDCARD_VALUE} to blacklist all metas. + * @return The calling object, this is a builder pattern. + */ + public SubSoilRequirement blacklistBlock(BlockWithMeta... args) { + // ignore silently if nothing is passed + if (args.length <= 0) return this; + // check for nulls + for (int i = 0; i < args.length; i++) { + if (args[i] == null) { + if (CropsNHUtils.shouldPanicIfNullFound()) { + throw new IllegalStateException( + "Attempted to blacklist null block as a sub-soil for " + this.materialDescription + " @ " + i); + } else { + try { + throw new Exception( + "CROPS NH ATTEMPTED TO BLACKLIST NULL BLOCK AS A SUB-SOIL FOR " + this.materialDescription + + " @ " + + i); + } catch (Exception e) { + LogHelper.warn(e.getMessage()); + e.printStackTrace(); + } + } + } + } + // initialize block whitelist if not set + if (this.blockBlacklist == null) { + this.blockBlacklist = new MetaSet<>(); + } + // whitelist blocks + for (BlockWithMeta arg : args) { + if (arg == null) continue; + this.blockBlacklist.add(arg.getBlock(), arg.ignoreMeta() ? OreDictionary.WILDCARD_VALUE : arg.getMeta()); + } + return this; + } + + /** + * Adds a custom predicate that will be ORed with all the other whitelist values for this requirement. + * + * @param arg The OR predicate to add to this requirement. + * @return The calling object, this is a builder pattern. + */ + public SubSoilRequirement addOrPredicate(Predicate arg) { + if (arg == null) { + throw new IllegalStateException("Attempted to add a null OR predicate for " + this.materialDescription); + } + this.customOrPredicates = this.customOrPredicates == null ? arg : this.customOrPredicates.or(arg); + return this; + } + + /** + * Adds a custom predicate that will be ANDed on top of all existing requirements. + * If called multiple times, the existing AND predicated will be ANDed with the provided one. + * + * @param arg The AND predicate to add to this requirement. + * @return The calling object, this is a builder pattern. + */ + public SubSoilRequirement addAndPredicate(Predicate arg) { + if (arg == null) { + throw new IllegalStateException("Attempted to add a null AND predicate for " + this.materialDescription); + } + this.customAndPredicates = this.customAndPredicates == null ? arg : this.customAndPredicates.and(arg); + return this; } + // endregion builder api + + // region check handlers + @Override - public @NotNull Pair<@NotNull String, @Nullable String[]> getUnlocalizedDescription() { - return this.unlocalizedDesc; + public boolean onlyPreventsHarvest() { + return false; } @Override public boolean canGrow(World world, ICropStickTile tile, int x, int y, int z) { + if (this.predicate == null) return false; // pre-flight checks SubSoilTarget target = getSubSoil(world, x, y, z); if (target == null) return false; - return canGrow(target.block, target.meta, target.te); - } - - @Override - public boolean onlyPreventsHarvest() { - return false; + return predicate.test(target); } @Override public boolean canBreed(ArrayList parents, World world, ICropStickTile tile, int x, int y, int z) { + if (this.predicate == null) return false; return canGrow(world, tile, x, y, z); } - public static SubSoilTarget getSubSoil(IBlockAccess world, int x, int y, int z) { - // pre-flight check - y -= 2; - if (y < 0) return null; - if (world.isAirBlock(x, y, z)) return null; - - // get the sub-soil - Block block = world.getBlock(x, y, z); - if (block.getMaterial() == Material.air) return null; - int meta = world.getBlockMetadata(x, y, z); - TileEntity te = world.getTileEntity(x, y, z); - return new SubSoilTarget(block, meta, te); - } - @Override public boolean canGrow(ISeedData seedData, IGregTechTileEntity te, ItemStack[] catalysts) { + if (this.predicate == null) return false; return Arrays.stream(catalysts) - .anyMatch(this::isValidSubSoil); - } - - public static class SubSoilTarget { - - public final Block block; - public final int meta; - public final TileEntity te; - - public SubSoilTarget(Block block, int meta, TileEntity te) { - this.block = block; - this.meta = meta; - this.te = te; - } + .anyMatch(x -> this.isValidSubSoil(x, false)); } @Override public boolean canBreed(ArrayList parents, IGregTechTileEntity te, ItemStack @NotNull [] catalysts, int[] consumptionTracker) { + if (this.predicate == null) return false; for (int i = 0; i < catalysts.length; i++) { // if stack is bad or if we can't consume it, abort early ItemStack stack = catalysts[i]; if (GTUtility.isStackInvalid(stack) || stack.stackSize - consumptionTracker[i] <= 0) continue; // consume if valid - if (isValidSubSoil(stack)) { + if (isValidSubSoil(stack, false)) { consumptionTracker[i] += 1; return true; } @@ -179,92 +386,51 @@ public boolean canBreed(ArrayList parents, IGregTechTileEntity te, It return false; } - public @Nullable ItemStack findIndustrialFarmInsertionCatalyst(@NotNull List catalysts) { - for (ItemStack stack : catalysts) { - // if stack is bad or if we can't consume it, abort early - if (GTUtility.isStackInvalid(stack) || stack.stackSize <= 0) continue; - // else check if it's a valid sub-soil - if (isValidSubSoil(stack)) { - return stack; - } - } - return null; + /** + * Checks if a block is a valid sub-soil for this requirement. + * + * @param block The sub-soil block. + * @param meta The meta of the sub-soil block. + * @param te The TE at the position of the block. + * @param isNEI True if the request originated from NEI. + * @return true if it can grow. + */ + public boolean isValidSubSoil(Block block, int meta, TileEntity te, boolean isNEI) { + return predicate != null && predicate.test(new SubSoilTarget(block, meta, te, isNEI)); } /** - * @param toValidate The stack to validate + * Checks if a stack contains a valid sub-soil for this requirement. + * + * @param toValidate The stack to validate. + * @param isNEI True if the request originated from NEI. * @return True if the stack contains a valid sub-soil. */ - public boolean isValidSubSoil(ItemStack toValidate) { + public boolean isValidSubSoil(ItemStack toValidate, boolean isNEI) { + if (this.predicate == null) return false; // ensure valid stack if (CropsNHUtils.isStackInvalid(toValidate)) return false; - // GT Material check - for (Materials material : this.materials) { - if (checkGTBlockOrOreMaterial(toValidate, material)) { - return true; - } - } - - // Ore dict check - for (String oreDict : this.oreDictionaries) { - if (checkOreDict(toValidate, oreDict)) { - return true; - } - } + SubSoilTarget query = new SubSoilTarget(toValidate, isNEI); + // air as a stack will never be a valid block + if (query.block != null && query.block.getMaterial() == Material.air) return false; - // Block conversion - Block block = CropsNHUtils.getBlockFromItem(toValidate); - return block.getMaterial() != Material.air && blocks.contains(block, CropsNHUtils.getItemMeta(toValidate)); + return predicate.test(query); } - public boolean canGrow(Block block, int meta, TileEntity te) { - // non-world dependent check for the GoBlyn - ItemStack stack = new ItemStack(Item.getItemFromBlock(block), 1, meta); + // endregion check handlers - // gt material check - for (Materials material : this.materials) { - if (block instanceof GTBlockOre && te instanceof TileEntityOres) { - Materials generatedMaterial = GregTechAPI.sGeneratedMaterials[((TileEntityOres) te).mMetaData % 1000]; - if (generatedMaterial != null && generatedMaterial != Materials._NULL - && generatedMaterial == material) { - return true; - } - } else if (checkGTBlockOrOreMaterial(stack, material)) { - return true; - } - } + // region NEI and localisations - // ore dict checks - for (String oreDict : this.oreDictionaries) { - if (checkOreDict(stack, oreDict)) return true; - } - - // direct block access - return blocks.contains(block, meta); - } - - private boolean checkGTBlockOrOreMaterial(ItemStack stack, Materials toMatch) { - ItemData association = GTOreDictUnificator.getAssociation(stack); - // spotless:off - return association != null - && (association.mPrefix.toString().startsWith("ore") || association.mPrefix == OrePrefixes.block) - && (association.mMaterial.mMaterial == toMatch); - //spotless:on + @Override + public @NotNull String getDescription() { + return StatCollector + .translateToLocalFormatted(this.unlocalizedDesc.getLeft(), (Object[]) unlocalizedDesc.getRight()); } - private boolean checkOreDict(ItemStack stack, String oreDict) { - // ore dictionary - for (int aux = 0; aux < OreDictionary.getOres(oreDict) - .size(); ++aux) { - ItemStack oreDictStack = OreDictionary.getOres(oreDict) - .get(aux); - if (oreDictStack.getItem() != stack.getItem()) continue; - int dmg = CropsNHUtils.getItemMeta(oreDictStack); - // check for both the old and the new wildcard value, this helps with a couple things - if (CropsNHUtils.getItemMeta(stack) == dmg || OreDictionary.WILDCARD_VALUE == dmg || -1 == dmg) return true; - } - return false; + @Override + public @NotNull Pair<@NotNull String, @Nullable String[]> getUnlocalizedDescription() { + return this.unlocalizedDesc; } @Override @@ -272,38 +438,200 @@ private boolean checkOreDict(ItemStack stack, String oreDict) { return getItemsForNEI(); } + /** + * @return A list of all block and items that are valid sub-soils. + */ public List getItemsForNEI() { List ret = new LinkedList<>(); // load up direct block mentions - for (MetaSet.Entry e : this.blocks.getStream() - .collect(Collectors.toList())) { - Item item = CropsNHUtils.getItemFromBlock(e.key); - if (item == null) continue; - int meta = e.meta == null ? OreDictionary.WILDCARD_VALUE : e.meta; - ret.add(new ItemStack(item, 1, meta)); + if (this.blockWhitelist != null) { + for (MetaSet.Entry e : this.blockWhitelist.getStream() + .collect(Collectors.toList())) { + Item item = CropsNHUtils.getItemFromBlock(e.key); + if (item == null) continue; + int meta = e.meta == null ? OreDictionary.WILDCARD_VALUE : e.meta; + ret.add(new ItemStack(item, 1, meta)); + } } // load up materials - for (Materials mat : this.materials) { - // all the ore variations! - ret.addAll(GTOreDictUnificator.getOres(OrePrefixes.ore, mat)); - ret.addAll(GTOreDictUnificator.getOres(OrePrefixes.oreNetherrack, mat)); - ret.addAll(GTOreDictUnificator.getOres(OrePrefixes.oreEndstone, mat)); - ret.addAll(GTOreDictUnificator.getOres(OrePrefixes.oreBlackgranite, mat)); - ret.addAll(GTOreDictUnificator.getOres(OrePrefixes.oreRedgranite, mat)); - ret.addAll(GTOreDictUnificator.getOres(OrePrefixes.oreMarble, mat)); - ret.addAll(GTOreDictUnificator.getOres(OrePrefixes.oreBasalt, mat)); - // compressed storage block - ret.addAll(GTOreDictUnificator.getOres(OrePrefixes.block, mat)); + if (this.materialWhitelist != null) { + for (Materials mat : this.materialWhitelist) { + // all the ore variations! + ret.addAll(GTOreDictUnificator.getOres(OrePrefixes.ore, mat)); + ret.addAll(GTOreDictUnificator.getOres(OrePrefixes.oreNetherrack, mat)); + ret.addAll(GTOreDictUnificator.getOres(OrePrefixes.oreEndstone, mat)); + ret.addAll(GTOreDictUnificator.getOres(OrePrefixes.oreBlackgranite, mat)); + ret.addAll(GTOreDictUnificator.getOres(OrePrefixes.oreRedgranite, mat)); + ret.addAll(GTOreDictUnificator.getOres(OrePrefixes.oreMarble, mat)); + ret.addAll(GTOreDictUnificator.getOres(OrePrefixes.oreBasalt, mat)); + // compressed storage block + ret.addAll(GTOreDictUnificator.getOres(OrePrefixes.block, mat)); + } } // load up ore dicts - for (String oreDict : this.oreDictionaries) { - ret.addAll(OreDictionary.getOres(oreDict)); + if (this.oreDictWhitelist != null) { + for (String oreDict : this.oreDictWhitelist) { + ret.addAll(OreDictionary.getOres(oreDict)); + } + } + + // filter out blacklisted items + if (this.blockBlacklist != null) { + ret.removeIf(x -> { + Block b = CropsNHUtils.getBlockFromItem(x); + // eg armor shards which have the ore dict, this is intended btw. + if (b == null) return false; + return this.blockBlacklist.contains(b, CropsNHUtils.getItemMeta(x)); + }); + } + + // run predicates on it to effectively run all potential blacklists + if (this.predicate != null) { + ret.removeIf(x -> x == null || !this.predicate.test(new SubSoilTarget(x, true))); } // the nei step will deduplicate stuff later, so we don't have to care about that. return ret; } + // endregion NEI and localisations + + // region runtime searching + + /** + * A holder class used to query sub-soil validity at runtime. + */ + public static class SubSoilTarget { + + /** The block being validated. */ + public final Block block; + /** The meta of the item or block being validated. */ + public final int meta; + /** Values of the stack should never be modified. */ + public final ItemStack stack; + /** The tile entity at the position of the block being validated. */ + public final TileEntity te; + /** True if the request originated from NEI. */ + public final boolean isNEI; + + /** + * @param block The block being validated. + * @param meta The meta of the item or block being validated. + * @param te The tile entity at the position of the block being validated. + * @param isNEI True if the request originated from NEI. + */ + public SubSoilTarget(Block block, int meta, TileEntity te, boolean isNEI) { + this.block = block; + this.meta = meta; + this.stack = new ItemStack(block, 1, meta); + this.te = te; + this.isNEI = isNEI; + } + + /** + * + * @param stack The stack being validated. + * @param isNEI True if the request originated from NEI. + */ + public SubSoilTarget(ItemStack stack, boolean isNEI) { + this.block = CropsNHUtils.getBlockFromItem(stack); + this.meta = CropsNHUtils.getItemMeta(stack); + this.stack = stack; + this.te = null; + this.isNEI = isNEI; + } + } + + /** + * Gets the subsoil of the crop stick at position x,y,z + * + * @param world The world the crop stick is in. + * @param x The x coordinate of the crop stick. + * @param y The y coordinate of the crop stick. + * @param z The z coordinate of the crop stick. + * @return The query to used for the sub-soil search. + */ + private static SubSoilTarget getSubSoil(IBlockAccess world, int x, int y, int z) { + // pre-flight check + y -= 2; + if (y < 0) return null; + if (world.isAirBlock(x, y, z)) return null; + + // get the sub-soil + Block block = world.getBlock(x, y, z); + if (block.getMaterial() == Material.air) return null; + int meta = world.getBlockMetadata(x, y, z); + TileEntity te = world.getTileEntity(x, y, z); + return new SubSoilTarget(block, meta, te, false); + } + + /** + * @param target The target to check against. + * @return True if the target isn't blacklisted. + */ + private boolean isNotBlacklistedBlock(SubSoilTarget target) { + if (target.block == null) return true; + assert this.blockBlacklist != null; + return !this.blockBlacklist.contains(target.block, target.meta); + } + + /** + * @param target The target to check against. + * @return True if the target is whitelisted. + */ + private boolean isWhitelistedBlock(SubSoilTarget target) { + if (target.block == null) return false; + assert this.blockWhitelist != null; + return this.blockWhitelist.contains(target.block, target.meta); + } + + /** + * @param target The target to check against. + * @return True if the target is associated with a whitelisted ore dictionary. + */ + private boolean isWhitelistedOreDict(SubSoilTarget target) { + assert this.oreDictWhitelist != null; + for (String oreDict : this.oreDictWhitelist) { + for (int aux = 0; aux < OreDictionary.getOres(oreDict) + .size(); ++aux) { + ItemStack oreDictStack = OreDictionary.getOres(oreDict) + .get(aux); + if (oreDictStack.getItem() != target.stack.getItem()) continue; + int dmg = CropsNHUtils.getItemMeta(oreDictStack); + // check for both the old and the new wildcard value, this helps with a couple things + if (CropsNHUtils.getItemMeta(target.stack) == dmg || OreDictionary.WILDCARD_VALUE == dmg || -1 == dmg) + return true; + } + } + return false; + } + + /** + * @param target The target to check against. + * @return True if the target is associated with a whitelisted GT material. + */ + private boolean isWhitelistedMaterial(SubSoilTarget target) { + assert this.materialWhitelist != null; + for (Materials material : this.materialWhitelist) { + if (target.block instanceof GTBlockOre && target.te instanceof TileEntityOres teOre) { + Materials generatedMaterial = GregTechAPI.sGeneratedMaterials[teOre.mMetaData % 1000]; + if (generatedMaterial != null && generatedMaterial != Materials._NULL + && generatedMaterial == material) { + return true; + } + } else { + ItemData association = GTOreDictUnificator.getAssociation(target.stack); + if (association != null && (association.mPrefix.toString() + .startsWith("ore") || association.mPrefix == OrePrefixes.block) + && (association.mMaterial.mMaterial == material)) { + return true; + } + } + } + return false; + } + + // endregion runtime searching } diff --git a/src/main/java/com/gtnewhorizon/cropsnh/loaders/GTRecipeLoader.java b/src/main/java/com/gtnewhorizon/cropsnh/loaders/GTRecipeLoader.java index 00f54785..123b8868 100644 --- a/src/main/java/com/gtnewhorizon/cropsnh/loaders/GTRecipeLoader.java +++ b/src/main/java/com/gtnewhorizon/cropsnh/loaders/GTRecipeLoader.java @@ -43,14 +43,10 @@ public abstract class GTRecipeLoader extends BaseGTRecipeLoader { - public static void PostInit() { + public static void postInit() { CropRecipes.postInit(); FertilizerRecipes.postInit(); CropsPlusPlusRecipes.postInit(); - SeedGeneratorFakeRecipeLoader.postInit(); - CropBreederFakeRecipeLoader.postInit(); - CropGeneExtractorFakeRecipeLoader.postInit(); - CropSynthesizerFakeRecipeLoader.postInit(); ScannerLoader.postInit(); addPlantLensRecipe(); @@ -74,6 +70,13 @@ public static void PostInit() { addOverclockedGrowthAccelerationUnits(); } + public static void loadComplete() { + SeedGeneratorFakeRecipeLoader.loadComplete(); + CropGeneExtractorFakeRecipeLoader.loadComplete(); + CropSynthesizerFakeRecipeLoader.loadComplete(); + CropBreederFakeRecipeLoader.loadComplete(); + } + private static void addPlantLensRecipe() { // early game glass lens recipe GTModHandler.addCraftingRecipe( diff --git a/src/main/java/com/gtnewhorizon/cropsnh/loaders/SubSoilRequirementLoader.java b/src/main/java/com/gtnewhorizon/cropsnh/loaders/SubSoilLoader.java similarity index 95% rename from src/main/java/com/gtnewhorizon/cropsnh/loaders/SubSoilRequirementLoader.java rename to src/main/java/com/gtnewhorizon/cropsnh/loaders/SubSoilLoader.java index 11fb48c6..1c1f67cf 100644 --- a/src/main/java/com/gtnewhorizon/cropsnh/loaders/SubSoilRequirementLoader.java +++ b/src/main/java/com/gtnewhorizon/cropsnh/loaders/SubSoilLoader.java @@ -1,10 +1,13 @@ package com.gtnewhorizon.cropsnh.loaders; +import java.util.function.Predicate; + import net.minecraft.block.Block; import net.minecraft.init.Blocks; import com.gtnewhorizon.cropsnh.api.BlockWithMeta; import com.gtnewhorizon.cropsnh.api.CropsNHSubSoilTypes; +import com.gtnewhorizon.cropsnh.farming.requirements.SubSoilRequirement; import com.gtnewhorizon.cropsnh.utility.ModUtils; import bartworks.system.material.WerkstoffLoader; @@ -12,7 +15,7 @@ import gregtech.api.GregTechAPI; import gregtech.api.enums.Materials; -public class SubSoilRequirementLoader { +public class SubSoilLoader { public static void postInit() { // spotless:off @@ -318,6 +321,15 @@ public static void postInit() { new BlockWithMeta(ModUtils.TinkerConstruct.getBlock("decoration.multibrickfancy"), 14), new BlockWithMeta(ModUtils.TinkerConstruct.getBlock("decoration.multibrickfancy"), 15) ); + // alu gravel gets replaced with zinc gravel when in full pack via a postea transformer. Because of some nei + // shenanigans all stacks displayed in nei also get hit by postea, so this effectively hides it from nei + // while still actually allowing it if it somehow doesn't get migrated (eg we add it back in the future) + if (ModUtils.NewHorizonsCoreMod.isModLoaded()) { + Block gravelOre = ModUtils.TinkerConstruct.getBlock("GravelOre"); + Predicate predicate = (target) -> !(target.isNEI && target.block == gravelOre && target.meta == 4); + CropsNHSubSoilTypes.aluminiumBauxite.addAndPredicate(predicate); + CropsNHSubSoilTypes.aluminium.addAndPredicate(predicate); + } } if (ModUtils.ThaumicBases.isModLoaded()) { CropsNHSubSoilTypes.stone.addBlock( diff --git a/src/main/java/com/gtnewhorizon/cropsnh/loaders/gtrecipes/CropBreederFakeRecipeLoader.java b/src/main/java/com/gtnewhorizon/cropsnh/loaders/gtrecipes/CropBreederFakeRecipeLoader.java index 8bf6297b..2196fe32 100644 --- a/src/main/java/com/gtnewhorizon/cropsnh/loaders/gtrecipes/CropBreederFakeRecipeLoader.java +++ b/src/main/java/com/gtnewhorizon/cropsnh/loaders/gtrecipes/CropBreederFakeRecipeLoader.java @@ -24,7 +24,7 @@ public class CropBreederFakeRecipeLoader extends BaseGTRecipeLoader { - public static void postInit() { + public static void loadComplete() { for (ICropMutation mutation : MutationRegistry.instance.getDeterministicMutations()) { if (mutation.getOutput() .hideFromNEI()) continue; diff --git a/src/main/java/com/gtnewhorizon/cropsnh/loaders/gtrecipes/CropGeneExtractorFakeRecipeLoader.java b/src/main/java/com/gtnewhorizon/cropsnh/loaders/gtrecipes/CropGeneExtractorFakeRecipeLoader.java index b093205a..7ffaf55d 100644 --- a/src/main/java/com/gtnewhorizon/cropsnh/loaders/gtrecipes/CropGeneExtractorFakeRecipeLoader.java +++ b/src/main/java/com/gtnewhorizon/cropsnh/loaders/gtrecipes/CropGeneExtractorFakeRecipeLoader.java @@ -48,7 +48,7 @@ public CircuitResult(int circuitNo) { } } - public static void postInit() { + public static void loadComplete() { CircuitResult[] recipeCache = new CircuitResult[4]; for (int circuitNo = 1; circuitNo <= 4; circuitNo++) { recipeCache[circuitNo - 1] = new CircuitResult(circuitNo); diff --git a/src/main/java/com/gtnewhorizon/cropsnh/loaders/gtrecipes/CropSynthesizerFakeRecipeLoader.java b/src/main/java/com/gtnewhorizon/cropsnh/loaders/gtrecipes/CropSynthesizerFakeRecipeLoader.java index e3ac4a06..75370229 100644 --- a/src/main/java/com/gtnewhorizon/cropsnh/loaders/gtrecipes/CropSynthesizerFakeRecipeLoader.java +++ b/src/main/java/com/gtnewhorizon/cropsnh/loaders/gtrecipes/CropSynthesizerFakeRecipeLoader.java @@ -17,7 +17,7 @@ public abstract class CropSynthesizerFakeRecipeLoader extends BaseGTRecipeLoader { - public static void postInit() { + public static void loadComplete() { // generate base inputs ItemStack growthOrb = ItemList.Tool_DataOrb.get(0L); BehaviourDataOrb.setDataTitle(growthOrb, Names.DataOrb.growth); diff --git a/src/main/java/com/gtnewhorizon/cropsnh/loaders/gtrecipes/SeedGeneratorFakeRecipeLoader.java b/src/main/java/com/gtnewhorizon/cropsnh/loaders/gtrecipes/SeedGeneratorFakeRecipeLoader.java index 21987786..fde94854 100644 --- a/src/main/java/com/gtnewhorizon/cropsnh/loaders/gtrecipes/SeedGeneratorFakeRecipeLoader.java +++ b/src/main/java/com/gtnewhorizon/cropsnh/loaders/gtrecipes/SeedGeneratorFakeRecipeLoader.java @@ -24,7 +24,7 @@ public abstract class SeedGeneratorFakeRecipeLoader extends BaseGTRecipeLoader { - public static void postInit() { + public static void loadComplete() { for (ICropCard cc : CropRegistry.instance.getAllInRegistrationOrder()) { if (cc.hideFromNEI()) continue; ItemStack seedStack = cc.getSeedItem(SeedStats.DEFAULT_ANALYZED); diff --git a/src/main/java/com/gtnewhorizon/cropsnh/tileentity/multi/MTEIndustrialFarm.java b/src/main/java/com/gtnewhorizon/cropsnh/tileentity/multi/MTEIndustrialFarm.java index a0c235c6..bb8d147e 100644 --- a/src/main/java/com/gtnewhorizon/cropsnh/tileentity/multi/MTEIndustrialFarm.java +++ b/src/main/java/com/gtnewhorizon/cropsnh/tileentity/multi/MTEIndustrialFarm.java @@ -1003,7 +1003,7 @@ private CheckRecipeResult tryAddNewSeeds(List inputs) { if (CropsNHUtils.isStackValid(existingSubSoilStack)) { // check if the existing sub-soil matches the new crop. // this shouldn't happen much since sub-soil can't be manually accessed by the player. - if (!subSoilReq.isValidSubSoil(existingSubSoilStack)) { + if (!subSoilReq.isValidSubSoil(existingSubSoilStack, false)) { return CHECK_RECIPE_RESULT_SUB_SOIL_MISMATCH_INPUT; } newSubSoilStack = existingSubSoilStack; @@ -1012,7 +1012,8 @@ private CheckRecipeResult tryAddNewSeeds(List inputs) { for (subSoilIndex = 0; subSoilIndex < inputs.size(); subSoilIndex++) { ItemStack subSoilCandidate = inputs.get(subSoilIndex); // abort early if it's the seed candidate or the not a valid sub-soil. - if (subSoilIndex == seedIndex || !subSoilReq.isValidSubSoil(subSoilCandidate)) continue; + if (subSoilIndex == seedIndex || !subSoilReq.isValidSubSoil(subSoilCandidate, false)) + continue; // else save the stack for later. newSubSoilStack = subSoilCandidate.copy(); newSubSoilStack.stackSize = 0;