Show missing indicators in JEI (#2582)
* port #2330 to 1.15 * remove unecessary reflection * rewrite JEI show missing and craftable items so that: - clicking transfer button still works - it is also shown for processing recipes - also for fluids * add missing method * send Fluids with the ItemGridUpdateMessage * port #2330 to 1.15 * remove unecessary reflection * rewrite JEI show missing and craftable items so that: - clicking transfer button still works - it is also shown for processing recipes - also for fluids * add missing method * send Fluids with the ItemGridUpdateMessage * fix accidental lang duplication * move lang key to appropriate location * remove accidental changes to lang file * port to 1.16 and cleanup * remove fluids * update translation to newer method. * add being able to request autocrafting. * remove unused variable * formatting * fix requested changes Co-authored-by: Michael <mcpower@users.noreply.github.com>
This commit is contained in:
@@ -47,6 +47,10 @@ public class GridContainer extends BaseContainer implements ICraftingGridListene
|
||||
grid.addCraftingListener(this);
|
||||
}
|
||||
|
||||
public IScreenInfoProvider getScreenInfoProvider() {
|
||||
return screenInfoProvider;
|
||||
}
|
||||
|
||||
public void setScreenInfoProvider(IScreenInfoProvider screenInfoProvider) {
|
||||
this.screenInfoProvider = screenInfoProvider;
|
||||
}
|
||||
|
@@ -4,20 +4,25 @@ import com.refinedmods.refinedstorage.RS;
|
||||
import com.refinedmods.refinedstorage.api.network.grid.GridType;
|
||||
import com.refinedmods.refinedstorage.api.network.grid.IGrid;
|
||||
import com.refinedmods.refinedstorage.container.GridContainer;
|
||||
import com.refinedmods.refinedstorage.network.grid.GridCraftingPreviewRequestMessage;
|
||||
import com.refinedmods.refinedstorage.network.grid.GridProcessingTransferMessage;
|
||||
import com.refinedmods.refinedstorage.network.grid.GridTransferMessage;
|
||||
import com.refinedmods.refinedstorage.screen.grid.GridScreen;
|
||||
import com.refinedmods.refinedstorage.screen.grid.stack.IGridStack;
|
||||
import com.refinedmods.refinedstorage.screen.grid.stack.ItemGridStack;
|
||||
import mezz.jei.api.constants.VanillaRecipeCategoryUid;
|
||||
import mezz.jei.api.gui.IRecipeLayout;
|
||||
import mezz.jei.api.gui.ingredient.IGuiIngredient;
|
||||
import mezz.jei.api.recipe.category.IRecipeCategory;
|
||||
import mezz.jei.api.recipe.transfer.IRecipeTransferError;
|
||||
import mezz.jei.api.recipe.transfer.IRecipeTransferHandler;
|
||||
import net.minecraft.client.gui.screen.Screen;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.inventory.CraftingInventory;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraftforge.fluids.FluidStack;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -33,12 +38,69 @@ public class GridRecipeTransferHandler implements IRecipeTransferHandler<GridCon
|
||||
|
||||
@Override
|
||||
public IRecipeTransferError transferRecipe(@Nonnull GridContainer container, Object recipe, @Nonnull IRecipeLayout recipeLayout, @Nonnull PlayerEntity player, boolean maxTransfer, boolean doTransfer) {
|
||||
IGrid grid = container.getGrid();
|
||||
IngredientTracker tracker = trackItems(container, recipeLayout, player);
|
||||
|
||||
if (tracker.hasMissing()) {
|
||||
if (doTransfer && Screen.hasControlDown()) {
|
||||
tracker.getCraftingRequests().forEach((id, count) -> {
|
||||
RS.NETWORK_HANDLER.sendToServer(new GridCraftingPreviewRequestMessage(id, count, Screen.hasShiftDown(), false));
|
||||
});
|
||||
} else if (doTransfer) {
|
||||
moveItems(container, recipeLayout);
|
||||
} else {
|
||||
return new RecipeTransferGridError(tracker);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private IngredientTracker trackItems(GridContainer container, IRecipeLayout recipeLayout, PlayerEntity player) {
|
||||
IngredientTracker tracker = new IngredientTracker(recipeLayout);
|
||||
if (!(container.getScreenInfoProvider() instanceof GridScreen)) {
|
||||
return tracker;
|
||||
}
|
||||
|
||||
// Using IGridView#getStacks will return a *filtered* list of items in the view,
|
||||
// which will cause problems - especially if the user uses JEI synchronised searching.
|
||||
// Instead, we will use IGridView#getAllStacks which provides an unordered view of all GridStacks.
|
||||
Collection<IGridStack> gridStacks = ((GridScreen) container.getScreenInfoProvider()).getView().getAllStacks();
|
||||
|
||||
// Check grid
|
||||
for (IGridStack gridStack : gridStacks) {
|
||||
if (gridStack instanceof ItemGridStack) {
|
||||
tracker.addAvailableStack(((ItemGridStack) gridStack).getStack(), gridStack);
|
||||
}
|
||||
}
|
||||
|
||||
// Check inventory
|
||||
for (int inventorySlot = 0; inventorySlot < player.inventory.getSizeInventory(); inventorySlot++) {
|
||||
if (!player.inventory.getStackInSlot(inventorySlot).isEmpty()) {
|
||||
tracker.addAvailableStack(player.inventory.getStackInSlot(inventorySlot), null);
|
||||
}
|
||||
}
|
||||
|
||||
// Check grid crafting slots
|
||||
if (container.getGrid().getGridType().equals(GridType.CRAFTING)) {
|
||||
CraftingInventory craftingMatrix = container.getGrid().getCraftingMatrix();
|
||||
if (craftingMatrix != null) {
|
||||
for (int matrixSlot = 0; matrixSlot < craftingMatrix.getSizeInventory(); matrixSlot++) {
|
||||
if (!craftingMatrix.getStackInSlot(matrixSlot).isEmpty()) {
|
||||
tracker.addAvailableStack(craftingMatrix.getStackInSlot(matrixSlot), null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tracker;
|
||||
}
|
||||
|
||||
private void moveItems(GridContainer gridContainer, IRecipeLayout recipeLayout) {
|
||||
IGrid grid = gridContainer.getGrid();
|
||||
|
||||
if (doTransfer) {
|
||||
LAST_TRANSFER_TIME = System.currentTimeMillis();
|
||||
|
||||
if (grid.getGridType() == GridType.PATTERN && !isCraftingRecipe(recipeLayout.getRecipeCategory())) {
|
||||
if (grid.getGridType() == GridType.PATTERN && !recipeLayout.getRecipeCategory().getUid().equals(VanillaRecipeCategoryUid.CRAFTING)) {
|
||||
List<ItemStack> inputs = new LinkedList<>();
|
||||
List<ItemStack> outputs = new LinkedList<>();
|
||||
|
||||
@@ -73,15 +135,10 @@ public class GridRecipeTransferHandler implements IRecipeTransferHandler<GridCon
|
||||
} else {
|
||||
RS.NETWORK_HANDLER.sendToServer(new GridTransferMessage(
|
||||
recipeLayout.getItemStacks().getGuiIngredients(),
|
||||
container.inventorySlots.stream().filter(s -> s.inventory instanceof CraftingInventory).collect(Collectors.toList())
|
||||
gridContainer.inventorySlots.stream().filter(s -> s.inventory instanceof CraftingInventory).collect(Collectors.toList())
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean isCraftingRecipe(IRecipeCategory<?> recipeCategory) {
|
||||
return recipeCategory.getUid().equals(VanillaRecipeCategoryUid.CRAFTING);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,46 @@
|
||||
package com.refinedmods.refinedstorage.integration.jei;
|
||||
|
||||
import mezz.jei.api.gui.ingredient.IGuiIngredient;
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
class Ingredient {
|
||||
private IGuiIngredient<ItemStack> guiIngredient;
|
||||
private UUID craftStackId;
|
||||
private int required;
|
||||
private int fulfilled;
|
||||
|
||||
public Ingredient(IGuiIngredient<ItemStack> guiIngredient) {
|
||||
this.guiIngredient = guiIngredient;
|
||||
this.required = guiIngredient.getAllIngredients().get(0).getCount();
|
||||
}
|
||||
|
||||
public boolean isAvailable() {
|
||||
return getMissingAmount() == 0;
|
||||
}
|
||||
|
||||
public int getMissingAmount() {
|
||||
return required - fulfilled;
|
||||
}
|
||||
|
||||
public boolean isCraftable() {
|
||||
return craftStackId != null;
|
||||
}
|
||||
|
||||
public IGuiIngredient<ItemStack> getGuiIngredient() {
|
||||
return guiIngredient;
|
||||
}
|
||||
|
||||
public UUID getCraftStackId() {
|
||||
return craftStackId;
|
||||
}
|
||||
|
||||
public void setCraftStackId(UUID craftStackId) {
|
||||
this.craftStackId = craftStackId;
|
||||
}
|
||||
|
||||
public void fulfill(int amount) {
|
||||
fulfilled += amount;
|
||||
}
|
||||
}
|
@@ -0,0 +1,71 @@
|
||||
package com.refinedmods.refinedstorage.integration.jei;
|
||||
|
||||
import com.refinedmods.refinedstorage.api.util.IComparer;
|
||||
import com.refinedmods.refinedstorage.apiimpl.API;
|
||||
import com.refinedmods.refinedstorage.screen.grid.stack.IGridStack;
|
||||
import mezz.jei.api.gui.IRecipeLayout;
|
||||
import mezz.jei.api.gui.ingredient.IGuiIngredient;
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class IngredientTracker {
|
||||
private final List<Ingredient> ingredients = new ArrayList<>();
|
||||
|
||||
public IngredientTracker(IRecipeLayout recipeLayout) {
|
||||
for (IGuiIngredient<ItemStack> guiIngredient : recipeLayout.getItemStacks().getGuiIngredients().values()) {
|
||||
if (guiIngredient.isInput() && !guiIngredient.getAllIngredients().isEmpty()) {
|
||||
ingredients.add(new Ingredient(guiIngredient));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<Ingredient> getIngredients() {
|
||||
return ingredients;
|
||||
}
|
||||
|
||||
public void addAvailableStack(ItemStack stack, IGridStack gridStack) {
|
||||
int available = stack.getCount();
|
||||
|
||||
for (Ingredient ingredient : ingredients) {
|
||||
if (available == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ingredient.isAvailable()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Optional<?> match = ingredient.getGuiIngredient().getAllIngredients().stream().filter((ItemStack matchingStack) -> API.instance().getComparer().isEqual(stack, matchingStack, IComparer.COMPARE_NBT)).findFirst();
|
||||
if (match.isPresent()) {
|
||||
//craftables and non-craftables are 2 different gridstacks
|
||||
//as such we need to ignore craftable stacks as they are not actual items
|
||||
if (gridStack != null && gridStack.isCraftable()) {
|
||||
ingredient.setCraftStackId(gridStack.getId());
|
||||
} else {
|
||||
int needed = ingredient.getMissingAmount();
|
||||
int used = Math.min(available, needed);
|
||||
ingredient.fulfill(used);
|
||||
available -= used;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasMissing() {
|
||||
return ingredients.stream().anyMatch(ingredient -> !ingredient.isAvailable());
|
||||
}
|
||||
|
||||
public Map<UUID, Integer> getCraftingRequests() {
|
||||
Map<UUID, Integer> toRequest = new HashMap<>();
|
||||
|
||||
for (Ingredient ingredient : ingredients) {
|
||||
if (!ingredient.isAvailable() && ingredient.isCraftable()) {
|
||||
toRequest.merge(ingredient.getCraftStackId(), ingredient.getMissingAmount(), Integer::sum);
|
||||
}
|
||||
}
|
||||
|
||||
return toRequest;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,67 @@
|
||||
package com.refinedmods.refinedstorage.integration.jei;
|
||||
|
||||
import com.mojang.blaze3d.matrix.MatrixStack;
|
||||
import com.refinedmods.refinedstorage.util.TemporaryPortingUtils;
|
||||
import mezz.jei.api.gui.IRecipeLayout;
|
||||
import mezz.jei.api.recipe.transfer.IRecipeTransferError;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.screen.Screen;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.text.ITextComponent;
|
||||
import net.minecraft.util.text.TextFormatting;
|
||||
import net.minecraft.util.text.TranslationTextComponent;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class RecipeTransferGridError implements IRecipeTransferError {
|
||||
private static final Color HIGHLIGHT_COLOR = new Color(1.0f, 0.0f, 0.0f, 0.4f);
|
||||
private static final Color HIGHLIGHT_AUTOCRAFT_COLOR = new Color(0.0f, 0.0f, 1.0f, 0.4f);
|
||||
IngredientTracker tracker;
|
||||
|
||||
public RecipeTransferGridError(IngredientTracker tracker) {
|
||||
this.tracker = tracker;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return Type.COSMETIC;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showError(MatrixStack stack, int mouseX, int mouseY, IRecipeLayout recipeLayout, int recipeX, int recipeY) {
|
||||
List<ITextComponent> message = drawIngredientHighlights(stack, tracker, recipeX, recipeY);
|
||||
Screen currentScreen = Minecraft.getInstance().currentScreen;
|
||||
TemporaryPortingUtils.drawHoveringText(ItemStack.EMPTY, stack, message, mouseX, mouseY, currentScreen.width, currentScreen.height, 150, Minecraft.getInstance().fontRenderer);
|
||||
}
|
||||
|
||||
private List<ITextComponent> drawIngredientHighlights(MatrixStack stack, IngredientTracker tracker, int recipeX, int recipeY) {
|
||||
List<ITextComponent> message = new ArrayList<>();
|
||||
message.add(new TranslationTextComponent("jei.tooltip.transfer"));
|
||||
boolean craftMessage = false;
|
||||
boolean missingMessage = false;
|
||||
|
||||
for (Ingredient ingredient : tracker.getIngredients()) {
|
||||
if (!ingredient.isAvailable()) {
|
||||
if (ingredient.isCraftable()) {
|
||||
ingredient.getGuiIngredient().drawHighlight(stack, HIGHLIGHT_AUTOCRAFT_COLOR.getRGB(), recipeX, recipeY);
|
||||
craftMessage = true;
|
||||
} else {
|
||||
ingredient.getGuiIngredient().drawHighlight(stack, HIGHLIGHT_COLOR.getRGB(), recipeX, recipeY);
|
||||
missingMessage = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (missingMessage) {
|
||||
message.add(new TranslationTextComponent("jei.tooltip.error.recipe.transfer.missing").mergeStyle(TextFormatting.RED));
|
||||
}
|
||||
|
||||
if (craftMessage) {
|
||||
message.add(new TranslationTextComponent("gui.refinedstorage.jei.tooltip.error.recipe.transfer.missing.autocraft").mergeStyle(TextFormatting.BLUE));
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
}
|
@@ -32,6 +32,11 @@ public abstract class BaseGridView implements IGridView {
|
||||
return stacks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<IGridStack> getAllStacks() {
|
||||
return map.values();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public IGridStack get(UUID id) {
|
||||
|
@@ -3,6 +3,7 @@ package com.refinedmods.refinedstorage.screen.grid.view;
|
||||
import com.refinedmods.refinedstorage.screen.grid.stack.IGridStack;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@@ -12,6 +13,8 @@ public interface IGridView {
|
||||
@Nullable
|
||||
IGridStack get(UUID id);
|
||||
|
||||
Collection<IGridStack> getAllStacks();
|
||||
|
||||
void setStacks(List<IGridStack> stacks);
|
||||
|
||||
void postChange(IGridStack stack, int delta);
|
||||
|
@@ -88,17 +88,15 @@
|
||||
"gui.refinedstorage.crafter_manager": "Crafter Manager",
|
||||
"gui.refinedstorage.alternatives": "Alternatives",
|
||||
"gui.refinedstorage.alternatives.apply": "Apply",
|
||||
|
||||
"gui.refinedstorage.jei.tooltip.error.recipe.transfer.missing.autocraft": "CTRL + CLICK to request autocrafting",
|
||||
"misc.refinedstorage.energy_stored": "%d / %d FE",
|
||||
"misc.refinedstorage.energy_usage": "Usage: %d FE/t",
|
||||
"misc.refinedstorage.energy_usage_minimal": "%d FE/t",
|
||||
|
||||
"misc.refinedstorage.storage.stored": "Stored: %s",
|
||||
"misc.refinedstorage.storage.stored_capacity": "Stored: %s / %s",
|
||||
"misc.refinedstorage.storage.stored_minimal": "%s",
|
||||
"misc.refinedstorage.storage.stored_capacity_minimal": "%s / %s",
|
||||
"misc.refinedstorage.storage.full": "%d%% full",
|
||||
|
||||
"misc.refinedstorage.network_item.tooltip": "Linked to %d, %d, %d.",
|
||||
"misc.refinedstorage.network_item.out_of_range": "There is no Wireless Transmitter in range.",
|
||||
"misc.refinedstorage.network_item.not_found": "Network not found.",
|
||||
|
Reference in New Issue
Block a user