mirror of
				https://github.com/SquidDev-CC/CC-Tweaked
				synced 2025-10-31 13:42:59 +00:00 
			
		
		
		
	Refactor out our JEI plugin into reusable components
Pretty useless right now, but either useful for CC:R or our eventual multi-loader support.
This commit is contained in:
		| @@ -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<ComputerFamily> 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<ItemStack> getExtraStacks() | ||||
|     { | ||||
|         List<ItemStack> 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; | ||||
|     } | ||||
| } | ||||
| @@ -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 <T> The type the recipe mod uses for recipes. | ||||
|  * @see RecipeModHelpers | ||||
|  */ | ||||
| public class UpgradeRecipeGenerator<T> | ||||
| { | ||||
|     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<ICraftingRecipe, T> wrap; | ||||
| 
 | ||||
|     private final Map<Item, List<UpgradeInfo>> upgradeItemLookup = new HashMap<>(); | ||||
|     private final List<UpgradeInfo> pocketUpgrades = new ArrayList<>(); | ||||
|     private final List<UpgradeInfo> turtleUpgrades = new ArrayList<>(); | ||||
|     private boolean initialised = false; | ||||
| 
 | ||||
|     public UpgradeRecipeGenerator( Function<ICraftingRecipe, T> 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<UpgradeInfo> 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<T> 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<T> 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<T> 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<UpgradeInfo> upgrades = upgradeItemLookup.get( stack.getItem() ); | ||||
|             if( upgrades == null ) return Collections.emptyList(); | ||||
| 
 | ||||
|             List<T> 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<T> 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<T> 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<T> 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<T> 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<T> getRecipes() | ||||
|         { | ||||
|             ArrayList<T> 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; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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,21 +66,11 @@ public class JEIComputerCraft implements IModPlugin | ||||
|         IRecipeManager registry = runtime.getRecipeManager(); | ||||
| 
 | ||||
|         // Register all turtles/pocket computers (not just vanilla upgrades) as upgrades on JEI. | ||||
|         List<ItemStack> upgradeItems = new ArrayList<>(); | ||||
|         for( ComputerFamily family : MAIN_FAMILIES ) | ||||
|         List<ItemStack> 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 ); | ||||
|         } | ||||
| 
 | ||||
|         // Hide all upgrade recipes | ||||
|         IRecipeCategory<?> category = registry.getRecipeCategory( VanillaRecipeCategoryUid.CRAFTING ); | ||||
| @@ -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 ); | ||||
|                 } | ||||
|   | ||||
| @@ -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<Item, List<UpgradeInfo>> upgradeItemLookup = new HashMap<>(); | ||||
|     private final List<UpgradeInfo> pocketUpgrades = new ArrayList<>(); | ||||
|     private final List<UpgradeInfo> 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<UpgradeInfo> 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<ICraftingRecipe> resolver = new UpgradeRecipeGenerator<>( x -> x ); | ||||
| 
 | ||||
|     @Nonnull | ||||
|     @Override | ||||
|     public <V> List<ResourceLocation> getRecipeCategoryUids( @Nonnull IFocus<V> focus ) | ||||
|     public <V> List<ResourceLocation> getRecipeCategoryUids( IFocus<V> 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 <T, V> List<T> getRecipes( @Nonnull IRecipeCategory<T> recipeCategory, @Nonnull IFocus<V> focus ) | ||||
|     public <T, V> List<T> getRecipes( @Nonnull IRecipeCategory<T> recipeCategory, IFocus<V> 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<Shaped> 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<Shaped> 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<Shaped> 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<UpgradeInfo> upgrades = upgradeItemLookup.get( stack.getItem() ); | ||||
|             if( upgrades == null ) return Collections.emptyList(); | ||||
| 
 | ||||
|             List<Shaped> 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<Shaped> 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<Shaped> 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<Shaped> 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 <T, U> List<T> cast( List<U> 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<Ingredient> input, ItemStack result ) | ||||
|     { | ||||
|         return new Shaped( 1, input.size(), input, result ); | ||||
|     } | ||||
| 
 | ||||
|     private static Shaped horizontal( NonNullList<Ingredient> 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<Ingredient> 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<Shaped> 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<Shaped> getRecipes() | ||||
|         { | ||||
|             ArrayList<Shaped> 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; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Jonathan Coates
					Jonathan Coates