diff --git a/src/main/java/com/cleanroommc/modularui/core/mixinplugin/Mixins.java b/src/main/java/com/cleanroommc/modularui/core/mixinplugin/Mixins.java index 1de7d46ea..3323fa546 100644 --- a/src/main/java/com/cleanroommc/modularui/core/mixinplugin/Mixins.java +++ b/src/main/java/com/cleanroommc/modularui/core/mixinplugin/Mixins.java @@ -27,7 +27,11 @@ public enum Mixins implements IMixins { THAUMCRAFT(new MixinBuilder() .addClientMixins("thaumcraft.ClientTickEventsFMLMixin") .setPhase(Phase.LATE) - .addRequiredMod(TargetedMod.THAUMCRAFT)); + .addRequiredMod(TargetedMod.THAUMCRAFT)), + NEI(new MixinBuilder() + .addCommonMixins("nei.RecipeInfoMixin") + .setPhase(Phase.LATE) + .addRequiredMod(TargetedMod.NEI)); private final MixinBuilder builder; diff --git a/src/main/java/com/cleanroommc/modularui/core/mixinplugin/TargetedMod.java b/src/main/java/com/cleanroommc/modularui/core/mixinplugin/TargetedMod.java index b9438f7b9..59755b4e2 100644 --- a/src/main/java/com/cleanroommc/modularui/core/mixinplugin/TargetedMod.java +++ b/src/main/java/com/cleanroommc/modularui/core/mixinplugin/TargetedMod.java @@ -6,7 +6,8 @@ public enum TargetedMod implements ITargetMod { - THAUMCRAFT("Thaumcraft"); + THAUMCRAFT("Thaumcraft"), + NEI("NotEnoughItems"); private final TargetModBuilder builder; diff --git a/src/main/java/com/cleanroommc/modularui/core/mixins/late/nei/RecipeInfoMixin.java b/src/main/java/com/cleanroommc/modularui/core/mixins/late/nei/RecipeInfoMixin.java new file mode 100644 index 000000000..67d84ba0b --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/core/mixins/late/nei/RecipeInfoMixin.java @@ -0,0 +1,51 @@ +package com.cleanroommc.modularui.core.mixins.late.nei; + + +import com.cleanroommc.modularui.api.IMuiScreen; +import com.cleanroommc.modularui.integration.nei.INEIRecipeTransfer; +import com.cleanroommc.modularui.integration.nei.ModularUIGuiContainerStackPositioner; +import com.cleanroommc.modularui.integration.nei.NEIModularUIConfig; +import com.cleanroommc.modularui.screen.ModularContainer; + +import net.minecraft.client.gui.inventory.GuiContainer; + +import codechicken.nei.api.IOverlayHandler; +import codechicken.nei.api.IStackPositioner; +import codechicken.nei.recipe.RecipeInfo; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.util.Arrays; + +/* + Mixin to properly handle idents for modular uis +*/ +@Mixin(RecipeInfo.class) +public class RecipeInfoMixin { + + @Inject(method = "hasOverlayHandler(Lnet/minecraft/client/gui/inventory/GuiContainer;Ljava/lang/String;)Z", remap = false, cancellable = true, at = @At("HEAD")) + private static void modularui$hasOverlayHandler(GuiContainer gui, String ident, CallbackInfoReturnable ci) { + if (gui instanceof IMuiScreen && gui.inventorySlots instanceof ModularContainer muc && + muc instanceof INEIRecipeTransfer tr && Arrays.asList(tr.getIdents()).contains(ident)) { + ci.setReturnValue(true); + } + } + + @Inject(method = "getStackPositioner", remap = false, cancellable = true, at = @At("HEAD")) + private static void modularui$getStackPositioner(GuiContainer gui, String ident, CallbackInfoReturnable ci) { + ModularUIGuiContainerStackPositioner positioner = ModularUIGuiContainerStackPositioner.of(gui, ident); + if (positioner != null) { + ci.setReturnValue(positioner); + } + } + + @Inject(method = "getOverlayHandler", remap = false, cancellable = true, at = @At("HEAD")) + private static void modularui$getOverlayHandler(GuiContainer gui, String ident, CallbackInfoReturnable ci) { + if (gui instanceof IMuiScreen && gui.inventorySlots instanceof ModularContainer muc && + muc instanceof INEIRecipeTransfer tr && Arrays.asList(tr.getIdents()).contains(ident)) { + ci.setReturnValue(NEIModularUIConfig.overlayHandler); + } + } +} diff --git a/src/main/java/com/cleanroommc/modularui/integration/nei/INEIRecipeTransfer.java b/src/main/java/com/cleanroommc/modularui/integration/nei/INEIRecipeTransfer.java new file mode 100644 index 000000000..a91df3746 --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/integration/nei/INEIRecipeTransfer.java @@ -0,0 +1,66 @@ +package com.cleanroommc.modularui.integration.nei; + +import net.minecraft.client.gui.inventory.GuiContainer; +import net.minecraft.item.ItemStack; + +import codechicken.nei.PositionedStack; +import codechicken.nei.recipe.GuiOverlayButton; +import codechicken.nei.recipe.IRecipeHandler; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +public interface INEIRecipeTransfer { + + String[] getIdents(); + + default void overlayRecipe(G gui, IRecipeHandler recipe, int recipeIndex, boolean maxTransfer) { + transferRecipe(gui, recipe, recipeIndex, maxTransfer ? Integer.MAX_VALUE : 1); + } + + int transferRecipe(G gui, IRecipeHandler recipe, int recipeIndex, int multiplier); + + default boolean canFillCraftingGrid(G gui, IRecipeHandler recipe, int recipeIndex) { + return true; + } + + default boolean craft(G gui, IRecipeHandler recipe, int recipeIndex, int multiplier) { + return false; + } + + default boolean canCraft(G gui, IRecipeHandler recipe, int recipeIndex) { + return false; + } + + default List presenceOverlay(G gui, IRecipeHandler recipe, int recipeIndex) { + final List itemPresenceSlots = new ArrayList<>(); + final List ingredients = recipe.getIngredientStacks(recipeIndex); + final List invStacks = gui.inventorySlots.inventorySlots.stream() + .filter( + s -> s != null && s.getStack() != null + && s.getStack().stackSize > 0 + && s.isItemValid(s.getStack()) + && s.canTakeStack(gui.mc.thePlayer)) + .map(s -> s.getStack().copy()).collect(Collectors.toList()); + + for (PositionedStack stack : ingredients) { + Optional used = invStacks.stream().filter(is -> is.stackSize > 0 && stack.contains(is)) + .findAny(); + + itemPresenceSlots.add(new GuiOverlayButton.ItemOverlayState(stack, used.isPresent())); + + if (used.isPresent()) { + ItemStack is = used.get(); + is.stackSize -= 1; + } + } + + return itemPresenceSlots; + } + + default ArrayList positionStacks(G gui, ArrayList stacks) { + return stacks; + } +} diff --git a/src/main/java/com/cleanroommc/modularui/integration/nei/ModularUIGuiContainerStackOverlay.java b/src/main/java/com/cleanroommc/modularui/integration/nei/ModularUIGuiContainerStackOverlay.java new file mode 100644 index 000000000..bd6df33a6 --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/integration/nei/ModularUIGuiContainerStackOverlay.java @@ -0,0 +1,68 @@ +package com.cleanroommc.modularui.integration.nei; + +import com.cleanroommc.modularui.api.IMuiScreen; +import com.cleanroommc.modularui.screen.GuiContainerWrapper; +import com.cleanroommc.modularui.screen.ModularContainer; + +import net.minecraft.client.gui.inventory.GuiContainer; + +import codechicken.nei.api.IOverlayHandler; +import codechicken.nei.recipe.GuiOverlayButton; +import codechicken.nei.recipe.IRecipeHandler; + +import java.util.List; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +public class ModularUIGuiContainerStackOverlay implements IOverlayHandler { + + private interface Action { + T doAction(G gui, INEIRecipeTransfer tr); + } + + @SuppressWarnings("unchecked") + private static T doAction(GuiContainer gui, Action action) { + if (gui instanceof IMuiScreen && gui.inventorySlots instanceof ModularContainer mc && mc instanceof INEIRecipeTransfer tr) { + return action.doAction((G) gui, (INEIRecipeTransfer) tr); + } + return null; + } + + @Override + public void overlayRecipe(GuiContainer gui, IRecipeHandler recipe, int recipeIndex, boolean maxTransfer) { + doAction(gui, (mui, tr) -> { + tr.overlayRecipe(mui, recipe, recipeIndex, maxTransfer); + return null; + }); + } + + @Override + public int transferRecipe(GuiContainer gui, IRecipeHandler recipe, int recipeIndex, int multiplier) { + var res = doAction(gui, (mui, tr) -> tr.transferRecipe(mui, recipe, recipeIndex, multiplier)); + return res != null ? res : IOverlayHandler.super.transferRecipe(gui, recipe, recipeIndex, multiplier); + } + + @Override + public boolean canFillCraftingGrid(GuiContainer gui, IRecipeHandler recipe, int recipeIndex) { + var res = doAction(gui, (mui, tr) -> tr.canFillCraftingGrid(mui, recipe, recipeIndex)); + return res != null ? res : IOverlayHandler.super.canFillCraftingGrid(gui, recipe, recipeIndex); + } + + @Override + public boolean craft(GuiContainer gui, IRecipeHandler recipe, int recipeIndex, int multiplier) { + var res = doAction(gui, (mui, tr) -> tr.craft(mui, recipe, recipeIndex, multiplier)); + return res != null ? res : IOverlayHandler.super.craft(gui, recipe, recipeIndex, multiplier); + } + + @Override + public boolean canCraft(GuiContainer gui, IRecipeHandler recipe, int recipeIndex) { + var res = doAction(gui, (mui, tr) -> tr.canCraft(mui, recipe, recipeIndex)); + return res != null ? res : IOverlayHandler.super.canCraft(gui, recipe, recipeIndex); + } + + @Override + public List presenceOverlay(GuiContainer gui, IRecipeHandler recipe, int recipeIndex) { + var res = doAction(gui, (mui, tr) -> tr.presenceOverlay(mui, recipe, recipeIndex)); + return res != null ? res : IOverlayHandler.super.presenceOverlay(gui, recipe, recipeIndex); + } +} diff --git a/src/main/java/com/cleanroommc/modularui/integration/nei/ModularUIGuiContainerStackPositioner.java b/src/main/java/com/cleanroommc/modularui/integration/nei/ModularUIGuiContainerStackPositioner.java new file mode 100644 index 000000000..08dd57403 --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/integration/nei/ModularUIGuiContainerStackPositioner.java @@ -0,0 +1,39 @@ +package com.cleanroommc.modularui.integration.nei; + +import com.cleanroommc.modularui.api.IMuiScreen; +import com.cleanroommc.modularui.screen.ModularContainer; + +import net.minecraft.client.gui.inventory.GuiContainer; + +import codechicken.nei.PositionedStack; +import codechicken.nei.api.IStackPositioner; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Arrays; + +public class ModularUIGuiContainerStackPositioner implements IStackPositioner { + + @Nullable + @SuppressWarnings("unchecked") + public static ModularUIGuiContainerStackPositioner of(GuiContainer gui, String ident) { + if (gui instanceof IMuiScreen && gui.inventorySlots instanceof ModularContainer mc && + mc instanceof INEIRecipeTransfer tr && Arrays.asList(tr.getIdents()).contains(ident)) { + return new ModularUIGuiContainerStackPositioner<>((G) gui, (INEIRecipeTransfer) tr); + } + return null; + } + + public final G wrapper; + public final INEIRecipeTransfer recipeTransfer; + + public ModularUIGuiContainerStackPositioner(G wrapper, INEIRecipeTransfer recipeTransfer) { + this.wrapper = wrapper; + this.recipeTransfer = recipeTransfer; + } + + @Override + public ArrayList positionStacks(ArrayList ai) { + return recipeTransfer.positionStacks(wrapper, ai); + } +} diff --git a/src/main/java/com/cleanroommc/modularui/integration/nei/NEIModularUIConfig.java b/src/main/java/com/cleanroommc/modularui/integration/nei/NEIModularUIConfig.java index aa4491c45..80298abbc 100644 --- a/src/main/java/com/cleanroommc/modularui/integration/nei/NEIModularUIConfig.java +++ b/src/main/java/com/cleanroommc/modularui/integration/nei/NEIModularUIConfig.java @@ -7,6 +7,8 @@ @SuppressWarnings("unused") public class NEIModularUIConfig implements IConfigureNEI { + public static final ModularUIGuiContainerStackOverlay overlayHandler = new ModularUIGuiContainerStackOverlay(); + @Override public void loadConfig() { GuiContainerManager.addInputHandler(new ModularUIContainerInputHandler());