From ab85baafd92ea6f46a879005adb5a40629bbd64d Mon Sep 17 00:00:00 2001 From: Alex <8379108+Alex33856@users.noreply.github.com> Date: Thu, 24 Jul 2025 16:04:24 -0400 Subject: REI /viewrecipe Crafting, Skyblock Info Display (#1496) * Skyblock Info Display + /viewrecipe Crafting * Don't register global display generator if already there * Check if /viewrecipe failed, use similar system for item price lookup failure * Exclude Forge recipes * Update comment * Update Item Price Lookup keybind translation --- .../compatibility/rei/SkyblockRecipeCategory.java | 94 ----------------- .../compatibility/rei/SkyblockRecipeDisplay.java | 56 ---------- .../rei/SkyblockRecipeDisplayGenerator.java | 24 ----- .../compatibility/rei/SkyblockTransferHandler.java | 81 +++++++++++++++ .../rei/SkyblockerREIClientPlugin.java | 20 +++- .../rei/info/SkyblockInfoCategory.java | 114 +++++++++++++++++++++ .../rei/info/SkyblockInfoDisplay.java | 47 +++++++++ .../rei/info/SkyblockInfoDisplayGenerator.java | 23 +++++ .../rei/recipe/SkyblockRecipeCategory.java | 94 +++++++++++++++++ .../rei/recipe/SkyblockRecipeDisplay.java | 56 ++++++++++ .../rei/recipe/SkyblockRecipeDisplayGenerator.java | 24 +++++ 11 files changed, 457 insertions(+), 176 deletions(-) delete mode 100644 src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockRecipeCategory.java delete mode 100644 src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockRecipeDisplay.java delete mode 100644 src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockRecipeDisplayGenerator.java create mode 100644 src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockTransferHandler.java create mode 100644 src/main/java/de/hysky/skyblocker/compatibility/rei/info/SkyblockInfoCategory.java create mode 100644 src/main/java/de/hysky/skyblocker/compatibility/rei/info/SkyblockInfoDisplay.java create mode 100644 src/main/java/de/hysky/skyblocker/compatibility/rei/info/SkyblockInfoDisplayGenerator.java create mode 100644 src/main/java/de/hysky/skyblocker/compatibility/rei/recipe/SkyblockRecipeCategory.java create mode 100644 src/main/java/de/hysky/skyblocker/compatibility/rei/recipe/SkyblockRecipeDisplay.java create mode 100644 src/main/java/de/hysky/skyblocker/compatibility/rei/recipe/SkyblockRecipeDisplayGenerator.java (limited to 'src/main/java') diff --git a/src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockRecipeCategory.java b/src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockRecipeCategory.java deleted file mode 100644 index 0460a1cf..00000000 --- a/src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockRecipeCategory.java +++ /dev/null @@ -1,94 +0,0 @@ -package de.hysky.skyblocker.compatibility.rei; - -import de.hysky.skyblocker.skyblock.itemlist.recipes.SkyblockRecipe; -import me.shedaniel.math.Point; -import me.shedaniel.math.Rectangle; -import me.shedaniel.rei.api.client.gui.Renderer; -import me.shedaniel.rei.api.client.gui.widgets.Widget; -import me.shedaniel.rei.api.client.gui.widgets.Widgets; -import me.shedaniel.rei.api.client.registry.display.DisplayCategory; -import me.shedaniel.rei.api.common.category.CategoryIdentifier; -import me.shedaniel.rei.api.common.util.EntryStacks; -import net.minecraft.client.gui.ScreenPos; -import net.minecraft.client.util.math.MatrixStack; -import net.minecraft.item.ItemStack; -import net.minecraft.text.Text; -import net.minecraft.util.Identifier; - -import java.util.ArrayList; -import java.util.List; - -/** - * Skyblock recipe category class for REI - */ -public class SkyblockRecipeCategory implements DisplayCategory { - - private final Identifier identifier; - private final Text title; - private final ItemStack icon; - private final int height; - - public SkyblockRecipeCategory(Identifier identifier, Text title, ItemStack icon, int height) { - this.identifier = identifier; - this.title = title; - this.icon = icon; - this.height = height; - } - - @Override - public CategoryIdentifier getCategoryIdentifier() { - return CategoryIdentifier.of(identifier); - } - - @Override - public int getDisplayHeight() { - return height; - } - - @Override - public Text getTitle() { - return title; - } - - @Override - public Renderer getIcon() { - return EntryStacks.of(icon); - } - - /** - * Draws display for SkyblockCraftingDisplay - * - * @param display the display - * @param bounds the bounds of the display, configurable with overriding the width, height methods. - */ - @Override - public List setupDisplay(SkyblockRecipeDisplay display, Rectangle bounds) { - List out = new ArrayList<>(); - out.add(Widgets.createRecipeBase(bounds)); - SkyblockRecipe recipe = display.getRecipe(); - for (SkyblockRecipe.RecipeSlot inputSlot : recipe.getInputSlots(bounds.getWidth(), bounds.getHeight())) { - out.add(Widgets.createSlot(new Point(inputSlot.x() + bounds.getX(), inputSlot.y() + bounds.getY())) - .markInput() - .backgroundEnabled(inputSlot.showBackground()) - .entry(EntryStacks.of(inputSlot.stack()))); - } - for (SkyblockRecipe.RecipeSlot outputSlot : recipe.getOutputSlots(bounds.getWidth(), bounds.getHeight())) { - out.add(Widgets.createSlot(new Point(outputSlot.x() + bounds.getX(), outputSlot.y() + bounds.getY())) - .markOutput() - .backgroundEnabled(outputSlot.showBackground()) - .entry(EntryStacks.of(outputSlot.stack()))); - } - out.add(Widgets.createDrawableWidget((context, mouseX, mouseY, delta) -> { - MatrixStack matrices = context.getMatrices(); - matrices.push(); - matrices.translate(bounds.getX(), bounds.getY(), 0.f); - recipe.render(context, bounds.getWidth(), bounds.getHeight(), mouseX - bounds.getX(), mouseY - bounds.getY()); - matrices.pop(); - })); - ScreenPos arrowLocation = recipe.getArrowLocation(bounds.getWidth(), bounds.getHeight()); - if (arrowLocation != null) - out.add(Widgets.createArrow(new Point(arrowLocation.x() + bounds.getX(), arrowLocation.y() + bounds.getY()))); - out.add(Widgets.createLabel(new Point(bounds.getCenterX(), bounds.getCenterY() + 24), recipe.getExtraText())); - return out; - } -} diff --git a/src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockRecipeDisplay.java b/src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockRecipeDisplay.java deleted file mode 100644 index 44d44011..00000000 --- a/src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockRecipeDisplay.java +++ /dev/null @@ -1,56 +0,0 @@ -package de.hysky.skyblocker.compatibility.rei; - -import de.hysky.skyblocker.skyblock.itemlist.recipes.SkyblockRecipe; -import me.shedaniel.rei.api.common.category.CategoryIdentifier; -import me.shedaniel.rei.api.common.display.Display; -import me.shedaniel.rei.api.common.display.DisplaySerializer; -import me.shedaniel.rei.api.common.entry.EntryIngredient; -import me.shedaniel.rei.api.common.util.EntryStacks; -import net.minecraft.util.Identifier; -import org.jetbrains.annotations.Nullable; - -import java.util.List; -import java.util.Optional; - -/** - * Skyblock Crafting Recipe display class for REI - */ -public class SkyblockRecipeDisplay implements Display { - - private final Identifier category; - private final SkyblockRecipe recipe; - - public SkyblockRecipeDisplay(SkyblockRecipe recipe) { - this.category = recipe.getCategoryIdentifier(); - this.recipe = recipe; - } - - @Override - public List getInputEntries() { - return recipe.getInputs().stream().map(EntryStacks::of).map(EntryIngredient::of).toList(); - } - - @Override - public List getOutputEntries() { - return recipe.getOutputs().stream().map(EntryStacks::of).map(EntryIngredient::of).toList(); - } - - public SkyblockRecipe getRecipe() { - return recipe; - } - - @Override - public CategoryIdentifier getCategoryIdentifier() { - return CategoryIdentifier.of(category); - } - - @Override - public Optional getDisplayLocation() { - return Optional.empty(); - } - - @Override - public @Nullable DisplaySerializer getSerializer() { - return null; - } -} diff --git a/src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockRecipeDisplayGenerator.java b/src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockRecipeDisplayGenerator.java deleted file mode 100644 index 2b9775c6..00000000 --- a/src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockRecipeDisplayGenerator.java +++ /dev/null @@ -1,24 +0,0 @@ -package de.hysky.skyblocker.compatibility.rei; - -import de.hysky.skyblocker.skyblock.itemlist.ItemRepository; -import me.shedaniel.rei.api.client.registry.display.DynamicDisplayGenerator; -import me.shedaniel.rei.api.common.entry.EntryStack; -import net.minecraft.item.ItemStack; - -import java.util.List; -import java.util.Optional; - -public class SkyblockRecipeDisplayGenerator implements DynamicDisplayGenerator { - - @Override - public Optional> getRecipeFor(EntryStack entry) { - if (!(entry.getValue() instanceof ItemStack entryStack)) return Optional.empty(); - return Optional.of(ItemRepository.getRecipes(entryStack).map(SkyblockRecipeDisplay::new).toList()); - } - - @Override - public Optional> getUsageFor(EntryStack entry) { - if (!(entry.getValue() instanceof ItemStack entryStack)) return Optional.empty(); - return Optional.of(ItemRepository.getUsages(entryStack).map(SkyblockRecipeDisplay::new).toList()); - } -} diff --git a/src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockTransferHandler.java b/src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockTransferHandler.java new file mode 100644 index 00000000..8272a474 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockTransferHandler.java @@ -0,0 +1,81 @@ +package de.hysky.skyblocker.compatibility.rei; + +import de.hysky.skyblocker.compatibility.rei.info.SkyblockInfoCategory; +import de.hysky.skyblocker.skyblock.itemlist.recipes.SkyblockCraftingRecipe; +import de.hysky.skyblocker.utils.NEURepoManager; +import de.hysky.skyblocker.utils.scheduler.MessageScheduler; +import de.hysky.skyblocker.utils.scheduler.Scheduler; +import io.github.moulberry.repo.data.NEUCraftingRecipe; +import me.shedaniel.rei.api.client.registry.transfer.TransferHandler; +import me.shedaniel.rei.api.common.category.CategoryIdentifier; +import me.shedaniel.rei.api.common.entry.EntryIngredient; +import me.shedaniel.rei.api.common.entry.EntryStack; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.screen.ingame.GenericContainerScreen; +import net.minecraft.item.ItemStack; +import net.minecraft.text.Text; + +public class SkyblockTransferHandler implements TransferHandler { + private static final int MAX_FAIL_COUNT = 5; + + private String FAILED_ITEM; + private int FAIL_COUNT; + + @Override + public ApplicabilityResult checkApplicable(Context context) { + // Only work on our displays + CategoryIdentifier identifier = context.getDisplay().getCategoryIdentifier(); + if (identifier != CategoryIdentifier.of(SkyblockCraftingRecipe.IDENTIFIER) && identifier != CategoryIdentifier.of(SkyblockInfoCategory.IDENTIFIER)) + return ApplicabilityResult.createNotApplicable(); + + EntryIngredient ingredient = context.getDisplay().getOutputEntries().getFirst(); + EntryStack entryStack = ingredient.getFirst(); + if (!(entryStack.getValue() instanceof ItemStack itemStack)) + return ApplicabilityResult.createNotApplicable(); + + // Not applicable if it has 0 recipes, or no crafting recipe + String neuId = itemStack.getNeuName(); + if (!NEURepoManager.getRecipes().containsKey(neuId) || NEURepoManager.getRecipes().get(neuId).stream().noneMatch(recipe -> recipe instanceof NEUCraftingRecipe)) + return ApplicabilityResult.createApplicableWithError(Text.translatable("skyblocker.rei.transfer.noRecipe")); + + return ApplicabilityResult.createApplicable(); + + } + + private boolean hasFailed(String skyblockId) { + if (skyblockId.equals(FAILED_ITEM)) { + if (FAIL_COUNT < MAX_FAIL_COUNT) { + FAIL_COUNT += 1; + return true; + } + + FAILED_ITEM = ""; + FAIL_COUNT = 0; + } + return false; + } + + private void checkScreen(String skyblockId) { + Screen currentScreen = MinecraftClient.getInstance().currentScreen; + if (!(currentScreen instanceof GenericContainerScreen)) { + FAIL_COUNT = 0; + FAILED_ITEM = skyblockId; + } + } + + @Override + public Result handle(Context context) { + EntryIngredient ingredient = context.getDisplay().getOutputEntries().getFirst(); + EntryStack entryStack = ingredient.getFirst(); + if (!(entryStack.getValue() instanceof ItemStack itemStack)) return Result.createNotApplicable(); + + String skyblockId = itemStack.getSkyblockId(); + if (hasFailed(skyblockId)) return Result.createFailed(Text.translatable("skyblocker.rei.transfer.failed")); + if (!context.isActuallyCrafting()) return Result.createSuccessful(); + + MessageScheduler.INSTANCE.sendMessageAfterCooldown("/viewrecipe " + skyblockId, false); + Scheduler.INSTANCE.schedule(() -> checkScreen(skyblockId), 5); + return Result.createSuccessful(); + } +} diff --git a/src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockerREIClientPlugin.java b/src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockerREIClientPlugin.java index 59a694f6..febcf032 100644 --- a/src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockerREIClientPlugin.java +++ b/src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockerREIClientPlugin.java @@ -1,5 +1,9 @@ package de.hysky.skyblocker.compatibility.rei; +import de.hysky.skyblocker.compatibility.rei.info.SkyblockInfoCategory; +import de.hysky.skyblocker.compatibility.rei.info.SkyblockInfoDisplayGenerator; +import de.hysky.skyblocker.compatibility.rei.recipe.SkyblockRecipeCategory; +import de.hysky.skyblocker.compatibility.rei.recipe.SkyblockRecipeDisplayGenerator; import de.hysky.skyblocker.config.SkyblockerConfigManager; import de.hysky.skyblocker.mixins.accessors.HandledScreenAccessor; import de.hysky.skyblocker.skyblock.garden.visitor.VisitorHelper; @@ -15,6 +19,7 @@ import me.shedaniel.rei.api.client.registry.category.CategoryRegistry; import me.shedaniel.rei.api.client.registry.display.DisplayRegistry; import me.shedaniel.rei.api.client.registry.entry.EntryRegistry; import me.shedaniel.rei.api.client.registry.screen.ExclusionZones; +import me.shedaniel.rei.api.client.registry.transfer.TransferHandlerRegistry; import me.shedaniel.rei.api.common.category.CategoryIdentifier; import me.shedaniel.rei.api.common.util.EntryStacks; import net.minecraft.client.gui.screen.Screen; @@ -29,19 +34,24 @@ import java.util.List; */ public class SkyblockerREIClientPlugin implements REIClientPlugin { - @Override + @Override public void registerCategories(CategoryRegistry categoryRegistry) { if (!SkyblockerConfigManager.get().general.itemList.enableItemList) return; categoryRegistry.addWorkstations(CategoryIdentifier.of(SkyblockCraftingRecipe.IDENTIFIER), EntryStacks.of(Items.CRAFTING_TABLE)); categoryRegistry.addWorkstations(CategoryIdentifier.of(SkyblockForgeRecipe.IDENTIFIER), EntryStacks.of(Items.ANVIL)); categoryRegistry.add(new SkyblockRecipeCategory(SkyblockCraftingRecipe.IDENTIFIER, Text.translatable("emi.category.skyblocker.skyblock_crafting"), ItemUtils.getSkyblockerStack(), 73)); categoryRegistry.add(new SkyblockRecipeCategory(SkyblockForgeRecipe.IDENTIFIER, Text.translatable("emi.category.skyblocker.skyblock_forge"), ItemUtils.getSkyblockerForgeStack(), 84)); + + categoryRegistry.add(new SkyblockInfoCategory()); } @Override public void registerDisplays(DisplayRegistry displayRegistry) { if (!SkyblockerConfigManager.get().general.itemList.enableItemList) return; - displayRegistry.registerGlobalDisplayGenerator(new SkyblockRecipeDisplayGenerator()); + if (displayRegistry.getGlobalDisplayGenerators().stream().noneMatch(generator -> generator instanceof SkyblockRecipeDisplayGenerator)) + displayRegistry.registerGlobalDisplayGenerator(new SkyblockRecipeDisplayGenerator()); + if (displayRegistry.getGlobalDisplayGenerators().stream().noneMatch(generator -> generator instanceof SkyblockInfoDisplayGenerator)) + displayRegistry.registerGlobalDisplayGenerator(new SkyblockInfoDisplayGenerator()); } @Override @@ -65,6 +75,12 @@ public class SkyblockerREIClientPlugin implements REIClientPlugin { }); } + @Override + public void registerTransferHandlers(TransferHandlerRegistry registry) { + if (!SkyblockerConfigManager.get().general.itemList.enableItemList) return; + registry.register(new SkyblockTransferHandler()); + } + @Override public double getPriority() { return -50; diff --git a/src/main/java/de/hysky/skyblocker/compatibility/rei/info/SkyblockInfoCategory.java b/src/main/java/de/hysky/skyblocker/compatibility/rei/info/SkyblockInfoCategory.java new file mode 100644 index 00000000..d1e48458 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/compatibility/rei/info/SkyblockInfoCategory.java @@ -0,0 +1,114 @@ +package de.hysky.skyblocker.compatibility.rei.info; + +import de.hysky.skyblocker.SkyblockerMod; +import de.hysky.skyblocker.skyblock.item.ItemPrice; +import de.hysky.skyblocker.skyblock.item.WikiLookup; +import de.hysky.skyblocker.skyblock.itemlist.ItemRepository; +import de.hysky.skyblocker.utils.render.gui.AbstractCustomHypixelGUI; +import de.hysky.skyblocker.utils.scheduler.Scheduler; +import me.shedaniel.math.Point; +import me.shedaniel.math.Rectangle; +import me.shedaniel.rei.api.client.gui.Renderer; +import me.shedaniel.rei.api.client.gui.widgets.Slot; +import me.shedaniel.rei.api.client.gui.widgets.Widget; +import me.shedaniel.rei.api.client.gui.widgets.Widgets; +import me.shedaniel.rei.api.client.registry.display.DisplayCategory; +import me.shedaniel.rei.api.common.category.CategoryIdentifier; +import me.shedaniel.rei.api.common.entry.EntryStack; +import me.shedaniel.rei.api.common.util.EntryStacks; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.screen.ingame.GenericContainerScreen; +import net.minecraft.client.gui.widget.ButtonWidget; +import net.minecraft.client.gui.widget.DirectionalLayoutWidget; +import net.minecraft.client.network.ClientPlayerEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.text.Text; +import net.minecraft.util.Identifier; + +import java.util.ArrayList; +import java.util.List; + +public class SkyblockInfoCategory implements DisplayCategory { + private static final int REI_SLOT_HEIGHT = 18; + private static final int OFFSET = 10; + private static final int RED_ERROR_COLOR = 0xFFFF5555; + private static final EntryStack ICON = EntryStacks.of(new ItemStack(Items.CHEST)); + + public static final Identifier IDENTIFIER = Identifier.of(SkyblockerMod.NAMESPACE, "skyblock_info"); + + @Override + public CategoryIdentifier getCategoryIdentifier() { + return CategoryIdentifier.of(IDENTIFIER); + } + + @Override + public Text getTitle() { + return Text.translatable("emi.category.skyblocker.skyblock_info"); + } + + @Override + public Renderer getIcon() { + return ICON; + } + + private ButtonWidget getWikiLookupButton(Text text, boolean isOfficial, ItemStack itemStack, ClientPlayerEntity player) { + ButtonWidget btn = ButtonWidget.builder(text, (button) -> WikiLookup.openWiki(itemStack, player, isOfficial)).build(); + + if (ItemRepository.getWikiLink(itemStack.getNeuName(), isOfficial) == null) { + btn.setMessage(btn.getMessage().copy().withColor(RED_ERROR_COLOR)); + btn.active = false; + } + + return btn; + } + + private boolean checkScreen() { + Screen currentScreen = MinecraftClient.getInstance().currentScreen; + return currentScreen instanceof GenericContainerScreen || currentScreen instanceof AbstractCustomHypixelGUI; + } + + @Override + public List setupDisplay(SkyblockInfoDisplay display, Rectangle bounds) { + List widgets = new ArrayList<>(); + EntryStack entryStack = display.getInputEntries().getFirst().getFirst(); + if (!(entryStack.getValue() instanceof ItemStack itemStack)) return widgets; + + widgets.add(Widgets.createRecipeBase(bounds)); + Slot slot = Widgets.createSlot(new Point(bounds.getCenterX() - 9 + 1, bounds.y + 1 + OFFSET / 2)).entry(entryStack); + widgets.add(slot); + + ClientPlayerEntity player = MinecraftClient.getInstance().player; + DirectionalLayoutWidget layoutWidget = DirectionalLayoutWidget.vertical(); + layoutWidget.setPosition(bounds.x + OFFSET, bounds.y + OFFSET + REI_SLOT_HEIGHT); + + layoutWidget.add(ButtonWidget.builder(Text.translatable("key.itemPriceLookup"), (button) -> { + ItemPrice.itemPriceLookup(player, itemStack); + + Scheduler.INSTANCE.schedule(() -> { + if (checkScreen()) return; + button.setMessage(Text.translatable("skyblocker.rei.skyblockInfo.failedToFind").withColor(RED_ERROR_COLOR)); + button.active = false; + }, 10); + }).build()); + + layoutWidget.add(getWikiLookupButton(Text.translatable("key.wikiLookup.official"), true, itemStack, player)); + layoutWidget.add(getWikiLookupButton(Text.translatable("key.wikiLookup.fandom"), false, itemStack, player)); + + layoutWidget.forEachChild(child -> widgets.add(Widgets.wrapVanillaWidget(child))); + layoutWidget.refreshPositions(); + + return widgets; + } + + @Override + public int getDisplayHeight() { + return 3 * ButtonWidget.DEFAULT_HEIGHT + REI_SLOT_HEIGHT + 2 * OFFSET; + } + + @Override + public int getDisplayWidth(SkyblockInfoDisplay display) { + return 150 + 20; + } +} diff --git a/src/main/java/de/hysky/skyblocker/compatibility/rei/info/SkyblockInfoDisplay.java b/src/main/java/de/hysky/skyblocker/compatibility/rei/info/SkyblockInfoDisplay.java new file mode 100644 index 00000000..22714941 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/compatibility/rei/info/SkyblockInfoDisplay.java @@ -0,0 +1,47 @@ +package de.hysky.skyblocker.compatibility.rei.info; + +import de.hysky.skyblocker.SkyblockerMod; +import me.shedaniel.rei.api.common.category.CategoryIdentifier; +import me.shedaniel.rei.api.common.display.Display; +import me.shedaniel.rei.api.common.display.DisplaySerializer; +import me.shedaniel.rei.api.common.entry.EntryIngredient; +import me.shedaniel.rei.api.common.util.EntryStacks; +import net.minecraft.item.ItemStack; +import net.minecraft.util.Identifier; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Optional; + +public class SkyblockInfoDisplay implements Display { + private final ItemStack displayItem; + + public SkyblockInfoDisplay(ItemStack item) { + this.displayItem = item; + } + + @Override + public List getInputEntries() { + return List.of(EntryIngredient.of(EntryStacks.of(displayItem))); + } + + @Override + public List getOutputEntries() { + return List.of(EntryIngredient.of(EntryStacks.of(displayItem))); + } + + @Override + public CategoryIdentifier getCategoryIdentifier() { + return CategoryIdentifier.of(Identifier.of(SkyblockerMod.NAMESPACE, "skyblock_info")); + } + + @Override + public Optional getDisplayLocation() { + return Optional.empty(); + } + + @Override + public @Nullable DisplaySerializer getSerializer() { + return null; + } +} diff --git a/src/main/java/de/hysky/skyblocker/compatibility/rei/info/SkyblockInfoDisplayGenerator.java b/src/main/java/de/hysky/skyblocker/compatibility/rei/info/SkyblockInfoDisplayGenerator.java new file mode 100644 index 00000000..2b440220 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/compatibility/rei/info/SkyblockInfoDisplayGenerator.java @@ -0,0 +1,23 @@ +package de.hysky.skyblocker.compatibility.rei.info; + +import me.shedaniel.rei.api.client.registry.display.DynamicDisplayGenerator; +import me.shedaniel.rei.api.common.entry.EntryStack; +import net.minecraft.item.ItemStack; + +import java.util.List; +import java.util.Optional; + +public class SkyblockInfoDisplayGenerator implements DynamicDisplayGenerator { + @Override + public Optional> getRecipeFor(EntryStack entry) { + if (!(entry.getValue() instanceof ItemStack entryStack)) return Optional.empty(); + if (entryStack.getSkyblockId().isEmpty()) return Optional.empty(); + entryStack.setCount(1); + return Optional.of(List.of(new SkyblockInfoDisplay(entryStack))); + } + + @Override + public Optional> getUsageFor(EntryStack entry) { + return getRecipeFor(entry); + } +} diff --git a/src/main/java/de/hysky/skyblocker/compatibility/rei/recipe/SkyblockRecipeCategory.java b/src/main/java/de/hysky/skyblocker/compatibility/rei/recipe/SkyblockRecipeCategory.java new file mode 100644 index 00000000..167bafcf --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/compatibility/rei/recipe/SkyblockRecipeCategory.java @@ -0,0 +1,94 @@ +package de.hysky.skyblocker.compatibility.rei.recipe; + +import de.hysky.skyblocker.skyblock.itemlist.recipes.SkyblockRecipe; +import me.shedaniel.math.Point; +import me.shedaniel.math.Rectangle; +import me.shedaniel.rei.api.client.gui.Renderer; +import me.shedaniel.rei.api.client.gui.widgets.Widget; +import me.shedaniel.rei.api.client.gui.widgets.Widgets; +import me.shedaniel.rei.api.client.registry.display.DisplayCategory; +import me.shedaniel.rei.api.common.category.CategoryIdentifier; +import me.shedaniel.rei.api.common.util.EntryStacks; +import net.minecraft.client.gui.ScreenPos; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.item.ItemStack; +import net.minecraft.text.Text; +import net.minecraft.util.Identifier; + +import java.util.ArrayList; +import java.util.List; + +/** + * Skyblock recipe category class for REI + */ +public class SkyblockRecipeCategory implements DisplayCategory { + + private final Identifier identifier; + private final Text title; + private final ItemStack icon; + private final int height; + + public SkyblockRecipeCategory(Identifier identifier, Text title, ItemStack icon, int height) { + this.identifier = identifier; + this.title = title; + this.icon = icon; + this.height = height; + } + + @Override + public CategoryIdentifier getCategoryIdentifier() { + return CategoryIdentifier.of(identifier); + } + + @Override + public int getDisplayHeight() { + return height; + } + + @Override + public Text getTitle() { + return title; + } + + @Override + public Renderer getIcon() { + return EntryStacks.of(icon); + } + + /** + * Draws display for SkyblockCraftingDisplay + * + * @param display the display + * @param bounds the bounds of the display, configurable with overriding the width, height methods. + */ + @Override + public List setupDisplay(SkyblockRecipeDisplay display, Rectangle bounds) { + List out = new ArrayList<>(); + out.add(Widgets.createRecipeBase(bounds)); + SkyblockRecipe recipe = display.getRecipe(); + for (SkyblockRecipe.RecipeSlot inputSlot : recipe.getInputSlots(bounds.getWidth(), bounds.getHeight())) { + out.add(Widgets.createSlot(new Point(inputSlot.x() + bounds.getX(), inputSlot.y() + bounds.getY())) + .markInput() + .backgroundEnabled(inputSlot.showBackground()) + .entry(EntryStacks.of(inputSlot.stack()))); + } + for (SkyblockRecipe.RecipeSlot outputSlot : recipe.getOutputSlots(bounds.getWidth(), bounds.getHeight())) { + out.add(Widgets.createSlot(new Point(outputSlot.x() + bounds.getX(), outputSlot.y() + bounds.getY())) + .markOutput() + .backgroundEnabled(outputSlot.showBackground()) + .entry(EntryStacks.of(outputSlot.stack()))); + } + out.add(Widgets.createDrawableWidget((context, mouseX, mouseY, delta) -> { + MatrixStack matrices = context.getMatrices(); + matrices.push(); + matrices.translate(bounds.getX(), bounds.getY(), 0.f); + recipe.render(context, bounds.getWidth(), bounds.getHeight(), mouseX - bounds.getX(), mouseY - bounds.getY()); + matrices.pop(); + })); + ScreenPos arrowLocation = recipe.getArrowLocation(bounds.getWidth(), bounds.getHeight()); + if (arrowLocation != null) + out.add(Widgets.createArrow(new Point(arrowLocation.x() + bounds.getX(), arrowLocation.y() + bounds.getY()))); + out.add(Widgets.createLabel(new Point(bounds.getCenterX(), bounds.getCenterY() + 24), recipe.getExtraText())); + return out; + } +} diff --git a/src/main/java/de/hysky/skyblocker/compatibility/rei/recipe/SkyblockRecipeDisplay.java b/src/main/java/de/hysky/skyblocker/compatibility/rei/recipe/SkyblockRecipeDisplay.java new file mode 100644 index 00000000..640497c0 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/compatibility/rei/recipe/SkyblockRecipeDisplay.java @@ -0,0 +1,56 @@ +package de.hysky.skyblocker.compatibility.rei.recipe; + +import de.hysky.skyblocker.skyblock.itemlist.recipes.SkyblockRecipe; +import me.shedaniel.rei.api.common.category.CategoryIdentifier; +import me.shedaniel.rei.api.common.display.Display; +import me.shedaniel.rei.api.common.display.DisplaySerializer; +import me.shedaniel.rei.api.common.entry.EntryIngredient; +import me.shedaniel.rei.api.common.util.EntryStacks; +import net.minecraft.util.Identifier; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Optional; + +/** + * Skyblock Crafting Recipe display class for REI + */ +public class SkyblockRecipeDisplay implements Display { + + private final Identifier category; + private final SkyblockRecipe recipe; + + public SkyblockRecipeDisplay(SkyblockRecipe recipe) { + this.category = recipe.getCategoryIdentifier(); + this.recipe = recipe; + } + + @Override + public List getInputEntries() { + return recipe.getInputs().stream().map(EntryStacks::of).map(EntryIngredient::of).toList(); + } + + @Override + public List getOutputEntries() { + return recipe.getOutputs().stream().map(EntryStacks::of).map(EntryIngredient::of).toList(); + } + + public SkyblockRecipe getRecipe() { + return recipe; + } + + @Override + public CategoryIdentifier getCategoryIdentifier() { + return CategoryIdentifier.of(category); + } + + @Override + public Optional getDisplayLocation() { + return Optional.empty(); + } + + @Override + public @Nullable DisplaySerializer getSerializer() { + return null; + } +} diff --git a/src/main/java/de/hysky/skyblocker/compatibility/rei/recipe/SkyblockRecipeDisplayGenerator.java b/src/main/java/de/hysky/skyblocker/compatibility/rei/recipe/SkyblockRecipeDisplayGenerator.java new file mode 100644 index 00000000..6ac36288 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/compatibility/rei/recipe/SkyblockRecipeDisplayGenerator.java @@ -0,0 +1,24 @@ +package de.hysky.skyblocker.compatibility.rei.recipe; + +import de.hysky.skyblocker.skyblock.itemlist.ItemRepository; +import me.shedaniel.rei.api.client.registry.display.DynamicDisplayGenerator; +import me.shedaniel.rei.api.common.entry.EntryStack; +import net.minecraft.item.ItemStack; + +import java.util.List; +import java.util.Optional; + +public class SkyblockRecipeDisplayGenerator implements DynamicDisplayGenerator { + + @Override + public Optional> getRecipeFor(EntryStack entry) { + if (!(entry.getValue() instanceof ItemStack entryStack)) return Optional.empty(); + return Optional.of(ItemRepository.getRecipes(entryStack).map(SkyblockRecipeDisplay::new).toList()); + } + + @Override + public Optional> getUsageFor(EntryStack entry) { + if (!(entry.getValue() instanceof ItemStack entryStack)) return Optional.empty(); + return Optional.of(ItemRepository.getUsages(entryStack).map(SkyblockRecipeDisplay::new).toList()); + } +} -- cgit