package slimeknights.tconstruct.library; import com.google.common.base.CharMatcher; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import gnu.trove.map.hash.THashMap; import gnu.trove.set.hash.THashSet; import gnu.trove.set.hash.TLinkedHashSet; import net.minecraft.block.Block; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityList; import net.minecraft.init.Blocks; import net.minecraft.init.Items; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.fluids.Fluid; import net.minecraftforge.fluids.FluidRegistry; import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fml.common.Loader; import net.minecraftforge.fml.common.ModContainer; import net.minecraftforge.fml.common.eventhandler.Event; import net.minecraftforge.oredict.OreDictionary; import org.apache.logging.log4j.Logger; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import javax.annotation.Nullable; import slimeknights.mantle.client.CreativeTab; import slimeknights.mantle.util.RecipeMatch; import slimeknights.tconstruct.library.events.MaterialEvent; import slimeknights.tconstruct.library.events.TinkerRegisterEvent; import slimeknights.tconstruct.library.materials.IMaterialStats; import slimeknights.tconstruct.library.materials.Material; import slimeknights.tconstruct.library.materials.MaterialTypes; import slimeknights.tconstruct.library.materials.ProjectileMaterialStats; import slimeknights.tconstruct.library.modifiers.IModifier; import slimeknights.tconstruct.library.smeltery.AlloyRecipe; import slimeknights.tconstruct.library.smeltery.CastingRecipe; import slimeknights.tconstruct.library.smeltery.ICastingRecipe; import slimeknights.tconstruct.library.smeltery.MeltingRecipe; import slimeknights.tconstruct.library.tinkering.PartMaterialType; import slimeknights.tconstruct.library.tools.IPattern; import slimeknights.tconstruct.library.tools.IToolPart; import slimeknights.tconstruct.library.tools.Shard; import slimeknights.tconstruct.library.tools.ToolCore; import slimeknights.tconstruct.library.traits.ITrait; @SuppressWarnings("unused") public final class TinkerRegistry { // the logger for the library public static final Logger log = Util.getLogger("API"); private TinkerRegistry() { } /*--------------------------------------------------------------------------- | CREATIVE TABS | ---------------------------------------------------------------------------*/ public static CreativeTab tabGeneral = new CreativeTab("TinkerGeneral", new ItemStack(Items.SLIME_BALL)); public static CreativeTab tabTools = new CreativeTab("TinkerTools", new ItemStack(Items.IRON_PICKAXE)); public static CreativeTab tabParts = new CreativeTab("TinkerToolParts", new ItemStack(Items.STICK)); public static CreativeTab tabSmeltery = new CreativeTab("TinkerSmeltery", new ItemStack(Item.getItemFromBlock(Blocks.STONEBRICK))); public static CreativeTab tabWorld = new CreativeTab("TinkerWorld", new ItemStack(Item.getItemFromBlock(Blocks.SLIME_BLOCK))); public static CreativeTab tabGadgets = new CreativeTab("TinkerGadgets", new ItemStack(Blocks.TNT)); /*--------------------------------------------------------------------------- | MATERIALS | ---------------------------------------------------------------------------*/ // Identifier to Material mapping. Hashmap so we can look it up directly without iterating private static final Map<String, Material> materials = Maps.newLinkedHashMap(); private static final Map<String, ITrait> traits = new THashMap<String, ITrait>(); // traceability information who registered what. Used to find errors. private static final Map<String, ModContainer> materialRegisteredByMod = new THashMap<String, ModContainer>(); private static final Map<String, Map<String, ModContainer>> statRegisteredByMod = new THashMap<String, Map<String, ModContainer>>(); private static final Map<String, Map<String, ModContainer>> traitRegisteredByMod = new THashMap<String, Map<String, ModContainer>>(); // contains all cancelled materials, allows us to eat calls regarding the material silently private static final Set<String> cancelledMaterials = new THashSet<String>(); public static void addMaterial(Material material, IMaterialStats stats, ITrait trait) { addMaterial(material, stats); addMaterialTrait(material.identifier, trait, null); } public static void addMaterial(Material material, ITrait trait) { addMaterial(material); addMaterialTrait(material.identifier, trait, null); } public static void addMaterial(Material material, IMaterialStats stats) { addMaterial(material); addMaterialStats(material.identifier, stats); } /** * Registers a material. The materials identifier has to be lowercase and not contain any spaces. * Identifiers have to be globally unique! */ public static void addMaterial(Material material) { // ensure material identifiers are safe if(CharMatcher.WHITESPACE.matchesAnyOf(material.getIdentifier())) { error("Could not register Material \"%s\": Material identifier must not contain any spaces.", material.identifier); return; } if(CharMatcher.JAVA_UPPER_CASE.matchesAnyOf(material.getIdentifier())) { error("Could not register Material \"%s\": Material identifier must be completely lowercase.", material.identifier); return; } // duplicate material if(materials.containsKey(material.identifier)) { ModContainer registeredBy = materialRegisteredByMod.get(material.identifier); error(String.format( "Could not register Material \"%s\": It was already registered by %s", material.identifier, registeredBy.getName())); return; } MaterialEvent.MaterialRegisterEvent event = new MaterialEvent.MaterialRegisterEvent(material); if(MinecraftForge.EVENT_BUS.post(event)) { // event cancelled log.trace("Addition of material {} cancelled by event", material.getIdentifier()); cancelledMaterials.add(material.getIdentifier()); return; } // register material materials.put(material.identifier, material); putMaterialTrace(material.identifier); } public static Material getMaterial(String identifier) { return materials.containsKey(identifier) ? materials.get(identifier) : Material.UNKNOWN; } public static Collection<Material> getAllMaterials() { return ImmutableList.copyOf(materials.values()); } public static Collection<Material> getAllMaterialsWithStats(String statType) { ImmutableList.Builder<Material> mats = ImmutableList.builder(); for(Material material : materials.values()) { if(material.hasStats(statType)) { mats.add(material); } } return mats.build(); } /*--------------------------------------------------------------------------- | TRAITS & STATS | ---------------------------------------------------------------------------*/ public static void addTrait(ITrait trait) { // Trait might already have been registered since modifiers and materials share traits if(traits.containsKey(trait.getIdentifier())) { return; } traits.put(trait.getIdentifier(), trait); ModContainer activeMod = Loader.instance().activeModContainer(); putTraitTrace(trait.getIdentifier(), trait, activeMod); } public static void addMaterialStats(String materialIdentifier, IMaterialStats stats) { if(cancelledMaterials.contains(materialIdentifier)) { return; } if(!materials.containsKey(materialIdentifier)) { error(String.format("Could not add Stats \"%s\" to \"%s\": Unknown Material", stats.getIdentifier(), materialIdentifier)); return; } Material material = materials.get(materialIdentifier); addMaterialStats(material, stats); } public static void addMaterialStats(Material material, IMaterialStats stats, IMaterialStats... stats2) { addMaterialStats(material, stats); for(IMaterialStats stat : stats2) { addMaterialStats(material, stat); } } public static void addMaterialStats(Material material, IMaterialStats stats) { if(material == null) { error(String.format("Could not add Stats \"%s\": Material is null", stats.getIdentifier())); return; } if(cancelledMaterials.contains(material.identifier)) { return; } String identifier = material.identifier; // duplicate stats if(material.getStats(stats.getIdentifier()) != null) { String registeredBy = "Unknown"; Map<String, ModContainer> matReg = statRegisteredByMod.get(identifier); if(matReg != null) { registeredBy = matReg.get(stats.getIdentifier()).getName(); } error(String.format( "Could not add Stats to \"%s\": Stats of type \"%s\" were already registered by %s. Use the events to modify stats.", identifier, stats.getIdentifier(), registeredBy)); return; } // ensure there are default stats present if(Material.UNKNOWN.getStats(stats.getIdentifier()) == null) { error("Could not add Stat of type \"%s\": Default Material does not have default stats for said type. Please add default-values to the default material \"unknown\" first.", stats .getIdentifier()); return; } MaterialEvent.StatRegisterEvent<?> event = new MaterialEvent.StatRegisterEvent<IMaterialStats>(material, stats); MinecraftForge.EVENT_BUS.post(event); // overridden stats from event if(event.getResult() == Event.Result.ALLOW) { stats = event.newStats; } material.addStats(stats); ModContainer activeMod = Loader.instance().activeModContainer(); putStatTrace(identifier, stats, activeMod); if(Objects.equals(stats.getIdentifier(), MaterialTypes.HEAD) && !material.hasStats(MaterialTypes.PROJECTILE)) { addMaterialStats(material, new ProjectileMaterialStats()); } } public static void addMaterialTrait(String materialIdentifier, ITrait trait, String stats) { if(cancelledMaterials.contains(materialIdentifier)) { return; } if(!materials.containsKey(materialIdentifier)) { error(String.format("Could not add Trait \"%s\" to \"%s\": Unknown Material", trait.getIdentifier(), materialIdentifier)); return; } Material material = materials.get(materialIdentifier); addMaterialTrait(material, trait, stats); } public static void addMaterialTrait(Material material, ITrait trait, String stats) { if(checkMaterialTrait(material, trait, stats)) { material.addTrait(trait); } } /** * Call before adding a trait to a material. Checks consistency and takes care everything is in a consistent state. * Registers the trait if it's not registered, takes events into account. */ public static boolean checkMaterialTrait(Material material, ITrait trait, String stats) { if(material == null) { error(String.format("Could not add Trait \"%s\": Material is null", trait.getIdentifier())); return false; } if(cancelledMaterials.contains(material.identifier)) { return false; } String identifier = material.identifier; // duplicate traits if(material.hasTrait(trait.getIdentifier(), stats)) { String registeredBy = "Unknown"; Map<String, ModContainer> matReg = traitRegisteredByMod.get(identifier); if(matReg != null) { registeredBy = matReg.get(trait.getIdentifier()).getName(); } error(String.format( "Could not add Trait to \"%s\": Trait \"%s\" was already registered by %s", identifier, trait.getIdentifier(), registeredBy)); return false; } MaterialEvent.TraitRegisterEvent<?> event = new MaterialEvent.TraitRegisterEvent<ITrait>(material, trait); if(MinecraftForge.EVENT_BUS.post(event)) { // cancelled log.trace("Trait {} on {} cancelled by event", trait.getIdentifier(), material.getIdentifier()); return false; } addTrait(trait); return true; } public static ITrait getTrait(String identifier) { return traits.get(identifier); } /*--------------------------------------------------------------------------- | TOOLS & WEAPONS & Crafting | ---------------------------------------------------------------------------*/ /** This set contains all known tools */ private static final Set<ToolCore> tools = new TLinkedHashSet<ToolCore>(); private static final Set<IToolPart> toolParts = new TLinkedHashSet<IToolPart>(); private static final Set<ToolCore> toolStationCrafting = Sets.newLinkedHashSet(); private static final Set<ToolCore> toolForgeCrafting = Sets.newLinkedHashSet(); private static final List<ItemStack> stencilTableCrafting = Lists.newLinkedList(); private static final Set<Item> patternItems = Sets.newHashSet(); private static final Set<Item> castItems = Sets.newHashSet(); private static Shard shardItem; /** * Register a tool, making it known to tinkers' systems. * All toolparts used to craft the tool will be registered as well. */ public static void registerTool(ToolCore tool) { tools.add(tool); for(PartMaterialType pmt : tool.getRequiredComponents()) { for(IToolPart tp : pmt.getPossibleParts()) { registerToolPart(tp); } } } public static Set<ToolCore> getTools() { return ImmutableSet.copyOf(tools); } /** * Used for the sharpening kit. Allows to register a toolpart that is not part of a tool. */ public static void registerToolPart(IToolPart part) { toolParts.add(part); if(part instanceof Item) { if(part.canBeCrafted()) { addPatternForItem((Item) part); } if(part.canBeCasted()) { addCastForItem((Item) part); } } } public static Set<IToolPart> getToolParts() { return ImmutableSet.copyOf(toolParts); } /** Adds a tool to the Crafting UI of both the Tool Station as well as the Tool Forge */ public static void registerToolCrafting(ToolCore tool) { registerToolStationCrafting(tool); registerToolForgeCrafting(tool); } /** Adds a tool to the Crafting UI of the Tool Station */ public static void registerToolStationCrafting(ToolCore tool) { toolStationCrafting.add(tool); } public static Set<ToolCore> getToolStationCrafting() { return ImmutableSet.copyOf(toolStationCrafting); } /** Adds a tool to the Crafting UI of the Tool Forge */ public static void registerToolForgeCrafting(ToolCore tool) { toolForgeCrafting.add(tool); } public static Set<ToolCore> getToolForgeCrafting() { return ImmutableSet.copyOf(toolForgeCrafting); } /** Adds a new pattern to craft to the stenciltable. NBT sensitive. Has to be a Pattern. */ public static void registerStencilTableCrafting(ItemStack stencil) { if(!(stencil.getItem() instanceof IPattern)) { error(String.format( "Stencil Table Crafting has to be a pattern (%s)", stencil.toString())); return; } stencilTableCrafting.add(stencil); } public static List<ItemStack> getStencilTableCrafting() { return ImmutableList.copyOf(stencilTableCrafting); } public static void setShardItem(Shard shard) { if(shard == null) { return; } shardItem = shard; } public static Shard getShard() { return shardItem; } public static ItemStack getShard(Material material) { ItemStack out = material.getShard(); if(out == null) { out = shardItem.getItemstackWithMaterial(material); } return out; } /** Registers a pattern for the given item */ public static void addPatternForItem(Item item) { patternItems.add(item); } /** Registers a cast for the given item */ public static void addCastForItem(Item item) { castItems.add(item); } /** All items that have a pattern */ public static Collection<Item> getPatternItems() { return ImmutableList.copyOf(patternItems); } /** All items that have a cast */ public static Collection<Item> getCastItems() { return ImmutableList.copyOf(castItems); } /*--------------------------------------------------------------------------- | Modifiers | ---------------------------------------------------------------------------*/ private static final Map<String, IModifier> modifiers = new THashMap<String, IModifier>(); public static void registerModifier(IModifier modifier) { registerModifierAlias(modifier, modifier.getIdentifier()); } /** Registers an alternate name for a modifier. This is used for multi-level modifiers/traits where multiple exist, but one specific is needed for access */ public static void registerModifierAlias(IModifier modifier, String alias) { if(new TinkerRegisterEvent.ModifierRegisterEvent(modifier).fire()) { modifiers.put(alias, modifier); } else { log.debug("Registration of modifier " + alias + " has been cancelled by event"); } } public static IModifier getModifier(String identifier) { return modifiers.get(identifier); } public static Collection<IModifier> getAllModifiers() { return ImmutableList.copyOf(modifiers.values()); } /*--------------------------------------------------------------------------- | Smeltery | ---------------------------------------------------------------------------*/ private static List<MeltingRecipe> meltingRegistry = Lists.newLinkedList(); private static List<ICastingRecipe> tableCastRegistry = Lists.newLinkedList(); private static List<ICastingRecipe> basinCastRegistry = Lists.newLinkedList(); private static List<AlloyRecipe> alloyRegistry = Lists.newLinkedList(); private static Map<FluidStack, Integer> smelteryFuels = Maps.newHashMap(); private static Map<String, FluidStack> entityMeltingRegistry = Maps.newHashMap(); /** Registers this item with all its metadatas to melt into amount of the given fluid. */ public static void registerMelting(Item item, Fluid fluid, int amount) { ItemStack stack = new ItemStack(item, 1, OreDictionary.WILDCARD_VALUE); registerMelting(new MeltingRecipe(new RecipeMatch.Item(stack, 1, amount), fluid)); } /** Registers this block with all its metadatas to melt into amount of the given fluid. */ public static void registerMelting(Block block, Fluid fluid, int amount) { ItemStack stack = new ItemStack(block, 1, OreDictionary.WILDCARD_VALUE); registerMelting(new MeltingRecipe(new RecipeMatch.Item(stack, 1, amount), fluid)); } /** Registers this itemstack NBT-SENSITIVE to melt into amount of the given fluid. */ public static void registerMelting(ItemStack stack, Fluid fluid, int amount) { registerMelting(new MeltingRecipe(new RecipeMatch.ItemCombination(amount, stack), fluid)); } public static void registerMelting(String oredict, Fluid fluid, int amount) { registerMelting(new MeltingRecipe(new RecipeMatch.Oredict(oredict, 1, amount), fluid)); } public static void registerMelting(MeltingRecipe recipe) { if(new TinkerRegisterEvent.MeltingRegisterEvent(recipe).fire()) { meltingRegistry.add(recipe); } else { try { String input = recipe.input.getInputs().stream().findFirst().map(ItemStack::getUnlocalizedName).orElse("?"); log.debug("Registration of melting recipe for " + recipe.getResult().getUnlocalizedName() + " from " + input + " has been cancelled by event"); } catch(Exception e) { log.error("Error when logging melting event", e); } } } public static MeltingRecipe getMelting(ItemStack stack) { for(MeltingRecipe recipe : meltingRegistry) { if(recipe.matches(stack)) { return recipe; } } return null; } public static List<MeltingRecipe> getAllMeltingRecipies() { return ImmutableList.copyOf(meltingRegistry); } public static void registerAlloy(FluidStack result, FluidStack... inputs) { if(result.amount < 1) { error("Alloy Recipe: Resulting alloy %s has to have an amount (%d)", result.getLocalizedName(), result.amount); } if(inputs.length < 2) { error("Alloy Recipe: Alloy for %s must consist of at least 2 liquids", result.getLocalizedName()); } registerAlloy(new AlloyRecipe(result, inputs)); } public static void registerAlloy(AlloyRecipe recipe) { if(new TinkerRegisterEvent.AlloyRegisterEvent(recipe).fire()) { alloyRegistry.add(recipe); } else { try { String input = recipe.getFluids().stream().map(FluidStack::getUnlocalizedName).collect(Collectors.joining(", ")); String output = recipe.getResult().getUnlocalizedName(); log.debug("Registration of alloy recipe for " + output + " from [" + input + "] has been cancelled by event"); } catch(Exception e) { log.error("Error when logging alloy event", e); } } } public static List<AlloyRecipe> getAlloys() { return ImmutableList.copyOf(alloyRegistry); } /** Registers a casting recipe for casting table */ public static void registerTableCasting(ItemStack output, @Nullable ItemStack cast, Fluid fluid, int amount) { RecipeMatch rm = null; if(cast != null) { rm = RecipeMatch.ofNBT(cast); } registerTableCasting(new CastingRecipe(output, rm, fluid, amount)); } public static void registerTableCasting(ICastingRecipe recipe) { if(new TinkerRegisterEvent.TableCastingRegisterEvent(recipe).fire()) { tableCastRegistry.add(recipe); } else { try { String output = Optional.ofNullable(recipe.getResult(null, FluidRegistry.WATER)).map(ItemStack::getUnlocalizedName).orElse("Unknown"); log.debug("Registration of table casting recipe for " + output + " has been cancelled by event"); } catch(Exception e) { log.error("Error when logging table casting event", e); } } } public static ICastingRecipe getTableCasting(@Nullable ItemStack cast, Fluid fluid) { for(ICastingRecipe recipe : tableCastRegistry) { if(recipe.matches(cast, fluid)) { return recipe; } } return null; } public static List<ICastingRecipe> getAllTableCastingRecipes() { return ImmutableList.copyOf(tableCastRegistry); } /** Registers a casting recipe for the casting basin */ public static void registerBasinCasting(ItemStack output, @Nullable ItemStack cast, Fluid fluid, int amount) { RecipeMatch rm = null; if(cast != null) { rm = RecipeMatch.ofNBT(cast); } registerBasinCasting(new CastingRecipe(output, rm, fluid, amount)); } public static void registerBasinCasting(ICastingRecipe recipe) { if(new TinkerRegisterEvent.BasinCastingRegisterEvent(recipe).fire()) { basinCastRegistry.add(recipe); } else { try { String output = Optional.ofNullable(recipe.getResult(null, FluidRegistry.WATER)).map(ItemStack::getUnlocalizedName).orElse("Unknown"); log.debug("Registration of basin casting recipe for " + output + " has been cancelled by event"); } catch(Exception e) { log.error("Error when logging basin casting event", e); } } } public static ICastingRecipe getBasinCasting(@Nullable ItemStack cast, Fluid fluid) { for(ICastingRecipe recipe : basinCastRegistry) { if(recipe.matches(cast, fluid)) { return recipe; } } return null; } public static List<ICastingRecipe> getAllBasinCastingRecipes() { return ImmutableList.copyOf(basinCastRegistry); } /** * Registers a liquid to be used as smeltery fuel. * Temperature is derived from fluid temperature. * * @param fluidStack The fluid. Amount is the minimal increment that is consumed at once. * @param fuelDuration How many ticks the consumtpion of the fluidStack lasts. */ public static void registerSmelteryFuel(FluidStack fluidStack, int fuelDuration) { if(new TinkerRegisterEvent.SmelteryFuelRegisterEvent(fluidStack, fuelDuration).fire()) { smelteryFuels.put(fluidStack, fuelDuration); } else { try { String input = fluidStack.getUnlocalizedName(); log.debug("Registration of smeltery fuel " + input + " has been cancelled by event"); } catch(Exception e) { log.error("Error when logging smeltery fuel event", e); } } } /** Checks if the given fluidstack can be used as smeltery fuel */ public static boolean isSmelteryFuel(FluidStack in) { for(Map.Entry<FluidStack, Integer> entry : smelteryFuels.entrySet()) { if(entry.getKey().isFluidEqual(in)) { return true; } } return false; } /** Reduces the fluidstack by one increment of the fuel and returns how much fuel duration it gives. */ public static int consumeSmelteryFuel(FluidStack in) { for(Map.Entry<FluidStack, Integer> entry : smelteryFuels.entrySet()) { if(entry.getKey().isFluidEqual(in)) { FluidStack fuel = entry.getKey(); int out = entry.getValue(); if(in.amount < fuel.amount) { float coeff = (float) in.amount / (float) fuel.amount; out = Math.round(coeff * in.amount); in.amount = 0; } else { in.amount -= fuel.amount; } return out; } } return 0; } /** Returns all registered smeltery fuels */ public static Collection<FluidStack> getSmelteryFuels() { return ImmutableSet.copyOf(smelteryFuels.keySet()); } /** Register an entity to melt into the given fluidstack. The fluidstack is returned for 1 heart damage */ public static void registerEntityMelting(Class<? extends Entity> clazz, FluidStack liquid) { String name = EntityList.CLASS_TO_NAME.get(clazz); if(name == null) { error("Entity Melting: Entity %s is not registered in the EntityList", clazz.getSimpleName()); } TinkerRegisterEvent.EntityMeltingRegisterEvent event = new TinkerRegisterEvent.EntityMeltingRegisterEvent(clazz, liquid); if(event.fire()) { entityMeltingRegistry.put(name, event.getNewFluidStack()); } else { try { String output = liquid.getUnlocalizedName(); log.debug("Registration of entity melting for " + clazz.getName() + " into " + output + " has been cancelled by event"); } catch(Exception e) { log.error("Error when logging entity melting event", e); } } } public static FluidStack getMeltingForEntity(Entity entity) { String name = EntityList.getEntityString(entity); return entityMeltingRegistry.get(name); } /*--------------------------------------------------------------------------- | Drying Rack | ---------------------------------------------------------------------------*/ private static List<DryingRecipe> dryingRegistry = Lists.newLinkedList(); /** * @return The list of all drying rack recipes */ public static List<DryingRecipe> getAllDryingRecipes() { return ImmutableList.copyOf(dryingRegistry); } /** * Adds a new drying recipe * * @param input Input ItemStack * @param output Output ItemStack * @param time Recipe time in ticks */ public static void registerDryingRecipe(ItemStack input, ItemStack output, int time) { if(output == null || input == null) { return; } addDryingRecipe(new DryingRecipe(new RecipeMatch.Item(input, 1), output, time)); } /** * Adds a new drying recipe * * @param input Input Item * @param output Output ItemStack * @param time Recipe time in ticks */ public static void registerDryingRecipe(Item input, ItemStack output, int time) { if(output == null || input == null) { return; } ItemStack stack = new ItemStack(input, 1, OreDictionary.WILDCARD_VALUE); addDryingRecipe(new DryingRecipe(new RecipeMatch.Item(stack, 1), output, time)); } /** * Adds a new drying recipe * * @param input Input Item * @param output Output Item * @param time Recipe time in ticks */ public static void registerDryingRecipe(Item input, Item output, int time) { if(output == null || input == null) { return; } ItemStack stack = new ItemStack(input, 1, OreDictionary.WILDCARD_VALUE); addDryingRecipe(new DryingRecipe(new RecipeMatch.Item(stack, 1), new ItemStack(output), time)); } /** * Adds a new drying recipe * * @param input Input Block * @param output Output Block * @param time Recipe time in ticks */ public static void registerDryingRecipe(Block input, Block output, int time) { if(output == null || input == null) { return; } ItemStack stack = new ItemStack(input, 1, OreDictionary.WILDCARD_VALUE); addDryingRecipe(new DryingRecipe(new RecipeMatch.Item(stack, 1), new ItemStack(output), time)); } /** * Adds a new drying recipe * * @param oredict Input ore dictionary entry * @param output Output ItemStack * @param time Recipe time in ticks */ public static void registerDryingRecipe(String oredict, ItemStack output, int time) { if(output == null || oredict == null) { return; } addDryingRecipe(new DryingRecipe(new RecipeMatch.Oredict(oredict, 1), output, time)); } private static void addDryingRecipe(DryingRecipe recipe) { if(new TinkerRegisterEvent.DryingRackRegisterEvent(recipe).fire()) { dryingRegistry.add(recipe); } else { try { String input = recipe.input.getInputs().stream().findFirst().map(ItemStack::getUnlocalizedName).orElse("?"); String output = recipe.getResult().getUnlocalizedName(); log.debug("Registration of drying rack recipe for " + output + " from " + input + " has been cancelled by event"); } catch(Exception e) { log.error("Error when logging drying rack event", e); } } } /** * Gets the drying time for a drying recipe * * @param input Input ItemStack * @return Output drying time, or -1 if no recipe is found */ public static int getDryingTime(ItemStack input) { for(DryingRecipe r : dryingRegistry) { if(r.matches(input)) { return r.getTime(); } } return -1; } /** * Gets the result for a drying recipe * * @param input Input ItemStack * @return Output A copy of the output ItemStack, or null if no recipe is found */ public static ItemStack getDryingResult(ItemStack input) { for(DryingRecipe r : dryingRegistry) { if(r.matches(input)) { return r.getResult(); } } return null; } /*--------------------------------------------------------------------------- | Traceability & Internal stuff | ---------------------------------------------------------------------------*/ static void putMaterialTrace(String materialIdentifier) { ModContainer activeMod = Loader.instance().activeModContainer(); materialRegisteredByMod.put(materialIdentifier, activeMod); } static void putStatTrace(String materialIdentifier, IMaterialStats stats, ModContainer trace) { if(!statRegisteredByMod.containsKey(materialIdentifier)) { statRegisteredByMod.put(materialIdentifier, new HashMap<String, ModContainer>()); } statRegisteredByMod.get(materialIdentifier).put(stats.getIdentifier(), trace); } static void putTraitTrace(String materialIdentifier, ITrait trait, ModContainer trace) { if(!traitRegisteredByMod.containsKey(materialIdentifier)) { traitRegisteredByMod.put(materialIdentifier, new HashMap<String, ModContainer>()); } traitRegisteredByMod.get(materialIdentifier).put(trait.getIdentifier(), trace); } public static ModContainer getTrace(Material material) { return materialRegisteredByMod.get(material.identifier); } private static void error(String message, Object... params) { throw new TinkerAPIException(String.format(message, params)); } }