From d32fee0cfcaa92e9a480931f29577be296b2b186 Mon Sep 17 00:00:00 2001 From: viciscat <51047087+viciscat@users.noreply.github.com> Date: Wed, 2 Jul 2025 23:13:13 +0200 Subject: Item list things and forge!!!!!! (#948) * item list shenanigans * emi, finish forge and recipe book * remove emi runtime * Refactor recipe * Render forge recipe duration * Multiple things Remove unused accessor Remove REI runtime Make EMI and recipe book call render method of a recipe * work with 1.21.3 * REI priority + config * requested changes and uh stuff ye * update REI * fix stuff maybe * fix wrapRenderer block clicks; lava bucket -> fred --------- Co-authored-by: Kevinthegreat <92656833+kevinthegreat1@users.noreply.github.com> Co-authored-by: Yasin <19829407+LifeIsAParadox@users.noreply.github.com> --- .../compatibility/emi/SkyblockEmiRecipe.java | 57 +++- .../compatibility/emi/SkyblockerEMIPlugin.java | 26 +- .../jei/SkyblockCraftingRecipeCategory.java | 2 +- .../compatibility/jei/SkyblockerJEIPlugin.java | 5 +- .../compatibility/rei/SkyblockCategory.java | 84 ----- .../compatibility/rei/SkyblockCraftingDisplay.java | 50 --- .../rei/SkyblockCraftingDisplayGenerator.java | 76 ----- .../compatibility/rei/SkyblockRecipeCategory.java | 94 ++++++ .../compatibility/rei/SkyblockRecipeDisplay.java | 56 ++++ .../rei/SkyblockRecipeDisplayGenerator.java | 27 ++ .../rei/SkyblockerREIClientPlugin.java | 24 +- .../skyblock/itemlist/ItemRepository.java | 57 ++-- .../skyblock/itemlist/SkyblockCraftingRecipe.java | 61 ---- .../recipebook/SkyblockCraftingRecipeResults.java | 332 ------------------ .../itemlist/recipebook/SkyblockCraftingTab.java | 2 +- .../recipebook/SkyblockRecipeBookWidget.java | 2 +- .../recipebook/SkyblockRecipeResultButton.java | 11 +- .../itemlist/recipebook/SkyblockRecipeResults.java | 373 +++++++++++++++++++++ .../itemlist/recipes/SkyblockCraftingRecipe.java | 89 +++++ .../itemlist/recipes/SkyblockForgeRecipe.java | 117 +++++++ .../skyblock/itemlist/recipes/SkyblockRecipe.java | 98 ++++++ .../java/de/hysky/skyblocker/utils/ItemUtils.java | 4 + .../de/hysky/skyblocker/utils/NEURepoManager.java | 2 + .../de/hysky/skyblocker/utils/SkyblockTime.java | 8 +- 24 files changed, 992 insertions(+), 665 deletions(-) delete mode 100644 src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockCategory.java delete mode 100644 src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockCraftingDisplay.java delete mode 100644 src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockCraftingDisplayGenerator.java create mode 100644 src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockRecipeCategory.java create mode 100644 src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockRecipeDisplay.java create mode 100644 src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockRecipeDisplayGenerator.java delete mode 100644 src/main/java/de/hysky/skyblocker/skyblock/itemlist/SkyblockCraftingRecipe.java delete mode 100644 src/main/java/de/hysky/skyblocker/skyblock/itemlist/recipebook/SkyblockCraftingRecipeResults.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/itemlist/recipebook/SkyblockRecipeResults.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/itemlist/recipes/SkyblockCraftingRecipe.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/itemlist/recipes/SkyblockForgeRecipe.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/itemlist/recipes/SkyblockRecipe.java (limited to 'src/main/java') diff --git a/src/main/java/de/hysky/skyblocker/compatibility/emi/SkyblockEmiRecipe.java b/src/main/java/de/hysky/skyblocker/compatibility/emi/SkyblockEmiRecipe.java index 7f102fc1..281867f1 100644 --- a/src/main/java/de/hysky/skyblocker/compatibility/emi/SkyblockEmiRecipe.java +++ b/src/main/java/de/hysky/skyblocker/compatibility/emi/SkyblockEmiRecipe.java @@ -1,35 +1,70 @@ package de.hysky.skyblocker.compatibility.emi; -import de.hysky.skyblocker.skyblock.itemlist.SkyblockCraftingRecipe; -import dev.emi.emi.api.recipe.EmiCraftingRecipe; +import de.hysky.skyblocker.skyblock.itemlist.recipes.SkyblockRecipe; +import dev.emi.emi.api.recipe.EmiRecipe; import dev.emi.emi.api.recipe.EmiRecipeCategory; +import dev.emi.emi.api.render.EmiTexture; import dev.emi.emi.api.stack.EmiIngredient; import dev.emi.emi.api.stack.EmiStack; import dev.emi.emi.api.widget.WidgetHolder; import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.ScreenPos; import net.minecraft.text.Text; +import net.minecraft.util.Identifier; +import org.jetbrains.annotations.Nullable; -public class SkyblockEmiRecipe extends EmiCraftingRecipe { - private final String craftText; +import java.util.List; - public SkyblockEmiRecipe(SkyblockCraftingRecipe recipe) { - super(recipe.getGrid().stream().map(EmiStack::of).map(EmiIngredient.class::cast).toList(), EmiStack.of(recipe.getResult()), recipe.getId()); - this.craftText = recipe.getCraftText(); +public class SkyblockEmiRecipe implements EmiRecipe { + private final Text craftText; + private final SkyblockRecipe recipe; + + public SkyblockEmiRecipe(SkyblockRecipe recipe) { + this.craftText = recipe.getExtraText(); + this.recipe = recipe; } @Override public EmiRecipeCategory getCategory() { - return SkyblockerEMIPlugin.SKYBLOCK; + return SkyblockerEMIPlugin.IDENTIFIER_CATEGORY_MAP.get(recipe.getCategoryIdentifier()); + } + + @Override + public @Nullable Identifier getId() { + return recipe.getRecipeIdentifier(); + } + + @Override + public List getInputs() { + return recipe.getInputs().stream().map(EmiStack::of).map(EmiIngredient.class::cast).toList(); + } + + @Override + public List getOutputs() { + return recipe.getOutputs().stream().map(EmiStack::of).toList(); + } + + @Override + public int getDisplayWidth() { + return 118; } @Override public int getDisplayHeight() { - return super.getDisplayHeight() + (craftText.isEmpty() ? 0 : 10); + return 54 + (craftText.getString().isEmpty() ? 0 : 10); } @Override public void addWidgets(WidgetHolder widgets) { - super.addWidgets(widgets); - widgets.addText(Text.of(craftText), 59 - MinecraftClient.getInstance().textRenderer.getWidth(craftText) / 2, 55, 0xFFFFFF, true); + ScreenPos arrowLocation = recipe.getArrowLocation(getDisplayWidth(), getDisplayHeight()); + if (arrowLocation != null) widgets.addTexture(EmiTexture.EMPTY_ARROW, arrowLocation.x(), arrowLocation.y()); + widgets.addText(craftText, 59 - MinecraftClient.getInstance().textRenderer.getWidth(craftText) / 2, 55, 0xFFFFFF, true); + for (SkyblockRecipe.RecipeSlot inputSlot : recipe.getInputSlots(getDisplayWidth(), getDisplayHeight())) { + widgets.addSlot(EmiStack.of(inputSlot.stack()), inputSlot.x(), inputSlot.y()); + } + for (SkyblockRecipe.RecipeSlot outputSlot : recipe.getOutputSlots(getDisplayWidth(), getDisplayHeight())) { + widgets.addSlot(EmiStack.of(outputSlot.stack()), outputSlot.x(), outputSlot.y()).recipeContext(this); + } + widgets.addDrawable(0, 0, getDisplayWidth(), getDisplayHeight(), (draw, mouseX, mouseY, delta) -> recipe.render(draw, getDisplayWidth(), getDisplayHeight(), mouseX, mouseY)); } } diff --git a/src/main/java/de/hysky/skyblocker/compatibility/emi/SkyblockerEMIPlugin.java b/src/main/java/de/hysky/skyblocker/compatibility/emi/SkyblockerEMIPlugin.java index d8d2d28c..2baed2f2 100644 --- a/src/main/java/de/hysky/skyblocker/compatibility/emi/SkyblockerEMIPlugin.java +++ b/src/main/java/de/hysky/skyblocker/compatibility/emi/SkyblockerEMIPlugin.java @@ -4,7 +4,8 @@ import de.hysky.skyblocker.SkyblockerMod; import de.hysky.skyblocker.config.SkyblockerConfigManager; import de.hysky.skyblocker.mixins.accessors.HandledScreenAccessor; import de.hysky.skyblocker.skyblock.itemlist.ItemRepository; -import de.hysky.skyblocker.utils.ItemUtils; +import de.hysky.skyblocker.skyblock.itemlist.recipes.SkyblockCraftingRecipe; +import de.hysky.skyblocker.skyblock.itemlist.recipes.SkyblockForgeRecipe; import de.hysky.skyblocker.utils.Location; import de.hysky.skyblocker.utils.Utils; import dev.emi.emi.api.EmiPlugin; @@ -18,22 +19,33 @@ import net.minecraft.client.gui.screen.ingame.InventoryScreen; import net.minecraft.item.Items; import net.minecraft.util.Identifier; +import java.util.Map; + /** * EMI integration */ public class SkyblockerEMIPlugin implements EmiPlugin { - public static final Identifier SIMPLIFIED_TEXTURES = Identifier.of("emi", "textures/gui/widgets.png"); - // TODO: Custom simplified texture for Skyblock - public static final EmiRecipeCategory SKYBLOCK = new EmiRecipeCategory(Identifier.of(SkyblockerMod.NAMESPACE, "skyblock"), EmiStack.of(ItemUtils.getSkyblockerStack()), new EmiTexture(SIMPLIFIED_TEXTURES, 240, 240, 16, 16)); + public static final Identifier SIMPLIFIED_TEXTURES = Identifier.of(SkyblockerMod.NAMESPACE, "textures/gui/emi_icons.png"); + + public static final EmiRecipeCategory SKYBLOCK_CRAFTING = new EmiRecipeCategory(SkyblockCraftingRecipe.IDENTIFIER, EmiStack.of(Items.CRAFTING_TABLE), new EmiTexture(SIMPLIFIED_TEXTURES, 0, 0, 16, 16)); + public static final EmiRecipeCategory SKYBLOCK_FORGE = new EmiRecipeCategory(SkyblockForgeRecipe.IDENTIFIER, EmiStack.of(Items.LAVA_BUCKET), new EmiTexture(SIMPLIFIED_TEXTURES, 16, 0, 16, 16)); + + protected static final Map IDENTIFIER_CATEGORY_MAP = Map.of( + SkyblockCraftingRecipe.IDENTIFIER, SKYBLOCK_CRAFTING, + SkyblockForgeRecipe.IDENTIFIER, SKYBLOCK_FORGE + ); @Override public void register(EmiRegistry registry) { + if (!SkyblockerConfigManager.get().general.itemList.enableItemList) return; ItemRepository.getItemsStream().map(EmiStack::of).forEach(emiStack -> { registry.addEmiStack(emiStack); - registry.setDefaultComparison(emiStack, Comparison.compareComponents()); + registry.setDefaultComparison(emiStack, Comparison.compareData(emiStack1 -> emiStack1.getItemStack().getSkyblockId())); }); - registry.addCategory(SKYBLOCK); - registry.addWorkstation(SKYBLOCK, EmiStack.of(Items.CRAFTING_TABLE)); + registry.addCategory(SKYBLOCK_CRAFTING); + registry.addCategory(SKYBLOCK_FORGE); + registry.addWorkstation(SKYBLOCK_CRAFTING, EmiStack.of(Items.CRAFTING_TABLE)); + registry.addWorkstation(SKYBLOCK_CRAFTING, EmiStack.of(Items.LAVA_BUCKET)); ItemRepository.getRecipesStream().map(SkyblockEmiRecipe::new).forEach(registry::addRecipe); registry.addExclusionArea(InventoryScreen.class, (screen, consumer) -> { if (!SkyblockerConfigManager.get().farming.garden.gardenPlotsWidget || !Utils.getLocation().equals(Location.GARDEN)) return; diff --git a/src/main/java/de/hysky/skyblocker/compatibility/jei/SkyblockCraftingRecipeCategory.java b/src/main/java/de/hysky/skyblocker/compatibility/jei/SkyblockCraftingRecipeCategory.java index c08155a5..edafd7a6 100644 --- a/src/main/java/de/hysky/skyblocker/compatibility/jei/SkyblockCraftingRecipeCategory.java +++ b/src/main/java/de/hysky/skyblocker/compatibility/jei/SkyblockCraftingRecipeCategory.java @@ -15,7 +15,7 @@ import org.jetbrains.annotations.NotNull; public class SkyblockCraftingRecipeCategory extends CraftingRecipeCategory { @SuppressWarnings({"unchecked", "RedundantCast", "rawtypes"}) private static final RecipeType> SKYBLOCK_RECIPE = new RecipeType<>(Identifier.of(SkyblockerMod.NAMESPACE, "skyblock"), (Class>) (Class) RecipeEntry.class); - private final Text title = Text.translatable("emi.category.skyblocker.skyblock"); + private final Text title = Text.translatable("emi.category.skyblocker.skyblock_crafting"); private final IDrawable icon; public SkyblockCraftingRecipeCategory(IGuiHelper guiHelper) { diff --git a/src/main/java/de/hysky/skyblocker/compatibility/jei/SkyblockerJEIPlugin.java b/src/main/java/de/hysky/skyblocker/compatibility/jei/SkyblockerJEIPlugin.java index cf35208b..9b75bd77 100644 --- a/src/main/java/de/hysky/skyblocker/compatibility/jei/SkyblockerJEIPlugin.java +++ b/src/main/java/de/hysky/skyblocker/compatibility/jei/SkyblockerJEIPlugin.java @@ -4,6 +4,7 @@ import de.hysky.skyblocker.SkyblockerMod; import de.hysky.skyblocker.config.SkyblockerConfigManager; import de.hysky.skyblocker.mixins.accessors.HandledScreenAccessor; import de.hysky.skyblocker.skyblock.itemlist.ItemRepository; +import de.hysky.skyblocker.skyblock.itemlist.recipes.SkyblockCraftingRecipe; import de.hysky.skyblocker.utils.Location; import de.hysky.skyblocker.utils.Utils; import de.hysky.skyblocker.utils.datafixer.ItemStackComponentizationFixer; @@ -64,8 +65,8 @@ public class SkyblockerJEIPlugin implements IModPlugin { public void registerRecipes(@NotNull IRecipeRegistration registration) { //FIXME no clue what to replace any of this with, we can't use items as that does not work /*registration.getIngredientManager().addIngredientsAtRuntime(VanillaTypes.ITEM_STACK, ItemRepository.getItems()); - registration.addRecipes(skyblockCraftingRecipeCategory.getRecipeType(), ItemRepository.getRecipesStream().map(recipe -> - new RecipeEntry(recipe.getId(), new ShapedRecipe("", CraftingRecipeCategory.MISC, RawShapedRecipe.create(Map.of( + registration.addRecipes(skyblockCraftingRecipeCategory.getRecipeType(), ItemRepository.getRecipesStream().filter(skyblockRecipe -> skyblockRecipe instanceof SkyblockCraftingRecipe).map(SkyblockCraftingRecipe.class::cast).map(recipe -> + new RecipeEntry(recipe.getRecipeIdentifier(), new ShapedRecipe("", CraftingRecipeCategory.MISC, RawShapedRecipe.create(Map.of( 'a', Ingredient.ofStacks(recipe.getGrid().get(0)), 'b', Ingredient.ofStacks(recipe.getGrid().get(1)), 'c', Ingredient.ofStacks(recipe.getGrid().get(2)), diff --git a/src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockCategory.java b/src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockCategory.java deleted file mode 100644 index 1474c8be..00000000 --- a/src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockCategory.java +++ /dev/null @@ -1,84 +0,0 @@ -package de.hysky.skyblocker.compatibility.rei; - -import com.google.common.collect.Lists; -import de.hysky.skyblocker.utils.ItemUtils; -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.Label; -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.EntryIngredient; -import me.shedaniel.rei.api.common.util.EntryStacks; -import net.minecraft.text.Text; - -import java.util.ArrayList; -import java.util.List; - -/** - * Skyblock recipe category class for REI - */ -public class SkyblockCategory implements DisplayCategory { - @Override - public CategoryIdentifier getCategoryIdentifier() { - return SkyblockerREIClientPlugin.SKYBLOCK; - } - - @Override - public Text getTitle() { - return Text.translatable("emi.category.skyblocker.skyblock"); - } - - @Override - public Renderer getIcon() { - return EntryStacks.of(ItemUtils.getSkyblockerStack()); - } - - @Override - public int getDisplayHeight() { - return 73; - } - - /** - * 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(SkyblockCraftingDisplay display, Rectangle bounds) { - List out = new ArrayList<>(); - out.add(Widgets.createRecipeBase(bounds)); - - Point startPoint; - if (!display.getCraftText().isEmpty() && display.getCraftText() != null) { - startPoint = new Point(bounds.getCenterX() - 58, bounds.getCenterY() - 31); - } - else { - startPoint = new Point(bounds.getCenterX() - 58, bounds.getCenterY() - 26); - } - Point resultPoint = new Point(startPoint.x + 95, startPoint.y + 19); - out.add(Widgets.createArrow(new Point(startPoint.x + 60, startPoint.y + 18))); - out.add(Widgets.createResultSlotBackground(resultPoint)); - - // Generate Slots - List input = display.getInputEntries(); - List slots = Lists.newArrayList(); - for (int y = 0; y < 3; y++) - for (int x = 0; x < 3; x++) - slots.add(Widgets.createSlot(new Point(startPoint.x + 1 + x * 18, startPoint.y + 1 + y * 18)).markInput()); - for (int i = 0; i < input.size(); i++) { - slots.get(i).entries(input.get(i)).markInput(); - } - out.addAll(slots); - out.add(Widgets.createSlot(resultPoint).entries(display.getOutputEntries().getFirst()).disableBackground().markOutput()); - - // Add craftingText Label - Label craftTextLabel = Widgets.createLabel(new Point(bounds.getCenterX(), startPoint.y + 55), Text.of(display.getCraftText())); - out.add(craftTextLabel); - return out; - } -} diff --git a/src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockCraftingDisplay.java b/src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockCraftingDisplay.java deleted file mode 100644 index 3f4d7dd4..00000000 --- a/src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockCraftingDisplay.java +++ /dev/null @@ -1,50 +0,0 @@ -package de.hysky.skyblocker.compatibility.rei; - - -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.display.SimpleGridMenuDisplay; -import me.shedaniel.rei.api.common.display.basic.BasicDisplay; -import me.shedaniel.rei.api.common.entry.EntryIngredient; - -import java.util.List; - -import org.jetbrains.annotations.Nullable; - -/** - * Skyblock Crafting Recipe display class for REI - */ -public class SkyblockCraftingDisplay extends BasicDisplay implements SimpleGridMenuDisplay { - private final String craftText; - - public SkyblockCraftingDisplay(List input, List output, String craftText) { - super(input, output); - this.craftText = craftText; - } - - public String getCraftText() { - return craftText; - } - - @Override - public int getWidth() { - return 3; - } - - @Override - public int getHeight() { - return 3; - } - - @Override - public CategoryIdentifier getCategoryIdentifier() { - return SkyblockerREIClientPlugin.SKYBLOCK; - } - - @Override - @Nullable - public DisplaySerializer getSerializer() { - return null; - } -} \ No newline at end of file diff --git a/src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockCraftingDisplayGenerator.java b/src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockCraftingDisplayGenerator.java deleted file mode 100644 index f3ce5532..00000000 --- a/src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockCraftingDisplayGenerator.java +++ /dev/null @@ -1,76 +0,0 @@ -package de.hysky.skyblocker.compatibility.rei; - -import de.hysky.skyblocker.skyblock.itemlist.ItemRepository; -import de.hysky.skyblocker.skyblock.itemlist.SkyblockCraftingRecipe; -import de.hysky.skyblocker.utils.ItemUtils; -import me.shedaniel.rei.api.client.registry.display.DynamicDisplayGenerator; -import me.shedaniel.rei.api.common.entry.EntryIngredient; -import me.shedaniel.rei.api.common.entry.EntryStack; -import me.shedaniel.rei.api.common.util.EntryStacks; -import net.minecraft.item.ItemStack; - -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; - -public class SkyblockCraftingDisplayGenerator implements DynamicDisplayGenerator { - - @Override - public Optional> getRecipeFor(EntryStack entry) { - if (!(entry.getValue() instanceof ItemStack)) return Optional.empty(); - EntryStack inputItem = EntryStacks.of((ItemStack) entry.getValue()); - List filteredRecipes = ItemRepository.getRecipesStream() - .filter(recipe -> { - ItemStack itemStack = inputItem.getValue(); - ItemStack itemStack1 = recipe.getResult(); - return ItemUtils.getItemId(itemStack1).equals(ItemUtils.getItemId(itemStack)); - }) - .toList(); - - return Optional.of(generateDisplays(filteredRecipes)); - } - - @Override - public Optional> getUsageFor(EntryStack entry) { - if (!(entry.getValue() instanceof ItemStack)) return Optional.empty(); - EntryStack inputItem = EntryStacks.of((ItemStack) entry.getValue()); - List filteredRecipes = ItemRepository.getRecipesStream() - .filter(recipe -> { - for (ItemStack item : recipe.getGrid()) { - if(!ItemUtils.getItemId(item).isEmpty()) { - ItemStack itemStack = inputItem.getValue(); - if (ItemUtils.getItemId(item).equals(ItemUtils.getItemId(itemStack))) return true; - } - } - return false; - }) - .toList(); - return Optional.of(generateDisplays(filteredRecipes)); - } - - /** - * Generate Displays from a list of recipes - */ - private List generateDisplays(List recipes) { - List displays = new ArrayList<>(); - for (SkyblockCraftingRecipe recipe : recipes) { - List inputs = new ArrayList<>(); - List outputs = new ArrayList<>(); - - ArrayList> inputEntryStacks = new ArrayList<>(); - recipe.getGrid().forEach((item) -> inputEntryStacks.add(EntryStacks.of(item))); - - for (EntryStack entryStack : inputEntryStacks) { - if (entryStack.isEmpty()) { - inputs.add(EntryIngredient.empty()); - } else { - inputs.add(EntryIngredient.of(entryStack)); - } - } - outputs.add(EntryIngredient.of(EntryStacks.of(recipe.getResult()))); - - displays.add(new SkyblockCraftingDisplay(inputs, outputs, recipe.getCraftText())); - } - return displays; - } -} diff --git a/src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockRecipeCategory.java b/src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockRecipeCategory.java new file mode 100644 index 00000000..0460a1cf --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockRecipeCategory.java @@ -0,0 +1,94 @@ +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 new file mode 100644 index 00000000..44d44011 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockRecipeDisplay.java @@ -0,0 +1,56 @@ +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 new file mode 100644 index 00000000..fa4787df --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockRecipeDisplayGenerator.java @@ -0,0 +1,27 @@ +package de.hysky.skyblocker.compatibility.rei; + +import de.hysky.skyblocker.skyblock.itemlist.ItemRepository; +import de.hysky.skyblocker.utils.ItemUtils; +import de.hysky.skyblocker.utils.NEURepoManager; +import me.shedaniel.rei.api.client.registry.display.DynamicDisplayGenerator; +import me.shedaniel.rei.api.common.entry.EntryStack; +import me.shedaniel.rei.api.common.entry.type.VanillaEntryTypes; +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/SkyblockerREIClientPlugin.java b/src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockerREIClientPlugin.java index 7b1f7148..d4c9fe8f 100644 --- a/src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockerREIClientPlugin.java +++ b/src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockerREIClientPlugin.java @@ -1,10 +1,12 @@ package de.hysky.skyblocker.compatibility.rei; -import de.hysky.skyblocker.SkyblockerMod; import de.hysky.skyblocker.config.SkyblockerConfigManager; import de.hysky.skyblocker.mixins.accessors.HandledScreenAccessor; import de.hysky.skyblocker.skyblock.garden.visitor.VisitorHelper; import de.hysky.skyblocker.skyblock.itemlist.ItemRepository; +import de.hysky.skyblocker.skyblock.itemlist.recipes.SkyblockCraftingRecipe; +import de.hysky.skyblocker.skyblock.itemlist.recipes.SkyblockForgeRecipe; +import de.hysky.skyblocker.utils.ItemUtils; import de.hysky.skyblocker.utils.Location; import de.hysky.skyblocker.utils.Utils; import me.shedaniel.math.Rectangle; @@ -15,9 +17,11 @@ import me.shedaniel.rei.api.client.registry.entry.EntryRegistry; import me.shedaniel.rei.api.client.registry.screen.ExclusionZones; import me.shedaniel.rei.api.common.category.CategoryIdentifier; import me.shedaniel.rei.api.common.util.EntryStacks; +import net.minecraft.item.ItemStack; import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.gui.screen.ingame.InventoryScreen; import net.minecraft.item.Items; +import net.minecraft.text.Text; import java.util.List; @@ -25,21 +29,26 @@ import java.util.List; * REI integration */ public class SkyblockerREIClientPlugin implements REIClientPlugin { - public static final CategoryIdentifier SKYBLOCK = CategoryIdentifier.of(SkyblockerMod.NAMESPACE, "skyblock"); @Override public void registerCategories(CategoryRegistry categoryRegistry) { - categoryRegistry.addWorkstations(SKYBLOCK, EntryStacks.of(Items.CRAFTING_TABLE)); - categoryRegistry.add(new SkyblockCategory()); + 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)); } @Override public void registerDisplays(DisplayRegistry displayRegistry) { - displayRegistry.registerDisplayGenerator(SKYBLOCK, new SkyblockCraftingDisplayGenerator()); + if (!SkyblockerConfigManager.get().general.itemList.enableItemList) return; + displayRegistry.registerGlobalDisplayGenerator(new SkyblockRecipeDisplayGenerator()); } @Override public void registerEntries(EntryRegistry entryRegistry) { + if (!SkyblockerConfigManager.get().general.itemList.enableItemList) return; + entryRegistry.removeEntryIf(entryStack -> true); entryRegistry.addEntries(ItemRepository.getItemsStream().map(EntryStacks::of).toList()); } @@ -56,4 +65,9 @@ public class SkyblockerREIClientPlugin implements REIClientPlugin { return VisitorHelper.getExclusionZones(); }); } + + @Override + public double getPriority() { + return 4096; + } } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/itemlist/ItemRepository.java b/src/main/java/de/hysky/skyblocker/skyblock/itemlist/ItemRepository.java index 5572e030..4936c8f2 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/itemlist/ItemRepository.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/itemlist/ItemRepository.java @@ -1,21 +1,20 @@ package de.hysky.skyblocker.skyblock.itemlist; import de.hysky.skyblocker.annotations.Init; +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.skyblock.item.tooltip.ItemTooltip; +import de.hysky.skyblocker.skyblock.itemlist.recipes.SkyblockCraftingRecipe; +import de.hysky.skyblocker.skyblock.itemlist.recipes.SkyblockForgeRecipe; +import de.hysky.skyblocker.skyblock.itemlist.recipes.SkyblockRecipe; import de.hysky.skyblocker.utils.ItemUtils; import de.hysky.skyblocker.utils.NEURepoManager; -import io.github.moulberry.repo.data.NEUCraftingRecipe; -import io.github.moulberry.repo.data.NEUItem; -import io.github.moulberry.repo.data.NEURecipe; +import io.github.moulberry.repo.data.*; import net.minecraft.item.ItemStack; -import net.minecraft.item.Items; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.stream.Stream; public class ItemRepository { @@ -23,7 +22,7 @@ public class ItemRepository { private static final List items = new ArrayList<>(); private static final Map itemsMap = new HashMap<>(); - private static final List recipes = new ArrayList<>(); + private static final List recipes = new ArrayList<>(); private static boolean filesImported = false; @Init @@ -64,11 +63,7 @@ public class ItemRepository { } private static void loadRecipes(NEUItem item) { - for (NEURecipe recipe : item.getRecipes()) { - if (recipe instanceof NEUCraftingRecipe neuCraftingRecipe) { - recipes.add(SkyblockCraftingRecipe.fromNEURecipe(neuCraftingRecipe)); - } - } + item.getRecipes().stream().map(ItemRepository::toSkyblockRecipe).filter(Objects::nonNull).forEach(recipes::add); } public static String getWikiLink(String neuId, boolean useOfficial) { @@ -89,20 +84,8 @@ public class ItemRepository { return null; } - public static List getRecipes(String neuId) { - List result = new ArrayList<>(); - for (SkyblockCraftingRecipe recipe : recipes) { - if (ItemUtils.getItemId(recipe.getResult()).equals(neuId)) result.add(recipe); - } - for (SkyblockCraftingRecipe recipe : recipes) { - for (ItemStack ingredient : recipe.getGrid()) { - if (!ingredient.getItem().equals(Items.AIR) && ItemUtils.getItemId(ingredient).equals(neuId)) { - result.add(recipe); - break; - } - } - } - return result; + public static List getRecipesAndUsages(ItemStack stack) { + return Stream.concat(getRecipes(stack), getUsages(stack)).toList(); } public static boolean filesImported() { @@ -130,8 +113,22 @@ public class ItemRepository { return itemsMap.get(neuId); } - public static Stream getRecipesStream() { - return recipes.stream(); + public static Stream getRecipesStream() {return recipes.stream(); } + + public static Stream getRecipes(ItemStack stack) { + return NEURepoManager.RECIPE_CACHE.getRecipes().getOrDefault(stack.getNeuName(), Set.of()).stream().map(ItemRepository::toSkyblockRecipe).filter(Objects::nonNull); + } + + public static Stream getUsages(ItemStack stack) { + return NEURepoManager.RECIPE_CACHE.getUsages().getOrDefault(stack.getNeuName(), Set.of()).stream().map(ItemRepository::toSkyblockRecipe).filter(Objects::nonNull); + } + + private static SkyblockRecipe toSkyblockRecipe(NEURecipe neuRecipe) { + return switch (neuRecipe) { + case NEUCraftingRecipe craftingRecipe -> new SkyblockCraftingRecipe(craftingRecipe); + case NEUForgeRecipe forgeRecipe -> new SkyblockForgeRecipe(forgeRecipe); + case null, default -> null; + }; } } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/itemlist/SkyblockCraftingRecipe.java b/src/main/java/de/hysky/skyblocker/skyblock/itemlist/SkyblockCraftingRecipe.java deleted file mode 100644 index bc6d7977..00000000 --- a/src/main/java/de/hysky/skyblocker/skyblock/itemlist/SkyblockCraftingRecipe.java +++ /dev/null @@ -1,61 +0,0 @@ -package de.hysky.skyblocker.skyblock.itemlist; - -import de.hysky.skyblocker.utils.ItemUtils; -import io.github.moulberry.repo.data.NEUCraftingRecipe; -import io.github.moulberry.repo.data.NEUIngredient; -import net.minecraft.item.ItemStack; -import net.minecraft.item.Items; -import net.minecraft.util.Identifier; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.ArrayList; -import java.util.List; - -public class SkyblockCraftingRecipe { - private static final Logger LOGGER = LoggerFactory.getLogger(SkyblockCraftingRecipe.class); - private final String craftText; - private final List grid = new ArrayList<>(9); - private ItemStack result; - - public SkyblockCraftingRecipe(String craftText) { - this.craftText = craftText; - } - - public static SkyblockCraftingRecipe fromNEURecipe(NEUCraftingRecipe neuCraftingRecipe) { - SkyblockCraftingRecipe recipe = new SkyblockCraftingRecipe(neuCraftingRecipe.getExtraText() != null ? neuCraftingRecipe.getExtraText() : ""); - for (NEUIngredient input : neuCraftingRecipe.getInputs()) { - recipe.grid.add(getItemStack(input)); - } - recipe.result = getItemStack(neuCraftingRecipe.getOutput()); - return recipe; - } - - private static ItemStack getItemStack(NEUIngredient input) { - if (input != NEUIngredient.SENTINEL_EMPTY) { - ItemStack stack = ItemRepository.getItemStack(input.getItemId()); - if (stack != null) { - return stack.copyWithCount((int) input.getAmount()); - } else { - LOGGER.warn("[Skyblocker Recipe] Unable to find item {}", input.getItemId()); - } - } - return Items.AIR.getDefaultStack(); - } - - public List getGrid() { - return grid; - } - - public ItemStack getResult() { - return result; - } - - public String getCraftText() { - return craftText; - } - - public Identifier getId() { - return Identifier.of("skyblock", ItemUtils.getItemId(getResult()).toLowerCase().replace(';', '_') + "_" + getResult().getCount()); - } -} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/itemlist/recipebook/SkyblockCraftingRecipeResults.java b/src/main/java/de/hysky/skyblocker/skyblock/itemlist/recipebook/SkyblockCraftingRecipeResults.java deleted file mode 100644 index 367bcd31..00000000 --- a/src/main/java/de/hysky/skyblocker/skyblock/itemlist/recipebook/SkyblockCraftingRecipeResults.java +++ /dev/null @@ -1,332 +0,0 @@ -package de.hysky.skyblocker.skyblock.itemlist.recipebook; - -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; - -import com.google.common.collect.Lists; - -import de.hysky.skyblocker.config.SkyblockerConfigManager; -import de.hysky.skyblocker.skyblock.item.WikiLookup; -import de.hysky.skyblocker.skyblock.itemlist.ItemRepository; -import de.hysky.skyblocker.skyblock.itemlist.SkyblockCraftingRecipe; -import de.hysky.skyblocker.utils.ItemUtils; -import de.hysky.skyblocker.utils.render.RenderHelper; -import de.hysky.skyblocker.utils.scheduler.MessageScheduler; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.font.TextRenderer; -import net.minecraft.client.gui.DrawContext; -import net.minecraft.client.gui.screen.recipebook.RecipeBookResults; -import net.minecraft.client.gui.widget.ToggleButtonWidget; -import net.minecraft.component.DataComponentTypes; -import net.minecraft.item.ItemStack; -import net.minecraft.screen.ScreenTexts; -import net.minecraft.text.OrderedText; -import net.minecraft.text.StringVisitable; -import net.minecraft.text.Text; -import net.minecraft.util.Identifier; -import net.minecraft.util.Language; - -//TODO when in recipe view set search hint to talk about close or smth -/** - * Based off {@link net.minecraft.client.gui.screen.recipebook.RecipeBookResults}. - */ -public class SkyblockCraftingRecipeResults implements RecipeAreaDisplay { - /** - * The width before text will go outside of the recipe book area. - */ - private static final int MAX_TEXT_WIDTH = 124; - private static final String ELLIPSIS_STRING = ScreenTexts.ELLIPSIS.getString(); - - private final List resultButtons = Lists.newArrayListWithCapacity(20); - private MinecraftClient client; - private ToggleButtonWidget nextPageButton; - private ToggleButtonWidget prevPageButton; - private SkyblockRecipeResultButton hoveredResultButton; - private String lastSearchQuery = null; - private final List searchResults = new ArrayList<>(); - /** - * Text to be displayed as a tooltip. - */ - private Text hoveredText; - private List recipeResults = new ArrayList<>(); - private int pageCount = 0; - private int currentPage = 0; - /** - * Whether we are showing the recipe for an item (true) or just showing the search results (false). - */ - private boolean recipeView = false; - - protected SkyblockCraftingRecipeResults() { - for (int i = 0; i < 20; i++) { - this.resultButtons.add(new SkyblockRecipeResultButton()); - } - } - - @Override - public void initialize(MinecraftClient client, int parentLeft, int parentTop) { - this.client = client; - - //Position the result buttons - for (int i = 0; i < resultButtons.size(); i++) { - this.resultButtons.get(i).setPosition(parentLeft + 11 + 25 * (i % 5), parentTop + 31 + 25 * (i / 5)); - } - - //Setup & position the page flip buttons - this.nextPageButton = new ToggleButtonWidget(parentLeft + 93, parentTop + 137, 12, 17, false); - this.nextPageButton.setTextures(RecipeBookResults.PAGE_FORWARD_TEXTURES); - this.prevPageButton = new ToggleButtonWidget(parentLeft + 38, parentTop + 137, 12, 17, true); - this.prevPageButton.setTextures(RecipeBookResults.PAGE_BACKWARD_TEXTURES); - } - - @Override - public void draw(DrawContext context, int x, int y, int mouseX, int mouseY, float delta) { - TextRenderer textRenderer = this.client.textRenderer; - - //Reset the hovered text - this.hoveredText = null; - - //If we have selected an item to view recipes for then show the recipe view specific stuff (e.g. name, requirement) - if (this.recipeView) { - drawRecipeDisplay(context, textRenderer, x, y, mouseX, mouseY); - } - - //Render the page count - if (this.pageCount > 1) { - Text text = Text.translatable("gui.recipebook.page", this.currentPage + 1, this.pageCount); - int width = textRenderer.getWidth(text); - - context.drawText(textRenderer, text, x - width / 2 + 73, y + 141, -1, false); - } - - //Render the results - this.hoveredResultButton = null; - - for (SkyblockRecipeResultButton resultButton : resultButtons) { - resultButton.render(context, mouseX, mouseY, delta); - - if (resultButton.visible && resultButton.isSelected()) this.hoveredResultButton = resultButton; - } - - //Render the page flip buttons - if (this.prevPageButton.active) this.prevPageButton.render(context, mouseX, mouseY, delta); - if (this.nextPageButton.active) this.nextPageButton.render(context, mouseX, mouseY, delta); - } - - //TODO enable scissor? - private void drawRecipeDisplay(DrawContext context, TextRenderer textRenderer, int x, int y, int mouseX, int mouseY) { - //Render the "Craft Text" which is usually a requirement (e.g. Wolf Slayer 7) - String craftText = this.recipeResults.get(this.currentPage).getCraftText(); - - if (!craftText.isEmpty()) { - if (textRenderer.getWidth(craftText) > MAX_TEXT_WIDTH) { - //Set craft text as hovered text if we're hovering over it since it got truncated - if (isMouseHoveringText(x + 11, y + 31, mouseX, mouseY)) this.hoveredText = Text.literal(craftText); - - craftText = textRenderer.trimToWidth(craftText, MAX_TEXT_WIDTH) + ELLIPSIS_STRING; - } - - context.drawTextWithShadow(textRenderer, craftText, x + 11, y + 31, 0xffffffff); - } - - //Render the resulting item's name - Text itemName = this.recipeResults.get(this.currentPage).getResult().getName(); - - if (textRenderer.getWidth(itemName) > MAX_TEXT_WIDTH) { - StringVisitable trimmed = StringVisitable.concat(textRenderer.trimToWidth(itemName, MAX_TEXT_WIDTH), ScreenTexts.ELLIPSIS); - OrderedText ordered = Language.getInstance().reorder(trimmed); - - context.drawTextWithShadow(textRenderer, ordered, x + 11, y + 43, 0xffffffff); - - //Set the resulting item's name as hovered text if we're hovering over it since the text got truncated - if (isMouseHoveringText(x + 11, y + 43, mouseX, mouseY)) this.hoveredText = itemName; - } else { - context.drawTextWithShadow(textRenderer, itemName, x + 11, y + 43, 0xffffffff); - } - - //Draw the arrow that points to the recipe's result - context.drawTextWithShadow(textRenderer, "▶", x + 96, y + 90, 0xaaffffff); - } - - @Override - public void drawTooltip(DrawContext context, int x, int y) { - if (this.client.currentScreen != null) { - //Draw the tooltip of the hovered result button if one is hovered over - if (this.hoveredResultButton != null && !this.hoveredResultButton.getDisplayStack().isEmpty()) { - ItemStack stack = this.hoveredResultButton.getDisplayStack(); - Identifier tooltipStyle = stack.get(DataComponentTypes.TOOLTIP_STYLE); - - context.drawTooltip(this.client.textRenderer, SkyblockRecipeResultButton.getTooltip(stack), x, y, tooltipStyle); - } else if (this.hoveredText != null) { - //Draw text as a tooltip if it got truncated & we're hovering over it (for recipe display) - context.drawTooltip(this.client.textRenderer, this.hoveredText, x, y); - } - } - } - - /** - * Returns true if the mouse is hovering over the text at this location. - */ - private boolean isMouseHoveringText(int textX, int textY, int mouseX, int mouseY) { - return RenderHelper.pointIsInArea(mouseX, mouseY, textX, textY, textX + MAX_TEXT_WIDTH + 4, textY + this.client.textRenderer.fontHeight); - } - - protected void closeRecipeView() { - this.currentPage = 0; - this.pageCount = (this.searchResults.size() - 1) / resultButtons.size() + 1; - this.recipeView = false; - updateResultButtons(); - } - - /** - * Handles updating the search results when a character is typed into the search bar, - * - * @implNote The {@code query} is always passed as lower case. - */ - @Override - public void updateSearchResults(String query, FilterOption filterOption, boolean refresh) { - if (!ItemRepository.filesImported()) return; - if (!query.equals(this.lastSearchQuery) || refresh) { - this.lastSearchQuery = query; - this.searchResults.clear(); - - //Search for stacks which contain the search term - for (ItemStack stack : ItemRepository.getItems()) { - String name = stack.getName().getString().toLowerCase(Locale.ENGLISH); - if (!filterOption.test(name)) continue; - List lore = ItemUtils.getLore(stack); - - if (name.contains(query) || lore.stream().map(Text::getString) - .map(string -> string.toLowerCase(Locale.ENGLISH)) - .anyMatch(line -> line.contains(query))) { - this.searchResults.add(stack); - } - } - - closeRecipeView(); - } else { - hideShowPageButtons(); //This branch is called when the recipe book is reinitialized (usually from resizing) - } - } - - /** - * Updates the result buttons. - */ - private void updateResultButtons() { - if (this.recipeView) { - SkyblockCraftingRecipe recipe = this.recipeResults.get(this.currentPage); - - //Clear all result buttons to make way for displaying the recipe - for (SkyblockRecipeResultButton button : this.resultButtons) { - button.clearDisplayStack(); - } - - //Put the recipe in the proper result buttons - - //Row 1 - this.resultButtons.get(5).setDisplayStack(recipe.getGrid().getFirst()); - this.resultButtons.get(6).setDisplayStack(recipe.getGrid().get(1)); - this.resultButtons.get(7).setDisplayStack(recipe.getGrid().get(2)); - //Row 2 - this.resultButtons.get(10).setDisplayStack(recipe.getGrid().get(3)); - this.resultButtons.get(11).setDisplayStack(recipe.getGrid().get(4)); - this.resultButtons.get(12).setDisplayStack(recipe.getGrid().get(5)); - //Row 3 - this.resultButtons.get(15).setDisplayStack(recipe.getGrid().get(6)); - this.resultButtons.get(16).setDisplayStack(recipe.getGrid().get(7)); - this.resultButtons.get(17).setDisplayStack(recipe.getGrid().get(8)); - //Result - this.resultButtons.get(14).setDisplayStack(recipe.getResult()); - } else { - //Update the result buttons with the stacks from the search results - for (int i = 0; i < resultButtons.size(); ++i) { - int index = this.currentPage * resultButtons.size() + i; - - if (index < this.searchResults.size()) { - resultButtons.get(i).setDisplayStack(this.searchResults.get(index)); - } else { - resultButtons.get(i).clearDisplayStack(); - } - } - } - - hideShowPageButtons(); - } - - /** - * Hides or shows the page buttons. - */ - private void hideShowPageButtons() { - //Show the previous page button if the page count is greater than 0 - this.prevPageButton.active = this.currentPage > 0; - //Show the next page button if the current page is less than the highest possible page - this.nextPageButton.active = this.currentPage < this.pageCount - 1; - } - - @Override - public boolean mouseClicked(double mouseX, double mouseY, int button) { - if (this.nextPageButton.mouseClicked(mouseX, mouseY, button)) { - this.currentPage++; - this.updateResultButtons(); - - return true; - } else if (this.prevPageButton.mouseClicked(mouseX, mouseY, button)) { - this.currentPage--; - this.updateResultButtons(); - - return true; - } - - if (this.recipeView && button == 1) { - // The crafting result button - var result = resultButtons.get(14); - var rawID = ItemUtils.getItemId(result.getDisplayStack()); - if (result.isMouseOver(mouseX, mouseY)) { - MessageScheduler.INSTANCE.sendMessageAfterCooldown(String.format("/viewrecipe %s", rawID), true); - return true; - } - } - - for (SkyblockRecipeResultButton resultButton : this.resultButtons) { - //If the result button was clicked then try and show a recipe if there is one - //for the item - if (resultButton.mouseClicked(mouseX, mouseY, button)) { - String itemId = ItemUtils.getItemId(resultButton.getDisplayStack()); - - //Continue if this item doesn't have an item id - if (itemId.isEmpty()) continue; - - List recipes = ItemRepository.getRecipes(itemId); - - //If this item has recipes then set the fields so that they can be displayed - if (!recipes.isEmpty()) { - this.recipeResults = recipes; - this.currentPage = 0; - this.pageCount = recipes.size(); - this.recipeView = true; - this.updateResultButtons(); - } - - return true; - } - } - - return false; - } - - @Override - public boolean keyPressed(double mouseX, double mouseY, int keyCode, int scanCode, int modifiers) { - if (SkyblockerConfigManager.get().general.wikiLookup.enableWikiLookup) { - boolean officialWikiLookup = WikiLookup.officialWikiLookup.matchesKey(keyCode, scanCode); - if (officialWikiLookup || WikiLookup.fandomWikiLookup.matchesKey(keyCode, scanCode)) { - return this.resultButtons.stream() - .filter(button -> button.isMouseOver(mouseX, mouseY)) - .findFirst().map(button -> { - WikiLookup.openWiki(button.getDisplayStack(), client.player, officialWikiLookup); - return true; - }).orElse(false); - } - } - return false; - } -} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/itemlist/recipebook/SkyblockCraftingTab.java b/src/main/java/de/hysky/skyblocker/skyblock/itemlist/recipebook/SkyblockCraftingTab.java index 7b69f725..2422aef3 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/itemlist/recipebook/SkyblockCraftingTab.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/itemlist/recipebook/SkyblockCraftingTab.java @@ -10,7 +10,7 @@ import net.minecraft.util.math.MathHelper; /** * The Skyblock Crafting Tab which handles the mouse clicks & rendering for the results page and the search field. */ -record SkyblockCraftingTab(SkyblockRecipeBookWidget recipeBook, ItemStack icon, SkyblockCraftingRecipeResults results) implements RecipeTab { +record SkyblockCraftingTab(SkyblockRecipeBookWidget recipeBook, ItemStack icon, SkyblockRecipeResults results) implements RecipeTab { static final ItemStack CRAFTING_TABLE = new ItemStack(Items.CRAFTING_TABLE); @Override diff --git a/src/main/java/de/hysky/skyblocker/skyblock/itemlist/recipebook/SkyblockRecipeBookWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/itemlist/recipebook/SkyblockRecipeBookWidget.java index 680af462..3363c877 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/itemlist/recipebook/SkyblockRecipeBookWidget.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/itemlist/recipebook/SkyblockRecipeBookWidget.java @@ -41,7 +41,7 @@ public class SkyblockRecipeBookWidget extends RecipeBookWidget tabs = List.of( - new SkyblockCraftingTab(this, SkyblockCraftingTab.CRAFTING_TABLE, new SkyblockCraftingRecipeResults()), + new SkyblockCraftingTab(this, SkyblockCraftingTab.CRAFTING_TABLE, new SkyblockRecipeResults()), new UpcomingEventsTab() ); private final List> tabButtons = Lists.newArrayList(); diff --git a/src/main/java/de/hysky/skyblocker/skyblock/itemlist/recipebook/SkyblockRecipeResultButton.java b/src/main/java/de/hysky/skyblocker/skyblock/itemlist/recipebook/SkyblockRecipeResultButton.java index abd93c6b..891f1257 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/itemlist/recipebook/SkyblockRecipeResultButton.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/itemlist/recipebook/SkyblockRecipeResultButton.java @@ -13,6 +13,7 @@ import net.minecraft.client.render.RenderLayer; import net.minecraft.item.ItemStack; import net.minecraft.screen.ScreenTexts; import net.minecraft.text.Text; +import org.jetbrains.annotations.Nullable; public class SkyblockRecipeResultButton extends ClickableWidget { //Corresponds to AnimatedResultButton#field_32415 @@ -25,14 +26,20 @@ public class SkyblockRecipeResultButton extends ClickableWidget { super(0, 0, SIZE, SIZE, ScreenTexts.EMPTY); } - protected ItemStack getDisplayStack() { + protected SkyblockRecipeResultButton(int x, int y) { + this(); + setPosition(x, y); + } + + protected @Nullable ItemStack getDisplayStack() { return this.itemStack; } - protected void setDisplayStack(ItemStack stack) { + protected SkyblockRecipeResultButton setDisplayStack(ItemStack stack) { this.active = !stack.isEmpty(); this.visible = true; this.itemStack = stack; + return this; } protected void clearDisplayStack() { diff --git a/src/main/java/de/hysky/skyblocker/skyblock/itemlist/recipebook/SkyblockRecipeResults.java b/src/main/java/de/hysky/skyblocker/skyblock/itemlist/recipebook/SkyblockRecipeResults.java new file mode 100644 index 00000000..f2142a76 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/itemlist/recipebook/SkyblockRecipeResults.java @@ -0,0 +1,373 @@ +package de.hysky.skyblocker.skyblock.itemlist.recipebook; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +import com.google.common.collect.Lists; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.skyblock.item.WikiLookup; +import de.hysky.skyblocker.skyblock.itemlist.ItemRepository; +import de.hysky.skyblocker.skyblock.itemlist.recipes.SkyblockCraftingRecipe; +import de.hysky.skyblocker.skyblock.itemlist.recipes.SkyblockForgeRecipe; +import de.hysky.skyblocker.skyblock.itemlist.recipes.SkyblockRecipe; +import de.hysky.skyblocker.utils.ItemUtils; +import de.hysky.skyblocker.utils.render.RenderHelper; +import de.hysky.skyblocker.utils.scheduler.MessageScheduler; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.font.TextRenderer; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.screen.recipebook.RecipeBookResults; +import net.minecraft.client.gui.widget.ToggleButtonWidget; +import net.minecraft.component.DataComponentTypes; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.screen.ScreenTexts; +import net.minecraft.text.OrderedText; +import net.minecraft.text.StringVisitable; +import net.minecraft.text.Text; +import net.minecraft.util.Identifier; +import net.minecraft.util.Language; +import org.jetbrains.annotations.Nullable; +import org.joml.Vector2i; + +//TODO when in recipe view set search hint to talk about close or smth +/** + * Based off {@link net.minecraft.client.gui.screen.recipebook.RecipeBookResults}. + */ +public class SkyblockRecipeResults implements RecipeAreaDisplay { + /** + * The width before text will go outside of the recipe book area. + */ + private static final int MAX_TEXT_WIDTH = 124; + private static final String ELLIPSIS_STRING = ScreenTexts.ELLIPSIS.getString(); + + private final List resultButtons = Lists.newArrayListWithCapacity(20); + private final List recipeSlotButtons = Lists.newArrayListWithCapacity(16); + private @Nullable ItemStack recipeIcon = null; + private MinecraftClient client; + private ToggleButtonWidget nextPageButton; + private ToggleButtonWidget prevPageButton; + private SkyblockRecipeResultButton hoveredResultButton; + private String lastSearchQuery = null; + private final List searchResults = new ArrayList<>(); + /** + * Text to be displayed as a tooltip. + */ + private Text hoveredText; + private List recipeResults = new ArrayList<>(); + private int pageCount = 0; + private int currentPage = 0; + /** + * Whether we are showing the recipe for an item (true) or just showing the search results (false). + */ + private boolean recipeView = false; + + protected SkyblockRecipeResults() { + for (int i = 0; i < 20; i++) { + this.resultButtons.add(new SkyblockRecipeResultButton()); + } + } + + @Override + public void initialize(MinecraftClient client, int parentLeft, int parentTop) { + this.client = client; + + //Position the result buttons + for (int i = 0; i < resultButtons.size(); i++) { + this.resultButtons.get(i).setPosition(parentLeft + 11 + 25 * (i % 5), parentTop + 31 + 25 * (i / 5)); + } + + //Setup & position the page flip buttons + this.nextPageButton = new ToggleButtonWidget(parentLeft + 93, parentTop + 137, 12, 17, false); + this.nextPageButton.setTextures(RecipeBookResults.PAGE_FORWARD_TEXTURES); + this.prevPageButton = new ToggleButtonWidget(parentLeft + 38, parentTop + 137, 12, 17, true); + this.prevPageButton.setTextures(RecipeBookResults.PAGE_BACKWARD_TEXTURES); + updateResultButtons(); + } + + @Override + public void draw(DrawContext context, int x, int y, int mouseX, int mouseY, float delta) { + TextRenderer textRenderer = this.client.textRenderer; + + //Reset the hovered text + this.hoveredText = null; + + //If we have selected an item to view recipes for then show the recipe view specific stuff (e.g. name, requirement) + if (this.recipeView) { + drawRecipeDisplay(context, textRenderer, x, y, mouseX, mouseY); + } + + //Render the page count + if (this.pageCount > 1) { + Text text = Text.translatable("gui.recipebook.page", this.currentPage + 1, this.pageCount); + int width = textRenderer.getWidth(text); + + context.drawText(textRenderer, text, x - width / 2 + 73, y + 141, -1, false); + } + + //Render the results + this.hoveredResultButton = null; + + for (SkyblockRecipeResultButton resultButton : recipeView ? recipeSlotButtons : resultButtons) { + resultButton.render(context, mouseX, mouseY, delta); + + if (resultButton.visible && resultButton.isSelected()) this.hoveredResultButton = resultButton; + } + + + + //Render the page flip buttons + if (this.prevPageButton.active) this.prevPageButton.render(context, mouseX, mouseY, delta); + if (this.nextPageButton.active) this.nextPageButton.render(context, mouseX, mouseY, delta); + } + + //TODO enable scissor? + private void drawRecipeDisplay(DrawContext context, TextRenderer textRenderer, int x, int y, int mouseX, int mouseY) { + SkyblockRecipe recipe = this.recipeResults.get(this.currentPage); + //Render the "Craft Text" which is usually a requirement (e.g. Wolf Slayer 7) + String craftText = recipe.getExtraText().getString(); + + if (!craftText.isEmpty()) { + if (textRenderer.getWidth(craftText) > MAX_TEXT_WIDTH) { + //Set craft text as hovered text if we're hovering over it since it got truncated + if (isMouseHoveringText(x + 11, y + 31, mouseX, mouseY)) this.hoveredText = Text.literal(craftText); + + craftText = textRenderer.trimToWidth(craftText, MAX_TEXT_WIDTH) + ELLIPSIS_STRING; + } + + context.drawTextWithShadow(textRenderer, craftText, x + 11, y + 31, 0xffffffff); + } + + //Render the resulting item's name + Text