Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ public class GeneralConfig extends DummyConfig {
public static boolean guiStorageForceCraftingGridCenter = false;
@ConfigurableProperty(category = "general", comment = "If the automatic re-sorting of the storage terminal contents should be paused while the shift key is held down.", isCommandable = true, configLocation = ModConfig.Type.CLIENT)
public static boolean guiStoragePauseSortingWhileShifting = true;
@ConfigurableProperty(category = "general", comment = "If the tooltips in the storage terminal should indicate the channels in which ingredients are available when all channels are shown at once.", isCommandable = true, configLocation = ModConfig.Type.CLIENT)
public static boolean guiStorageTooltipChannels = true;

public GeneralConfig() {
super(IntegratedTerminals._instance, "general");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package org.cyclops.integratedterminals.core.terminalstorage;

import com.google.common.collect.Lists;
import it.unimi.dsi.fastutil.ints.Int2LongLinkedOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2LongMap;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.Component;
import org.cyclops.commoncapabilities.api.ingredient.IIngredientMatcher;
import org.cyclops.commoncapabilities.api.ingredient.IngredientComponent;
import org.cyclops.cyclopscore.ingredient.collection.IIngredientCollection;
import org.cyclops.integratedterminals.api.ingredient.IIngredientComponentTerminalStorageHandler;

import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.function.IntFunction;

/**
* Helpers for determining and displaying the channels in which ingredients are stored.
* @author rubensworks
*/
public final class TerminalStorageChannels {

private TerminalStorageChannels() {}

/**
* Determine the quantity of the given instance within each of the given channels.
* @param ingredientComponent The ingredient component.
* @param channels The channels to look in.
* @param channelIngredients A function to get the stored ingredients of a channel.
* @param instance The instance to look for, its quantity is ignored.
* @param <T> The instance type.
* @param <M> The matching condition parameter.
* @return The quantity per channel, in the order of the given channels, without channels that don't store it.
*/
public static <T, M> Int2LongMap getInstanceQuantitiesPerChannel(IngredientComponent<T, M> ingredientComponent,
int[] channels,
IntFunction<IIngredientCollection<T, M>> channelIngredients,
T instance) {
IIngredientMatcher<T, M> matcher = ingredientComponent.getMatcher();
M matchCondition = matcher.getExactMatchNoQuantityCondition();
Int2LongMap quantities = new Int2LongLinkedOpenHashMap();
for (int channel : channels) {
long quantity = 0;
Iterator<T> it = channelIngredients.apply(channel).iterator(instance, matchCondition);
while (it.hasNext()) {
quantity += matcher.getQuantity(it.next());
}
if (quantity > 0) {
quantities.put(channel, quantity);
}
}
return quantities;
}

/**
* Create the tooltip lines that indicate in which channels an instance is stored.
* @param viewHandler The terminal storage handler of the ingredient component.
* @param instance The instance that is stored, its quantity is ignored.
* @param quantitiesPerChannel The stored quantity per channel,
* as determined by {@link #getInstanceQuantitiesPerChannel}.
* @param <T> The instance type.
* @param <M> The matching condition parameter.
* @return The tooltip lines, which are empty if the instance is not stored in any channel.
*/
public static <T, M> List<Component> createChannelTooltipLines(IIngredientComponentTerminalStorageHandler<T, M> viewHandler,
T instance,
Int2LongMap quantitiesPerChannel) {
List<Component> lines = Lists.newArrayList();
if (quantitiesPerChannel.isEmpty()) {
return lines;
}

if (quantitiesPerChannel.size() == 1) {
lines.add(createChannelLine(quantitiesPerChannel.keySet().iterator().nextInt()));
} else {
IIngredientMatcher<T, M> matcher = viewHandler.getComponent().getMatcher();
lines.add(Component.translatable("gui.integratedterminals.terminal_storage.tooltip.channels")
.withStyle(ChatFormatting.GRAY));
for (Int2LongMap.Entry entry : quantitiesPerChannel.int2LongEntrySet()) {
lines.add(Component.translatable("gui.integratedterminals.terminal_storage.tooltip.channel_quantity",
formatChannel(entry.getIntKey()),
viewHandler.formatQuantity(matcher.withQuantity(instance, entry.getLongValue())))
.withStyle(ChatFormatting.DARK_GRAY));
}
}

return lines;
}

/**
* Create the tooltip line that indicates a single channel.
* @param channel A channel id.
* @return The tooltip line.
*/
public static Component createChannelLine(int channel) {
return Component.translatable("gui.integratedterminals.terminal_storage.tooltip.channel",
formatChannel(channel)).withStyle(ChatFormatting.GRAY);
}

private static String formatChannel(int channel) {
return String.format(Locale.ROOT, "%,d", channel);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.OptionalInt;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
Expand Down Expand Up @@ -326,6 +327,32 @@ public Collection<HandlerWrappedTerminalCraftingOption<T>> getCraftingOptions(in
return craftingOptions.get(channel);
}

/**
* Determine in which channels the given instance is stored, and in which quantity.
* @param instance An instance, its quantity is ignored.
* @return The stored quantity per channel, in ascending channel order.
*/
public Int2LongMap getInstanceQuantitiesPerChannel(T instance) {
return TerminalStorageChannels.getInstanceQuantitiesPerChannel(this.ingredientComponent, getChannels(),
this::getRawUnfilteredIngredientsView, instance);
}

/**
* Determine the channel that the given crafting option is available in.
* @param craftingOption A crafting option.
* @return A channel id, or empty if the crafting option is not available in any channel.
*/
public OptionalInt getCraftingOptionChannel(HandlerWrappedTerminalCraftingOption<T> craftingOption) {
for (int channel : getChannels()) {
Collection<HandlerWrappedTerminalCraftingOption<T>> channeledCraftingOptions = getCraftingOptions(channel);
if (channeledCraftingOptions != null
&& channeledCraftingOptions.stream().anyMatch(option -> option == craftingOption)) {
return OptionalInt.of(channel);
}
}
return OptionalInt.empty();
}

/**
* Called by the server when the outputs that running crafting jobs are still expected to produce have changed.
* @param channel The channel the outputs were collected for.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,17 @@
import net.neoforged.api.distmarker.Dist;
import net.neoforged.api.distmarker.OnlyIn;
import org.apache.commons.lang3.tuple.Triple;
import org.cyclops.cyclopscore.helper.GuiHelpers;
import org.cyclops.cyclopscore.helper.Helpers;
import org.cyclops.integrateddynamics.api.network.IPositionedAddonsNetwork;
import org.cyclops.integratedterminals.GeneralConfig;
import org.cyclops.integratedterminals.api.ingredient.IIngredientComponentTerminalStorageHandler;
import org.cyclops.integratedterminals.api.terminalstorage.ITerminalStorageSlot;
import org.cyclops.integratedterminals.api.terminalstorage.ITerminalStorageTabClient;
import org.cyclops.integratedterminals.client.gui.container.ContainerScreenTerminalStorage;
import org.cyclops.integratedterminals.client.gui.image.Images;
import org.cyclops.integratedterminals.client.gui.tooltip.TooltipRenderHelpers;
import org.cyclops.integratedterminals.core.terminalstorage.TerminalStorageChannels;
import org.cyclops.integratedterminals.core.terminalstorage.TerminalStorageTabIngredientComponentClient;
import org.cyclops.integratedterminals.core.terminalstorage.crafting.PendingCraftingJobOutput;

Expand Down Expand Up @@ -50,8 +55,13 @@ public void drawGuiContainerLayer(AbstractContainerScreen gui, GuiGraphics guiGr
ITerminalStorageTabClient tab, int channel, @Nullable String label) {
long maxQuantity = ((TerminalStorageTabIngredientComponentClient) tab).getMaxQuantity(channel);
PendingCraftingJobOutput<T> pendingCraftingJobOutput = getPendingCraftingJobOutput(tab, channel, label);
// This is called for all visible slots on every frame,
// so only determine the tooltip lines when they are actually going to be shown.
List<Component> tooltipLines = layer == ContainerScreenTerminalStorage.DrawLayer.FOREGROUND
&& TooltipRenderHelpers.isHovering(gui, x, y, GuiHelpers.SLOT_SIZE_INNER, GuiHelpers.SLOT_SIZE_INNER, mouseX, mouseY)
? createTooltipLines(pendingCraftingJobOutput, tab, channel, label) : null;
ingredientComponentViewHandler.drawInstance(guiGraphics, instance, maxQuantity, label, gui, layer, partialTick, x, y, mouseX, mouseY,
createCraftingJobTooltipLines(pendingCraftingJobOutput));
tooltipLines);
drawCraftingJobOverlay(guiGraphics, layer, x, y, pendingCraftingJobOutput);
}

Expand Down Expand Up @@ -82,17 +92,49 @@ protected PendingCraftingJobOutput<T> getPendingCraftingJobOutput(ITerminalStora
: null;
}

@Nullable
@OnlyIn(Dist.CLIENT)
protected List<Component> createCraftingJobTooltipLines(@Nullable PendingCraftingJobOutput<T> pendingCraftingJobOutput) {
if (pendingCraftingJobOutput == null) {
return null;
}
protected List<Component> createTooltipLines(@Nullable PendingCraftingJobOutput<T> pendingCraftingJobOutput,
ITerminalStorageTabClient tab, int channel, @Nullable String label) {
List<Component> tooltipLines = Lists.newArrayList();
addCraftingJobTooltipLines(tooltipLines, pendingCraftingJobOutput);
if (pendingCraftingJobOutput != null) {
addCraftingJobTooltipLines(tooltipLines, pendingCraftingJobOutput);
}
addChannelTooltipLines(tooltipLines, tab, channel, label);
return tooltipLines;
}

/**
* Add the tooltip lines that indicate in which channels this slot's instance is available.
*
* These are only shown when all channels are shown at once,
* as the channel is already known when a single channel is shown.
*
* @param tooltipLines The tooltip lines to append to.
* @param tab The tab this slot is being rendered in.
* @param channel The channel this slot is being rendered in.
* @param label An optional label that is rendered instead of the quantity.
* Slots with such a label are not part of the storage overview,
* so they don't get a channel indication.
*/
@OnlyIn(Dist.CLIENT)
protected void addChannelTooltipLines(List<Component> tooltipLines, ITerminalStorageTabClient tab,
int channel, @Nullable String label) {
if (GeneralConfig.guiStorageTooltipChannels && label == null
&& channel == IPositionedAddonsNetwork.WILDCARD_CHANNEL) {
tooltipLines.addAll(createChannelTooltipLines(tab));
}
}

/**
* @param tab The tab this slot is being rendered in.
* @return The tooltip lines indicating the channels in which this slot's instance is available.
*/
@OnlyIn(Dist.CLIENT)
protected List<Component> createChannelTooltipLines(ITerminalStorageTabClient tab) {
return TerminalStorageChannels.createChannelTooltipLines(getIngredientComponentViewHandler(), getInstance(),
((TerminalStorageTabIngredientComponentClient<T, M>) tab).getInstanceQuantitiesPerChannel(getInstance()));
}

@OnlyIn(Dist.CLIENT)
protected void addCraftingJobTooltipLines(List<Component> tooltipLines,
PendingCraftingJobOutput<T> pendingCraftingJobOutput) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package org.cyclops.integratedterminals.core.terminalstorage.slot;

import com.google.common.collect.Lists;
import net.minecraft.ChatFormatting;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiGraphics;
Expand All @@ -18,13 +17,15 @@
import org.cyclops.integratedterminals.client.gui.container.component.GuiCraftingPlan;
import org.cyclops.integratedterminals.client.gui.tooltip.CraftingOptionIngredientsTooltip;
import org.cyclops.integratedterminals.client.gui.tooltip.TooltipRenderHelpers;
import org.cyclops.integratedterminals.core.terminalstorage.TerminalStorageChannels;
import org.cyclops.integratedterminals.core.terminalstorage.TerminalStorageTabIngredientComponentClient;
import org.cyclops.integratedterminals.core.terminalstorage.crafting.HandlerWrappedTerminalCraftingOption;
import org.cyclops.integratedterminals.core.terminalstorage.crafting.PendingCraftingJobOutput;
import org.cyclops.integratedterminals.core.terminalstorage.crafting.TerminalCraftingOptionInputs;

import javax.annotation.Nullable;
import java.util.List;
import java.util.OptionalInt;

/**
* An ingredient slot for a crafting option.
Expand Down Expand Up @@ -54,24 +55,22 @@ public void drawGuiContainerLayer(AbstractContainerScreen gui, GuiGraphics guiGr
drawCraftLabel(guiGraphics, x, y);
} else {
// This is called for all visible slots on every frame,
// so only determine the requirements when they are actually going to be shown.
List<List<IPrototypedIngredient<?, ?>>> inputs = TooltipRenderHelpers.isHovering(gui, x, y,
GuiHelpers.SLOT_SIZE_INNER, GuiHelpers.SLOT_SIZE_INNER, mouseX, mouseY)
? getInputs() : List.of();
// so only determine the tooltip contents when they are actually going to be shown.
boolean hovering = TooltipRenderHelpers.isHovering(gui, x, y,
GuiHelpers.SLOT_SIZE_INNER, GuiHelpers.SLOT_SIZE_INNER, mouseX, mouseY);
List<List<IPrototypedIngredient<?, ?>>> inputs = hovering ? getInputs() : List.of();
viewHandler.drawInstance(guiGraphics, getInstance(), maxQuantity, label, gui, layer, partialTick, x, y, mouseX, mouseY,
getTooltipLines(pendingCraftingJobOutput, inputs),
hovering ? getTooltipLines(pendingCraftingJobOutput, inputs, tab, channel, label) : null,
inputs.isEmpty() ? null : new CraftingOptionIngredientsTooltip(inputs));
}
drawCraftingJobOverlay(guiGraphics, layer, x, y, pendingCraftingJobOutput);
}

@OnlyIn(Dist.CLIENT)
protected List<Component> getTooltipLines(@Nullable PendingCraftingJobOutput<T> pendingCraftingJobOutput,
List<List<IPrototypedIngredient<?, ?>>> inputs) {
List<Component> tooltipLines = Lists.newArrayList();
if (pendingCraftingJobOutput != null) {
addCraftingJobTooltipLines(tooltipLines, pendingCraftingJobOutput);
}
List<List<IPrototypedIngredient<?, ?>>> inputs,
ITerminalStorageTabClient tab, int channel, @Nullable String label) {
List<Component> tooltipLines = createTooltipLines(pendingCraftingJobOutput, tab, channel, label);
// An unknown duration says nothing here, so it is left out rather than shown as a placeholder
long estimatedTickDuration = getCraftingOption().getCraftingOption().getEstimatedTickDuration();
if (estimatedTickDuration >= 0) {
Expand All @@ -85,6 +84,17 @@ protected List<Component> getTooltipLines(@Nullable PendingCraftingJobOutput<T>
return tooltipLines;
}

@Override
@OnlyIn(Dist.CLIENT)
protected List<Component> createChannelTooltipLines(ITerminalStorageTabClient tab) {
// Contrary to stored ingredients, a crafting option is only available in a single channel.
OptionalInt channel = ((TerminalStorageTabIngredientComponentClient<T, M>) tab)
.getCraftingOptionChannel(getCraftingOption());
return channel.isPresent()
? List.of(TerminalStorageChannels.createChannelLine(channel.getAsInt()))
: List.of();
}

/**
* @return The inputs that are required by this crafting option, with all their alternatives.
*/
Expand Down
Loading
Loading