diff --git a/CHANGELOG.md b/CHANGELOG.md index 38a57538..5edd35f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ -### Tweaks -* Disabled stacks no longer appear as workstations #323 +### Additions +* Added option to use all registered items instead of creative tabs to source the index. +* Added tab scrolling on hover #308 +* Added better support for JEI slot highlighting ### Fixes -* Fixed "exception deserializing stack" and related resource oddities with a temporary workaround #257 -* Fixed typo in log #341 \ No newline at end of file +* Fixed ingredient serializer errors #354 +* Fixed #346 \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 8ccf6fc3..76897f71 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,7 +4,7 @@ minecraft_version=1.19.4 enabled_platforms=fabric,forge archives_base_name=emi -mod_version=1.0.24 +mod_version=1.0.25 maven_group=dev.emi architectury_version=4.9.83 diff --git a/xplat/src/main/java/dev/emi/emi/config/EmiConfig.java b/xplat/src/main/java/dev/emi/emi/config/EmiConfig.java index cc972364..0ccdede8 100644 --- a/xplat/src/main/java/dev/emi/emi/config/EmiConfig.java +++ b/xplat/src/main/java/dev/emi/emi/config/EmiConfig.java @@ -55,6 +55,10 @@ public class EmiConfig { @ConfigValue("general.help-level") public static HelpLevel helpLevel = HelpLevel.NORMAL; + @Comment("Where EMI should pull stacks from to populate the index.") + @ConfigValue("general.index-source") + public static IndexSource indexSource = IndexSource.CREATIVE; + @ConfigGroup("general.search") @Comment("Whether normal search queries should include the tooltip.") @ConfigValue("general.search-tooltip-by-default") diff --git a/xplat/src/main/java/dev/emi/emi/config/IndexSource.java b/xplat/src/main/java/dev/emi/emi/config/IndexSource.java new file mode 100644 index 00000000..dffbb0a5 --- /dev/null +++ b/xplat/src/main/java/dev/emi/emi/config/IndexSource.java @@ -0,0 +1,27 @@ +package dev.emi.emi.config; + +import dev.emi.emi.EmiPort; +import net.minecraft.text.Text; + +public enum IndexSource implements ConfigEnum { + CREATIVE("creative"), + REGISTERED("registered"), + CREATIVE_PLUS_REGISTERED("creative-plus-registered"), + ; + + public final String name; + + private IndexSource(String name) { + this.name = name; + } + + @Override + public String getName() { + return name; + } + + @Override + public Text getText() { + return EmiPort.translatable("emi.index_source." + name.replace("-", "_")); + } +} diff --git a/xplat/src/main/java/dev/emi/emi/data/EmiData.java b/xplat/src/main/java/dev/emi/emi/data/EmiData.java index 0da0b5d4..3825649c 100644 --- a/xplat/src/main/java/dev/emi/emi/data/EmiData.java +++ b/xplat/src/main/java/dev/emi/emi/data/EmiData.java @@ -200,18 +200,20 @@ public static void init(Consumer register) { getArrayOrSingleton(json, "text").map(t -> (Text) EmiPort.translatable(t.getAsString())).toList(), id)); } else if (s.equals("emi:world_interaction")) { - EmiWorldInteractionRecipe.Builder builder = EmiWorldInteractionRecipe.builder(); - getArrayOrSingleton(json, "left").map(EmiIngredientSerializer::getDeserialized).forEach( - i -> builder.leftInput(i) - ); - getArrayOrSingleton(json, "right").map(EmiIngredientSerializer::getDeserialized).forEach( - i -> builder.rightInput(i, false) - ); - getArrayOrSingleton(json, "output").map(EmiIngredientSerializer::getDeserialized).forEach( - i -> builder.output(i.getEmiStacks().get(0)) - ); - builder.id(id); - list.add(() -> builder.build()); + list.add(() -> { + EmiWorldInteractionRecipe.Builder builder = EmiWorldInteractionRecipe.builder(); + getArrayOrSingleton(json, "left").map(EmiIngredientSerializer::getDeserialized).forEach( + i -> builder.leftInput(i) + ); + getArrayOrSingleton(json, "right").map(EmiIngredientSerializer::getDeserialized).forEach( + i -> builder.rightInput(i, false) + ); + getArrayOrSingleton(json, "output").map(EmiIngredientSerializer::getDeserialized).forEach( + i -> builder.output(i.getEmiStacks().get(0)) + ); + builder.id(id); + return builder.build(); + }); } }, list -> recipes = list)); } diff --git a/xplat/src/main/java/dev/emi/emi/jemi/JemiRecipeHandler.java b/xplat/src/main/java/dev/emi/emi/jemi/JemiRecipeHandler.java index 4f7a2fd0..0233243e 100644 --- a/xplat/src/main/java/dev/emi/emi/jemi/JemiRecipeHandler.java +++ b/xplat/src/main/java/dev/emi/emi/jemi/JemiRecipeHandler.java @@ -3,6 +3,7 @@ import java.util.List; import java.util.Optional; +import dev.emi.emi.api.recipe.EmiCraftingRecipe; import dev.emi.emi.api.recipe.EmiPlayerInventory; import dev.emi.emi.api.recipe.EmiRecipe; import dev.emi.emi.api.recipe.VanillaEmiRecipeCategories; @@ -12,11 +13,13 @@ import dev.emi.emi.api.stack.EmiStack; import dev.emi.emi.api.widget.Bounds; import dev.emi.emi.api.widget.RecipeFillButtonWidget; +import dev.emi.emi.api.widget.SlotWidget; import dev.emi.emi.api.widget.Widget; import dev.emi.emi.jemi.impl.JemiRecipeLayoutBuilder; import dev.emi.emi.jemi.impl.JemiRecipeSlot; import dev.emi.emi.jemi.impl.JemiRecipeSlotsView; import dev.emi.emi.runtime.EmiDrawContext; +import dev.emi.emi.screen.EmiScreenManager; import mezz.jei.api.gui.builder.IIngredientAcceptor; import mezz.jei.api.ingredients.IIngredientType; import mezz.jei.api.ingredients.ITypedIngredient; @@ -91,11 +94,25 @@ public void render(EmiRecipe recipe, EmiCraftContext context, List wi } } } - /*R rawRecipe = getRawRecipe(recipe); - JemiRecipeSlotsView view = createSlotsView(recipe, rawRecipe); + R rawRecipe = getRawRecipe(recipe); + JemiRecipeSlotsView view = createSlotsView(recipe, rawRecipe, widgets); if (view != null) { + view.getSlotViews().forEach(v -> { + if (v instanceof JemiRecipeSlot jrs) { + jrs.highlight = 0; + } + }); + draw.push(); + draw.matrices().translate(-100000, -100000, -100000); + draw.matrices().scale(0, 0, 0); err.showError(raw, EmiScreenManager.lastMouseX, EmiScreenManager.lastMouseY, view, 0, 0); - }*/ + draw.pop(); + view.getSlotViews().forEach(v -> { + if (v instanceof JemiRecipeSlot jrs && jrs.highlight != 0 && !jrs.isEmpty()) { + draw.fill(jrs.x, jrs.y, 18, 18, jrs.highlight); + } + }); + } } } @@ -105,7 +122,7 @@ private IRecipeTransferError jeiCraft(EmiRecipe recipe, EmiCraftContext conte MinecraftClient client = MinecraftClient.getInstance(); R rawRecipe = getRawRecipe(recipe); - JemiRecipeSlotsView view = createSlotsView(recipe, rawRecipe); + JemiRecipeSlotsView view = createSlotsView(recipe, rawRecipe, List.of()); if (view == null) { return () -> IRecipeTransferError.Type.INTERNAL; @@ -118,7 +135,7 @@ private IRecipeTransferError jeiCraft(EmiRecipe recipe, EmiCraftContext conte return () -> IRecipeTransferError.Type.INTERNAL; } - private JemiRecipeSlotsView createSlotsView(EmiRecipe recipe, R rawRecipe) { + private JemiRecipeSlotsView createSlotsView(EmiRecipe recipe, R rawRecipe, List widgets) { JemiRecipeLayoutBuilder builder = null; if (rawRecipe != null) { /* @@ -131,15 +148,51 @@ private JemiRecipeSlotsView createSlotsView(EmiRecipe recipe, R rawRecipe) { } if (builder == null) { + List slotWidgets = widgets.stream().filter(w -> w instanceof SlotWidget).map(w -> (SlotWidget) w).toList(); builder = new JemiRecipeLayoutBuilder(); - addIngredients(builder, recipe.getOutputs(), RecipeIngredientRole.OUTPUT); - addIngredients(builder, recipe.getInputs(), RecipeIngredientRole.INPUT); + addIngredients(builder, slotWidgets, recipe.getOutputs(), RecipeIngredientRole.OUTPUT); + int blankedSlots = 0; + // People assume very specific slot layouts from JEI. Oblige them. + if (recipe instanceof EmiCraftingRecipe ecr) { + if (ecr.shapeless) { + int inputSize = recipe.getInputs().size(); + if (inputSize == 1) { + addBlankIngredients(builder, slotWidgets, 4, RecipeIngredientRole.INPUT); + blankedSlots += 4; + addIngredients(builder, slotWidgets, recipe.getInputs(), RecipeIngredientRole.INPUT); + } else if (inputSize < 5) { + int wrap = 0; + for (EmiIngredient i : recipe.getInputs()) { + addIngredients(builder, slotWidgets, List.of(i), RecipeIngredientRole.INPUT); + wrap++; + if (wrap >= 2) { + wrap = 0; + addBlankIngredients(builder, slotWidgets, 1, RecipeIngredientRole.INPUT); + blankedSlots += 1; + } + } + } else { + addIngredients(builder, slotWidgets, recipe.getInputs(), RecipeIngredientRole.INPUT); + } + } else { + if (ecr.canFit(1, 3)) { + addBlankIngredients(builder, slotWidgets, 1, RecipeIngredientRole.INPUT); + blankedSlots += 1; + } else if (ecr.canFit(3, 1) || (ecr.canFit(3, 2) && !ecr.canFit(2, 2))) { + addBlankIngredients(builder, slotWidgets, 3, RecipeIngredientRole.INPUT); + blankedSlots += 3; + } + addIngredients(builder, slotWidgets, recipe.getInputs(), RecipeIngredientRole.INPUT); + } + } else { + addIngredients(builder, slotWidgets, recipe.getInputs(), RecipeIngredientRole.INPUT); + } if (recipe.getCategory() == VanillaEmiRecipeCategories.CRAFTING) { - for (int i = recipe.getInputs().size(); i < 9; i++) { - addIngredients(builder, List.of(EmiStack.EMPTY), RecipeIngredientRole.INPUT); + for (int i = recipe.getInputs().size() + blankedSlots; i < 9; i++) { + addIngredients(builder, slotWidgets, List.of(EmiStack.EMPTY), RecipeIngredientRole.INPUT); } } - addIngredients(builder, recipe.getCatalysts(), RecipeIngredientRole.CATALYST); + addIngredients(builder, slotWidgets, recipe.getCatalysts(), RecipeIngredientRole.CATALYST); } return new JemiRecipeSlotsView(builder.slots.stream().map(JemiRecipeSlot::new).toList()); @@ -177,10 +230,23 @@ private R getRawRecipe(EmiRecipe recipe) { return null; } + private void addBlankIngredients(JemiRecipeLayoutBuilder builder, List widgets, int amount, RecipeIngredientRole role) { + for (int i = 0; i < amount; i++) { + addIngredients(builder, widgets, List.of(EmiStack.EMPTY), RecipeIngredientRole.INPUT); + } + } + @SuppressWarnings({"rawtypes", "unchecked"}) - private void addIngredients(JemiRecipeLayoutBuilder builder, List stacks, RecipeIngredientRole role) { + private void addIngredients(JemiRecipeLayoutBuilder builder, List widgets, List stacks, RecipeIngredientRole role) { for (EmiIngredient ing : stacks) { - IIngredientAcceptor acceptor = builder.addSlot(role, 0, 0); + int x = 0, y = 0; + for (SlotWidget w : widgets) { + if (w.getStack() == ing) { + x = w.getBounds().x(); + y = w.getBounds().y(); + } + } + IIngredientAcceptor acceptor = builder.addSlot(role, x, y); for (EmiStack stack : ing.getEmiStacks()) { Optional> opt = JemiUtil.getTyped(stack); if (opt.isPresent()) { diff --git a/xplat/src/main/java/dev/emi/emi/jemi/impl/JemiRecipeSlot.java b/xplat/src/main/java/dev/emi/emi/jemi/impl/JemiRecipeSlot.java index 61148881..60882f8c 100644 --- a/xplat/src/main/java/dev/emi/emi/jemi/impl/JemiRecipeSlot.java +++ b/xplat/src/main/java/dev/emi/emi/jemi/impl/JemiRecipeSlot.java @@ -28,6 +28,7 @@ public class JemiRecipeSlot implements IRecipeSlotView { public final TankInfo tankInfo; public final EmiIngredient stack; public SlotWidget widget; + public int highlight = 0; public JemiRecipeSlot(JemiRecipeSlotBuilder builder) { this.role = builder.acceptor.role; @@ -96,6 +97,7 @@ public RecipeIngredientRole getRole() { @Override public void drawHighlight(MatrixStack raw, int color) { + this.highlight = color; } public static record OffsetDrawable(IDrawable drawable, int xOff, int yOff){ diff --git a/xplat/src/main/java/dev/emi/emi/registry/EmiStackList.java b/xplat/src/main/java/dev/emi/emi/registry/EmiStackList.java index d0b95dcf..7fbfe9c6 100644 --- a/xplat/src/main/java/dev/emi/emi/registry/EmiStackList.java +++ b/xplat/src/main/java/dev/emi/emi/registry/EmiStackList.java @@ -15,6 +15,8 @@ import dev.emi.emi.api.stack.Comparison; import dev.emi.emi.api.stack.EmiIngredient; import dev.emi.emi.api.stack.EmiStack; +import dev.emi.emi.config.EmiConfig; +import dev.emi.emi.config.IndexSource; import dev.emi.emi.data.EmiData; import dev.emi.emi.data.IndexStackData; import dev.emi.emi.runtime.EmiHidden; @@ -68,25 +70,29 @@ public static void reload() { EmiStack stack = EmiStack.of(item); namespaceGroups.computeIfAbsent(stack.getId().getNamespace(), (k) -> new IndexGroup()).stacks.add(stack); } - for (ItemGroup group : ItemGroups.getGroups()) { - Object2IntMap usedNamespaces = new Object2IntOpenHashMap<>(); - IndexGroup ig = new IndexGroup(); - Collection searchStacks = group.getSearchTabStacks(); - for (ItemStack stack : searchStacks) { - EmiStack es = EmiStack.of(stack); - String namespace = es.getId().getNamespace(); - usedNamespaces.put(namespace, usedNamespaces.getOrDefault(namespace, 0) + 1); - ig.stacks.add(es); - } - for (String namespace : usedNamespaces.keySet()) { - if (namespaceGroups.containsKey(namespace)) { - IndexGroup ng = namespaceGroups.get(namespace); - if (usedNamespaces.getInt(namespace) * 3 >= searchStacks.size() || usedNamespaces.getInt(namespace) * 3 >= ng.stacks.size()) { - ng.suppressedBy.add(ig); + if (EmiConfig.indexSource != IndexSource.REGISTERED) { + for (ItemGroup group : ItemGroups.getGroups()) { + Object2IntMap usedNamespaces = new Object2IntOpenHashMap<>(); + IndexGroup ig = new IndexGroup(); + Collection searchStacks = group.getSearchTabStacks(); + for (ItemStack stack : searchStacks) { + EmiStack es = EmiStack.of(stack); + String namespace = es.getId().getNamespace(); + usedNamespaces.put(namespace, usedNamespaces.getOrDefault(namespace, 0) + 1); + ig.stacks.add(es); + } + if (EmiConfig.indexSource == IndexSource.CREATIVE) { + for (String namespace : usedNamespaces.keySet()) { + if (namespaceGroups.containsKey(namespace)) { + IndexGroup ng = namespaceGroups.get(namespace); + if (usedNamespaces.getInt(namespace) * 3 >= searchStacks.size() || usedNamespaces.getInt(namespace) * 3 >= ng.stacks.size()) { + ng.suppressedBy.add(ig); + } + } } } + groups.add(ig); } - groups.add(ig); } groups.addAll(namespaceGroups.values()); IndexGroup fluidGroup = new IndexGroup(); diff --git a/xplat/src/main/java/dev/emi/emi/screen/RecipeScreen.java b/xplat/src/main/java/dev/emi/emi/screen/RecipeScreen.java index aa6ae170..d31148d0 100644 --- a/xplat/src/main/java/dev/emi/emi/screen/RecipeScreen.java +++ b/xplat/src/main/java/dev/emi/emi/screen/RecipeScreen.java @@ -501,7 +501,7 @@ public boolean mouseScrolled(double mouseX, double mouseY, double amount) { scrollAcc += amount; int sa = (int) scrollAcc; scrollAcc %= 1; - if (EmiInput.isShiftDown()) { + if (EmiInput.isShiftDown() || mouseY < this.y) { setPage(tabPage, tab - sa, 0); } else { setPage(tabPage, tab, page - sa); diff --git a/xplat/src/main/resources/assets/emi/lang/en_us.json b/xplat/src/main/resources/assets/emi/lang/en_us.json index fd25083d..5b8b8da3 100644 --- a/xplat/src/main/resources/assets/emi/lang/en_us.json +++ b/xplat/src/main/resources/assets/emi/lang/en_us.json @@ -29,6 +29,9 @@ "key.emi.hide_stack": "Hide Stack", "key.emi.hide_stack_by_id": "Hide Stack by ID", + "emi.index_source.creative": "Creative", + "emi.index_source.registered": "Registered", + "emi.index_source.creative_plus_registered": "Creative + Registered", "emi.help_level.verbose": "Verbose", "emi.help_level.normal": "Normal", "emi.help_level.none": "None", @@ -89,6 +92,7 @@ "config.emi.general.enabled": "Enabled", "config.emi.general.cheat_mode": "Cheat Mode", "config.emi.general.help_level": "Help Level", + "config.emi.general.index_source": "Index Source", "config.emi.general.search_tooltip_by_default": "Search Tooltip by Default", "config.emi.general.search_mod_name_by_default": "Search Mod Name by Default", "config.emi.general.search_tags_by_default": "Search Tags by Default",