From 562f224c01393c7e5055cdc6bebd83073558d331 Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Tue, 25 Oct 2022 19:26:44 +0100 Subject: [PATCH] Refactor out our JEI plugin into reusable components Pretty useless right now, but either useful for CC:R or our eventual multi-loader support. --- .../shared/integration/RecipeModHelpers.java | 70 ++++ .../integration/UpgradeRecipeGenerator.java | 338 ++++++++++++++++++ .../integration/jei/JEIComputerCraft.java | 32 +- .../integration/jei/RecipeResolver.java | 327 +---------------- 4 files changed, 426 insertions(+), 341 deletions(-) create mode 100644 src/main/java/dan200/computercraft/shared/integration/RecipeModHelpers.java create mode 100644 src/main/java/dan200/computercraft/shared/integration/UpgradeRecipeGenerator.java diff --git a/src/main/java/dan200/computercraft/shared/integration/RecipeModHelpers.java b/src/main/java/dan200/computercraft/shared/integration/RecipeModHelpers.java new file mode 100644 index 000000000..1468fde76 --- /dev/null +++ b/src/main/java/dan200/computercraft/shared/integration/RecipeModHelpers.java @@ -0,0 +1,70 @@ +/* + * This file is part of ComputerCraft - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. + * Send enquiries to dratcliffe@gmail.com + */ +package dan200.computercraft.shared.integration; + +import dan200.computercraft.ComputerCraft; +import dan200.computercraft.shared.PocketUpgrades; +import dan200.computercraft.shared.TurtleUpgrades; +import dan200.computercraft.shared.computer.core.ComputerFamily; +import dan200.computercraft.shared.pocket.items.PocketComputerItemFactory; +import dan200.computercraft.shared.turtle.items.TurtleItemFactory; +import net.minecraft.item.ItemStack; +import net.minecraft.util.ResourceLocation; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Utilities for recipe mod plugins (such as JEI). + */ +public final class RecipeModHelpers +{ + static final List MAIN_FAMILIES = Arrays.asList( ComputerFamily.NORMAL, ComputerFamily.ADVANCED ); + + private RecipeModHelpers() + { + } + + /** + * Determine if a recipe should be hidden. This should be used in conjunction with {@link UpgradeRecipeGenerator} + * to hide our upgrade crafting recipes. + * + * @param id The recipe ID. + * @return Whether it should be removed. + */ + public static boolean shouldRemoveRecipe( ResourceLocation id ) + { + if( !id.getNamespace().equals( ComputerCraft.MOD_ID ) ) return false; + + String path = id.getPath(); + return path.startsWith( "turtle_normal/" ) || path.startsWith( "turtle_advanced/" ) + || path.startsWith( "pocket_normal/" ) || path.startsWith( "pocket_advanced/" ); + } + + /** + * Get additional ComputerCraft-related items which may not be visible in a creative tab. This includes upgraded + * turtle and pocket computers for each upgrade. + * + * @return The additional stacks to show. + */ + public static List getExtraStacks() + { + List upgradeItems = new ArrayList<>(); + for( ComputerFamily family : MAIN_FAMILIES ) + { + TurtleUpgrades.getUpgrades().forEach( upgrade -> { + upgradeItems.add( TurtleItemFactory.create( -1, null, -1, family, null, upgrade, 0, null ) ); + } ); + + PocketUpgrades.getUpgrades().forEach( upgrade -> { + upgradeItems.add( PocketComputerItemFactory.create( -1, null, -1, family, upgrade ) ); + } ); + } + + return upgradeItems; + } +} diff --git a/src/main/java/dan200/computercraft/shared/integration/UpgradeRecipeGenerator.java b/src/main/java/dan200/computercraft/shared/integration/UpgradeRecipeGenerator.java new file mode 100644 index 000000000..a58efd58f --- /dev/null +++ b/src/main/java/dan200/computercraft/shared/integration/UpgradeRecipeGenerator.java @@ -0,0 +1,338 @@ +/* + * This file is part of ComputerCraft - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. + * Send enquiries to dratcliffe@gmail.com + */ +package dan200.computercraft.shared.integration; + +import dan200.computercraft.ComputerCraft; +import dan200.computercraft.api.IUpgradeBase; +import dan200.computercraft.api.pocket.IPocketUpgrade; +import dan200.computercraft.api.turtle.ITurtleUpgrade; +import dan200.computercraft.api.turtle.TurtleSide; +import dan200.computercraft.shared.PocketUpgrades; +import dan200.computercraft.shared.TurtleUpgrades; +import dan200.computercraft.shared.computer.core.ComputerFamily; +import dan200.computercraft.shared.pocket.items.ItemPocketComputer; +import dan200.computercraft.shared.pocket.items.PocketComputerItemFactory; +import dan200.computercraft.shared.turtle.items.ItemTurtle; +import dan200.computercraft.shared.turtle.items.TurtleItemFactory; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.item.crafting.ICraftingRecipe; +import net.minecraft.item.crafting.Ingredient; +import net.minecraft.item.crafting.ShapedRecipe; +import net.minecraft.util.NonNullList; +import net.minecraft.util.ResourceLocation; + +import javax.annotation.Nullable; +import java.util.*; +import java.util.function.Function; + +import static dan200.computercraft.shared.integration.RecipeModHelpers.MAIN_FAMILIES; + +/** + * Provides dynamic recipe and usage information for upgraded turtle and pocket computers. This is intended to be + * consumed by our recipe mod plugins (for example JEI). + * + * @param The type the recipe mod uses for recipes. + * @see RecipeModHelpers + */ +public class UpgradeRecipeGenerator +{ + private static final ResourceLocation TURTLE_UPGRADE = new ResourceLocation( ComputerCraft.MOD_ID, "turtle_upgrade" ); + private static final ResourceLocation POCKET_UPGRADE = new ResourceLocation( ComputerCraft.MOD_ID, "pocket_upgrade" ); + + private final Function wrap; + + private final Map> upgradeItemLookup = new HashMap<>(); + private final List pocketUpgrades = new ArrayList<>(); + private final List turtleUpgrades = new ArrayList<>(); + private boolean initialised = false; + + public UpgradeRecipeGenerator( Function wrap ) + { + this.wrap = wrap; + } + + /** + * Build a cache of items which are used for turtle and pocket computer upgrades. + */ + private void setupCache() + { + if( initialised ) return; + initialised = true; + + TurtleUpgrades.getUpgrades().forEach( upgrade -> { + net.minecraft.item.ItemStack stack = upgrade.getCraftingItem(); + if( stack.isEmpty() ) return; + + UpgradeInfo info = new UpgradeInfo( stack, upgrade ); + upgradeItemLookup.computeIfAbsent( stack.getItem(), k -> new ArrayList<>( 1 ) ).add( info ); + turtleUpgrades.add( info ); + } ); + + PocketUpgrades.getUpgrades().forEach( upgrade -> { + ItemStack stack = upgrade.getCraftingItem(); + if( stack.isEmpty() ) return; + + UpgradeInfo info = new UpgradeInfo( stack, upgrade ); + upgradeItemLookup.computeIfAbsent( stack.getItem(), k -> new ArrayList<>( 1 ) ).add( info ); + pocketUpgrades.add( info ); + } ); + } + + /** + * Check if this item is usable as a turtle or pocket computer upgrade. + * + * @param stack The stack to check. + * @return Whether the item is an upgrade. + */ + public boolean isUpgrade( ItemStack stack ) + { + if( stack.isEmpty() ) return false; + + setupCache(); + List upgrades = upgradeItemLookup.get( stack.getItem() ); + if( upgrades == null ) return false; + + for( UpgradeInfo upgrade : upgrades ) + { + ItemStack craftingStack = upgrade.stack; + if( !craftingStack.isEmpty() && craftingStack.getItem() == stack.getItem() && upgrade.upgrade.isItemSuitable( stack ) ) + { + return true; + } + } + + return false; + } + + /** + * Find all usages of the given stack. + * + * @param stack The stack to find usages of. + * @return All upgrade recipes which take the current stack as an input. + */ + public List findRecipesWithInput( ItemStack stack ) + { + setupCache(); + + if( stack.getItem() instanceof ItemTurtle ) + { + ItemTurtle item = (ItemTurtle) stack.getItem(); + + // Suggest possible upgrades which can be applied to this turtle + ITurtleUpgrade left = item.getUpgrade( stack, TurtleSide.LEFT ); + ITurtleUpgrade right = item.getUpgrade( stack, TurtleSide.RIGHT ); + if( left != null && right != null ) return Collections.emptyList(); + + List recipes = new ArrayList<>(); + Ingredient ingredient = Ingredient.of( stack ); + for( UpgradeInfo upgrade : turtleUpgrades ) + { + // The turtle is facing towards us, so upgrades on the left are actually crafted on the right. + if( left == null ) + { + recipes.add( turtle( ingredient, upgrade.ingredient, turtleWith( stack, upgrade.turtle, right ) ) ); + } + + if( right == null ) + { + recipes.add( turtle( upgrade.ingredient, ingredient, turtleWith( stack, left, upgrade.turtle ) ) ); + } + } + + return Collections.unmodifiableList( recipes ); + } + else if( stack.getItem() instanceof ItemPocketComputer ) + { + // Suggest possible upgrades which can be applied to this turtle + IPocketUpgrade back = ItemPocketComputer.getUpgrade( stack ); + if( back != null ) return Collections.emptyList(); + + List recipes = new ArrayList<>(); + Ingredient ingredient = Ingredient.of( stack ); + for( UpgradeInfo upgrade : pocketUpgrades ) + { + recipes.add( pocket( upgrade.ingredient, ingredient, pocketWith( stack, upgrade.pocket ) ) ); + } + + return Collections.unmodifiableList( recipes ); + } + else + { + // If this item is usable as an upgrade, find all possible recipes. + List upgrades = upgradeItemLookup.get( stack.getItem() ); + if( upgrades == null ) return Collections.emptyList(); + + List recipes = null; + boolean multiple = false; + for( UpgradeInfo upgrade : upgrades ) + { + ItemStack craftingStack = upgrade.stack; + if( craftingStack.isEmpty() || craftingStack.getItem() != stack.getItem() || !upgrade.upgrade.isItemSuitable( stack ) ) + { + continue; + } + + if( recipes == null ) + { + recipes = upgrade.getRecipes(); + } + else + { + if( !multiple ) + { + multiple = true; + recipes = new ArrayList<>( recipes ); + } + recipes.addAll( upgrade.getRecipes() ); + } + } + + return recipes == null ? Collections.emptyList() : Collections.unmodifiableList( recipes ); + } + } + + /** + * Find all recipes for the given stack. + * + * @param stack The stack to find recipes of. + * @return All upgrade recipes which produce the stack as an output. + */ + public List findRecipesWithOutput( ItemStack stack ) + { + // Find which upgrade this item currently has, and so how we could build it. + if( stack.getItem() instanceof ItemTurtle ) + { + ItemTurtle item = (ItemTurtle) stack.getItem(); + List recipes = new ArrayList<>( 0 ); + + ITurtleUpgrade left = item.getUpgrade( stack, TurtleSide.LEFT ); + ITurtleUpgrade right = item.getUpgrade( stack, TurtleSide.RIGHT ); + + // The turtle is facing towards us, so upgrades on the left are actually crafted on the right. + if( left != null ) + { + recipes.add( turtle( + Ingredient.of( turtleWith( stack, null, right ) ), + Ingredient.of( left.getCraftingItem() ), + stack + ) ); + } + + if( right != null ) + { + recipes.add( turtle( + Ingredient.of( right.getCraftingItem() ), + Ingredient.of( turtleWith( stack, left, null ) ), + stack + ) ); + } + + return Collections.unmodifiableList( recipes ); + } + else if( stack.getItem() instanceof ItemPocketComputer ) + { + List recipes = new ArrayList<>( 0 ); + + IPocketUpgrade back = ItemPocketComputer.getUpgrade( stack ); + if( back != null ) + { + recipes.add( pocket( Ingredient.of( back.getCraftingItem() ), Ingredient.of( pocketWith( stack, null ) ), stack ) ); + } + + return Collections.unmodifiableList( recipes ); + } + else + { + return Collections.emptyList(); + } + } + + private static ItemStack turtleWith( ItemStack stack, @Nullable ITurtleUpgrade left, @Nullable ITurtleUpgrade right ) + { + ItemTurtle item = (ItemTurtle) stack.getItem(); + return TurtleItemFactory.create( + item.getComputerID( stack ), item.getLabel( stack ), item.getColour( stack ), item.getFamily(), + left, right, item.getFuelLevel( stack ), item.getOverlay( stack ) + ); + } + + private static ItemStack pocketWith( ItemStack stack, @Nullable IPocketUpgrade back ) + { + ItemPocketComputer item = (ItemPocketComputer) stack.getItem(); + return PocketComputerItemFactory.create( + item.getComputerID( stack ), item.getLabel( stack ), item.getColour( stack ), item.getFamily(), + back + ); + } + + private T pocket( Ingredient upgrade, Ingredient pocketComputer, ItemStack result ) + { + return wrap.apply( new ShapedRecipe( POCKET_UPGRADE, "", 1, 2, NonNullList.of( Ingredient.EMPTY, upgrade, pocketComputer ), result ) ); + } + + private T turtle( Ingredient left, Ingredient right, ItemStack result ) + { + return wrap.apply( new ShapedRecipe( TURTLE_UPGRADE, "", 2, 1, NonNullList.of( Ingredient.EMPTY, left, right ), result ) ); + } + + private class UpgradeInfo + { + final ItemStack stack; + final Ingredient ingredient; + final @Nullable ITurtleUpgrade turtle; + final @Nullable IPocketUpgrade pocket; + final IUpgradeBase upgrade; + private @Nullable ArrayList recipes; + + UpgradeInfo( ItemStack stack, ITurtleUpgrade turtle ) + { + this.stack = stack; + ingredient = Ingredient.of( stack ); + upgrade = this.turtle = turtle; + pocket = null; + } + + UpgradeInfo( ItemStack stack, IPocketUpgrade pocket ) + { + this.stack = stack; + ingredient = Ingredient.of( stack ); + turtle = null; + upgrade = this.pocket = pocket; + } + + List getRecipes() + { + ArrayList recipes = this.recipes; + if( recipes != null ) return recipes; + + recipes = this.recipes = new ArrayList<>( 4 ); + for( ComputerFamily family : MAIN_FAMILIES ) + { + if( turtle != null ) + { + recipes.add( turtle( + ingredient, // Right upgrade, recipe on left + Ingredient.of( TurtleItemFactory.create( -1, null, -1, family, null, null, 0, null ) ), + TurtleItemFactory.create( -1, null, -1, family, null, turtle, 0, null ) + ) ); + } + + if( pocket != null ) + { + recipes.add( pocket( + ingredient, + Ingredient.of( PocketComputerItemFactory.create( -1, null, -1, family, null ) ), + PocketComputerItemFactory.create( -1, null, -1, family, pocket ) + ) ); + } + } + + recipes.trimToSize(); + return recipes; + } + } +} diff --git a/src/main/java/dan200/computercraft/shared/integration/jei/JEIComputerCraft.java b/src/main/java/dan200/computercraft/shared/integration/jei/JEIComputerCraft.java index fc6786c0a..0674f5951 100644 --- a/src/main/java/dan200/computercraft/shared/integration/jei/JEIComputerCraft.java +++ b/src/main/java/dan200/computercraft/shared/integration/jei/JEIComputerCraft.java @@ -9,15 +9,11 @@ import dan200.computercraft.ComputerCraft; import dan200.computercraft.api.pocket.IPocketUpgrade; import dan200.computercraft.api.turtle.ITurtleUpgrade; import dan200.computercraft.api.turtle.TurtleSide; -import dan200.computercraft.shared.PocketUpgrades; import dan200.computercraft.shared.Registry; -import dan200.computercraft.shared.TurtleUpgrades; -import dan200.computercraft.shared.computer.core.ComputerFamily; +import dan200.computercraft.shared.integration.RecipeModHelpers; import dan200.computercraft.shared.media.items.ItemDisk; import dan200.computercraft.shared.pocket.items.ItemPocketComputer; -import dan200.computercraft.shared.pocket.items.PocketComputerItemFactory; import dan200.computercraft.shared.turtle.items.ITurtleItem; -import dan200.computercraft.shared.turtle.items.TurtleItemFactory; import mezz.jei.api.IModPlugin; import mezz.jei.api.JeiPlugin; import mezz.jei.api.constants.VanillaRecipeCategoryUid; @@ -34,11 +30,8 @@ import net.minecraft.item.crafting.IRecipe; import net.minecraft.util.ResourceLocation; import javax.annotation.Nonnull; -import java.util.ArrayList; import java.util.List; -import static dan200.computercraft.shared.integration.jei.RecipeResolver.MAIN_FAMILIES; - @JeiPlugin public class JEIComputerCraft implements IModPlugin { @@ -73,22 +66,12 @@ public class JEIComputerCraft implements IModPlugin IRecipeManager registry = runtime.getRecipeManager(); // Register all turtles/pocket computers (not just vanilla upgrades) as upgrades on JEI. - List upgradeItems = new ArrayList<>(); - for( ComputerFamily family : MAIN_FAMILIES ) + List upgradeItems = RecipeModHelpers.getExtraStacks(); + if( !upgradeItems.isEmpty() ) { - TurtleUpgrades.getUpgrades() - .filter( x -> TurtleUpgrades.suitableForFamily( family, x ) ) - .map( x -> TurtleItemFactory.create( -1, null, -1, family, null, x, 0, null ) ) - .forEach( upgradeItems::add ); - - for( IPocketUpgrade upgrade : PocketUpgrades.getUpgrades() ) - { - upgradeItems.add( PocketComputerItemFactory.create( -1, null, -1, family, upgrade ) ); - } + runtime.getIngredientManager().addIngredientsAtRuntime( VanillaTypes.ITEM, upgradeItems ); } - runtime.getIngredientManager().addIngredientsAtRuntime( VanillaTypes.ITEM, upgradeItems ); - // Hide all upgrade recipes IRecipeCategory category = registry.getRecipeCategory( VanillaRecipeCategoryUid.CRAFTING ); if( category != null ) @@ -96,12 +79,7 @@ public class JEIComputerCraft implements IModPlugin for( Object wrapper : registry.getRecipes( category ) ) { if( !(wrapper instanceof IRecipe) ) continue; - ResourceLocation id = ((IRecipe) wrapper).getId(); - if( !id.getNamespace().equals( ComputerCraft.MOD_ID ) ) continue; - - String path = id.getPath(); - if( path.startsWith( "turtle_normal/" ) || path.startsWith( "turtle_advanced/" ) - || path.startsWith( "pocket_normal/" ) || path.startsWith( "pocket_advanced/" ) ) + if( RecipeModHelpers.shouldRemoveRecipe( ((IRecipe) wrapper).getId() ) ) { registry.hideRecipe( wrapper, VanillaRecipeCategoryUid.CRAFTING ); } diff --git a/src/main/java/dan200/computercraft/shared/integration/jei/RecipeResolver.java b/src/main/java/dan200/computercraft/shared/integration/jei/RecipeResolver.java index 1c6411c70..3810f0a21 100644 --- a/src/main/java/dan200/computercraft/shared/integration/jei/RecipeResolver.java +++ b/src/main/java/dan200/computercraft/shared/integration/jei/RecipeResolver.java @@ -5,110 +5,41 @@ */ package dan200.computercraft.shared.integration.jei; -import dan200.computercraft.ComputerCraft; -import dan200.computercraft.api.IUpgradeBase; -import dan200.computercraft.api.pocket.IPocketUpgrade; -import dan200.computercraft.api.turtle.ITurtleUpgrade; -import dan200.computercraft.api.turtle.TurtleSide; -import dan200.computercraft.shared.PocketUpgrades; -import dan200.computercraft.shared.TurtleUpgrades; -import dan200.computercraft.shared.computer.core.ComputerFamily; +import dan200.computercraft.shared.integration.UpgradeRecipeGenerator; import dan200.computercraft.shared.pocket.items.ItemPocketComputer; -import dan200.computercraft.shared.pocket.items.PocketComputerItemFactory; -import dan200.computercraft.shared.turtle.items.ITurtleItem; -import dan200.computercraft.shared.turtle.items.TurtleItemFactory; +import dan200.computercraft.shared.turtle.items.ItemTurtle; import mezz.jei.api.constants.VanillaRecipeCategoryUid; import mezz.jei.api.recipe.IFocus; import mezz.jei.api.recipe.advanced.IRecipeManagerPlugin; import mezz.jei.api.recipe.category.IRecipeCategory; -import net.minecraft.item.Item; import net.minecraft.item.ItemStack; -import net.minecraft.item.crafting.IRecipeSerializer; -import net.minecraft.item.crafting.Ingredient; -import net.minecraft.item.crafting.ShapedRecipe; -import net.minecraft.util.NonNullList; +import net.minecraft.item.crafting.ICraftingRecipe; import net.minecraft.util.ResourceLocation; import javax.annotation.Nonnull; -import java.util.*; - -import static net.minecraft.item.crafting.Ingredient.of; -import static net.minecraft.util.NonNullList.of; +import java.util.Collections; +import java.util.List; class RecipeResolver implements IRecipeManagerPlugin { - static final ComputerFamily[] MAIN_FAMILIES = new ComputerFamily[] { ComputerFamily.NORMAL, ComputerFamily.ADVANCED }; - - private final Map> upgradeItemLookup = new HashMap<>(); - private final List pocketUpgrades = new ArrayList<>(); - private final List turtleUpgrades = new ArrayList<>(); - private boolean initialised = false; - - /** - * Build a cache of items which are used for turtle and pocket computer upgrades. - */ - private void setupCache() - { - if( initialised ) return; - initialised = true; - - TurtleUpgrades.getUpgrades().forEach( upgrade -> { - ItemStack stack = upgrade.getCraftingItem(); - if( stack.isEmpty() ) return; - - UpgradeInfo info = new UpgradeInfo( stack, upgrade ); - upgradeItemLookup.computeIfAbsent( stack.getItem(), k -> new ArrayList<>( 1 ) ).add( info ); - turtleUpgrades.add( info ); - } ); - - for( IPocketUpgrade upgrade : PocketUpgrades.getUpgrades() ) - { - ItemStack stack = upgrade.getCraftingItem(); - if( stack.isEmpty() ) continue; - - UpgradeInfo info = new UpgradeInfo( stack, upgrade ); - upgradeItemLookup.computeIfAbsent( stack.getItem(), k -> new ArrayList<>( 1 ) ).add( info ); - pocketUpgrades.add( info ); - } - } - - private boolean hasUpgrade( @Nonnull ItemStack stack ) - { - if( stack.isEmpty() ) return false; - - setupCache(); - List upgrades = upgradeItemLookup.get( stack.getItem() ); - if( upgrades == null ) return false; - - for( UpgradeInfo upgrade : upgrades ) - { - ItemStack craftingStack = upgrade.stack; - if( !craftingStack.isEmpty() && craftingStack.getItem() == stack.getItem() && upgrade.upgrade.isItemSuitable( stack ) ) - { - return true; - } - } - - return false; - } + private final UpgradeRecipeGenerator resolver = new UpgradeRecipeGenerator<>( x -> x ); @Nonnull @Override - public List getRecipeCategoryUids( @Nonnull IFocus focus ) + public List getRecipeCategoryUids( IFocus focus ) { - V value = focus.getValue(); + Object value = focus.getValue(); if( !(value instanceof ItemStack) ) return Collections.emptyList(); ItemStack stack = (ItemStack) value; switch( focus.getMode() ) { case INPUT: - return stack.getItem() instanceof ITurtleItem || stack.getItem() instanceof ItemPocketComputer || - hasUpgrade( stack ) + return stack.getItem() instanceof ItemTurtle || stack.getItem() instanceof ItemPocketComputer || resolver.isUpgrade( stack ) ? Collections.singletonList( VanillaRecipeCategoryUid.CRAFTING ) : Collections.emptyList(); case OUTPUT: - return stack.getItem() instanceof ITurtleItem || stack.getItem() instanceof ItemPocketComputer + return stack.getItem() instanceof ItemTurtle || stack.getItem() instanceof ItemPocketComputer ? Collections.singletonList( VanillaRecipeCategoryUid.CRAFTING ) : Collections.emptyList(); default: @@ -118,20 +49,19 @@ class RecipeResolver implements IRecipeManagerPlugin @Nonnull @Override - public List getRecipes( @Nonnull IRecipeCategory recipeCategory, @Nonnull IFocus focus ) + public List getRecipes( @Nonnull IRecipeCategory recipeCategory, IFocus focus ) { if( !(focus.getValue() instanceof ItemStack) || !recipeCategory.getUid().equals( VanillaRecipeCategoryUid.CRAFTING ) ) { return Collections.emptyList(); } - ItemStack stack = (ItemStack) focus.getValue(); switch( focus.getMode() ) { case INPUT: - return cast( findRecipesWithInput( stack ) ); + return cast( resolver.findRecipesWithInput( (ItemStack) focus.getValue() ) ); case OUTPUT: - return cast( findRecipesWithOutput( stack ) ); + return cast( resolver.findRecipesWithOutput( (ItemStack) focus.getValue() ) ); default: return Collections.emptyList(); } @@ -144,241 +74,10 @@ class RecipeResolver implements IRecipeManagerPlugin return Collections.emptyList(); } - @Nonnull - private List findRecipesWithInput( @Nonnull ItemStack stack ) - { - setupCache(); - - if( stack.getItem() instanceof ITurtleItem ) - { - // Suggest possible upgrades which can be applied to this turtle - ITurtleItem item = (ITurtleItem) stack.getItem(); - ITurtleUpgrade left = item.getUpgrade( stack, TurtleSide.LEFT ); - ITurtleUpgrade right = item.getUpgrade( stack, TurtleSide.RIGHT ); - if( left != null && right != null ) return Collections.emptyList(); - - List recipes = new ArrayList<>(); - Ingredient ingredient = of( stack ); - for( UpgradeInfo upgrade : turtleUpgrades ) - { - // The turtle is facing towards us, so upgrades on the left are actually crafted on the right. - if( left == null ) - { - recipes.add( horizontal( of( Ingredient.EMPTY, ingredient, upgrade.ingredient ), turtleWith( stack, upgrade.turtle, right ) ) ); - } - - if( right == null ) - { - recipes.add( horizontal( of( Ingredient.EMPTY, upgrade.ingredient, ingredient ), turtleWith( stack, left, upgrade.turtle ) ) ); - } - } - - return cast( recipes ); - } - else if( stack.getItem() instanceof ItemPocketComputer ) - { - // Suggest possible upgrades which can be applied to this turtle - IPocketUpgrade back = ItemPocketComputer.getUpgrade( stack ); - if( back != null ) return Collections.emptyList(); - - List recipes = new ArrayList<>(); - Ingredient ingredient = of( stack ); - for( UpgradeInfo upgrade : pocketUpgrades ) - { - recipes.add( vertical( of( Ingredient.EMPTY, ingredient, upgrade.ingredient ), pocketWith( stack, upgrade.pocket ) ) ); - } - - return recipes; - } - else - { - List upgrades = upgradeItemLookup.get( stack.getItem() ); - if( upgrades == null ) return Collections.emptyList(); - - List recipes = null; - boolean multiple = false; - for( UpgradeInfo upgrade : upgrades ) - { - ItemStack craftingStack = upgrade.stack; - if( craftingStack.isEmpty() || craftingStack.getItem() != stack.getItem() || !upgrade.upgrade.isItemSuitable( stack ) ) - { - continue; - } - - if( recipes == null ) - { - recipes = upgrade.getRecipes(); - } - else - { - if( !multiple ) - { - multiple = true; - recipes = new ArrayList<>( recipes ); - } - recipes.addAll( upgrade.getRecipes() ); - } - } - - return recipes == null ? Collections.emptyList() : recipes; - } - } - - @Nonnull - private static List findRecipesWithOutput( @Nonnull ItemStack stack ) - { - // Find which upgrade this item currently has, an so how we could build it. - if( stack.getItem() instanceof ITurtleItem ) - { - ITurtleItem item = (ITurtleItem) stack.getItem(); - List recipes = new ArrayList<>( 0 ); - - ITurtleUpgrade left = item.getUpgrade( stack, TurtleSide.LEFT ); - ITurtleUpgrade right = item.getUpgrade( stack, TurtleSide.RIGHT ); - - // The turtle is facing towards us, so upgrades on the left are actually crafted on the right. - if( left != null ) - { - recipes.add( horizontal( - of( Ingredient.EMPTY, of( turtleWith( stack, null, right ) ), of( left.getCraftingItem() ) ), - stack - ) ); - } - - if( right != null ) - { - recipes.add( horizontal( - of( Ingredient.EMPTY, of( right.getCraftingItem() ), of( turtleWith( stack, left, null ) ) ), - stack - ) ); - } - - return cast( recipes ); - } - else if( stack.getItem() instanceof ItemPocketComputer ) - { - List recipes = new ArrayList<>( 0 ); - - IPocketUpgrade back = ItemPocketComputer.getUpgrade( stack ); - if( back != null ) - { - recipes.add( vertical( - of( Ingredient.EMPTY, of( back.getCraftingItem() ), of( pocketWith( stack, null ) ) ), - stack - ) ); - } - - return cast( recipes ); - } - else - { - return Collections.emptyList(); - } - } @SuppressWarnings( { "unchecked", "rawtypes" } ) private static List cast( List from ) { return (List) from; } - - private static ItemStack turtleWith( ItemStack stack, ITurtleUpgrade left, ITurtleUpgrade right ) - { - ITurtleItem item = (ITurtleItem) stack.getItem(); - return TurtleItemFactory.create( - item.getComputerID( stack ), item.getLabel( stack ), item.getColour( stack ), item.getFamily(), - left, right, item.getFuelLevel( stack ), item.getOverlay( stack ) - ); - } - - private static ItemStack pocketWith( ItemStack stack, IPocketUpgrade back ) - { - ItemPocketComputer item = (ItemPocketComputer) stack.getItem(); - return PocketComputerItemFactory.create( - item.getComputerID( stack ), item.getLabel( stack ), item.getColour( stack ), item.getFamily(), - back - ); - } - - private static Shaped vertical( NonNullList input, ItemStack result ) - { - return new Shaped( 1, input.size(), input, result ); - } - - private static Shaped horizontal( NonNullList input, ItemStack result ) - { - return new Shaped( input.size(), 1, input, result ); - } - - private static class Shaped extends ShapedRecipe - { - private static final ResourceLocation ID = new ResourceLocation( ComputerCraft.MOD_ID, "impostor" ); - - Shaped( int width, int height, NonNullList input, ItemStack output ) - { - super( ID, null, width, height, input, output ); - } - - @Nonnull - @Override - public IRecipeSerializer getSerializer() - { - throw new IllegalStateException( "Should not serialise the JEI recipe" ); - } - } - - private static class UpgradeInfo - { - final ItemStack stack; - final Ingredient ingredient; - final ITurtleUpgrade turtle; - final IPocketUpgrade pocket; - final IUpgradeBase upgrade; - ArrayList recipes; - - UpgradeInfo( ItemStack stack, ITurtleUpgrade turtle ) - { - this.stack = stack; - ingredient = of( stack ); - upgrade = this.turtle = turtle; - pocket = null; - } - - UpgradeInfo( ItemStack stack, IPocketUpgrade pocket ) - { - this.stack = stack; - ingredient = of( stack ); - turtle = null; - upgrade = this.pocket = pocket; - } - - List getRecipes() - { - ArrayList recipes = this.recipes; - if( recipes != null ) return recipes; - - recipes = this.recipes = new ArrayList<>( 4 ); - for( ComputerFamily family : MAIN_FAMILIES ) - { - if( turtle != null && TurtleUpgrades.suitableForFamily( family, turtle ) ) - { - recipes.add( horizontal( - of( Ingredient.EMPTY, ingredient, of( TurtleItemFactory.create( -1, null, -1, family, null, null, 0, null ) ) ), - TurtleItemFactory.create( -1, null, -1, family, null, turtle, 0, null ) - ) ); - } - - if( pocket != null ) - { - recipes.add( vertical( - of( Ingredient.EMPTY, ingredient, of( PocketComputerItemFactory.create( -1, null, -1, family, null ) ) ), - PocketComputerItemFactory.create( -1, null, -1, family, pocket ) - ) ); - } - } - - recipes.trimToSize(); - return recipes; - } - } }