Files
Applied-Energistics-2-Unoff…/src/main/java/appeng/client/gui/implementations/GuiMEMonitorable.java
T

1254 lines
48 KiB
Java

/*
* This file is part of Applied Energistics 2. Copyright (c) 2013 - 2015, AlgorithmX2, All rights reserved. Applied
* Energistics 2 is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General
* Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
* later version. Applied Energistics 2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
* Public License for more details. You should have received a copy of the GNU Lesser General Public License along with
* Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.client.gui.implementations;
import static appeng.util.item.AEItemStackType.ITEM_STACK_TYPE;
import java.io.IOException;
import java.text.NumberFormat;
import java.util.EnumSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import javax.annotation.Nullable;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.InventoryPlayer;
import net.minecraft.inventory.Slot;
import net.minecraft.item.ItemStack;
import net.minecraft.util.EnumChatFormatting;
import net.minecraft.util.IIcon;
import net.minecraft.util.ResourceLocation;
import org.jetbrains.annotations.NotNull;
import org.lwjgl.input.Keyboard;
import org.lwjgl.input.Mouse;
import appeng.api.config.ActionItems;
import appeng.api.config.CraftingStatus;
import appeng.api.config.PinSectionOrder;
import appeng.api.config.PinsRows;
import appeng.api.config.SearchBoxFocusPriority;
import appeng.api.config.SearchBoxMode;
import appeng.api.config.Settings;
import appeng.api.config.TerminalFontSize;
import appeng.api.config.TerminalStyle;
import appeng.api.config.ViewItems;
import appeng.api.config.YesNo;
import appeng.api.implementations.tiles.IViewCellStorage;
import appeng.api.storage.ITerminalHost;
import appeng.api.storage.ITerminalPins;
import appeng.api.storage.ITerminalTypeFilterProvider;
import appeng.api.storage.data.AEStackTypeRegistry;
import appeng.api.storage.data.IAEItemStack;
import appeng.api.storage.data.IAEStack;
import appeng.api.storage.data.IAEStackType;
import appeng.api.util.IConfigManager;
import appeng.api.util.IConfigurableObject;
import appeng.client.ActionKey;
import appeng.client.gui.AEBaseGui;
import appeng.client.gui.slots.VirtualMEMonitorableSlot;
import appeng.client.gui.slots.VirtualMEPatternSlot;
import appeng.client.gui.slots.VirtualMEPinSlot;
import appeng.client.gui.slots.VirtualMESlot;
import appeng.client.gui.widgets.GuiImgButton;
import appeng.client.gui.widgets.GuiScrollbar;
import appeng.client.gui.widgets.GuiTabButton;
import appeng.client.gui.widgets.IDropToFillTextField;
import appeng.client.gui.widgets.ISortSource;
import appeng.client.gui.widgets.MEGuiTextField;
import appeng.client.gui.widgets.TypeToggleButton;
import appeng.client.me.ItemRepo;
import appeng.container.AEBaseContainer;
import appeng.container.implementations.ContainerMEMonitorable;
import appeng.container.slot.AppEngSlot;
import appeng.container.slot.SlotCraftingMatrix;
import appeng.container.slot.SlotFakeCraftingMatrix;
import appeng.container.slot.SlotRestrictedInput;
import appeng.core.AEConfig;
import appeng.core.AELog;
import appeng.core.CommonHelper;
import appeng.core.localization.ButtonToolTips;
import appeng.core.localization.ColorUtils;
import appeng.core.localization.GuiText;
import appeng.core.sync.GuiBridge;
import appeng.core.sync.network.NetworkHandler;
import appeng.core.sync.packets.PacketInventoryAction;
import appeng.core.sync.packets.PacketMonitorableAction;
import appeng.core.sync.packets.PacketMonitorableTypeFilter;
import appeng.core.sync.packets.PacketPinsUpdate;
import appeng.core.sync.packets.PacketSwitchGuis;
import appeng.core.sync.packets.PacketValueConfig;
import appeng.helpers.IPinsHandler;
import appeng.helpers.InventoryAction;
import appeng.helpers.MonitorableAction;
import appeng.integration.IntegrationRegistry;
import appeng.integration.IntegrationType;
import appeng.integration.modules.NEI;
import appeng.items.storage.ItemViewCell;
import appeng.me.cache.ItemFlowGridCache.FlowRate;
import appeng.util.AEStackTypeFilter;
import appeng.util.FlowRateFormatter;
import appeng.util.IConfigManagerHost;
import appeng.util.Platform;
import appeng.util.item.AEItemStack;
import it.unimi.dsi.fastutil.objects.Reference2BooleanMap;
public class GuiMEMonitorable extends AEBaseGui
implements ISortSource, IConfigManagerHost, IDropToFillTextField, IPinsHandler {
public static int craftingGridOffsetX;
public static int craftingGridOffsetY;
// Keybind use can be keyboard or mouse, both redirect to this.handleVirtualSlotClick
// if key/mouseButton == this.mc.gameSettings.keyBindPickBlock.getKeyCode()
public static final int keyBindPickBlockAction = 1_000_101;
private static String memoryText = "";
private final ItemRepo repo;
protected int offsetRepoX = 9;
protected int offsetRepoY = 18;
private final int MAGIC_HEIGHT_NUMBER = 114 + 1;
private final int lowerTextureOffset = 0;
private final IConfigManager configSrc;
private final boolean viewCell;
private final ItemStack[] myCurrentViewCells = new ItemStack[5];
private final ContainerMEMonitorable monitorableContainer;
private GuiTabButton craftingStatusBtn;
private GuiImgButton craftingStatusImgBtn;
protected final MEGuiTextField searchField;
private GuiText myName;
private int perRow = 9;
private int reservedSpace = 0;
private boolean customSortOrder = true;
protected int rows = 0;
private int standardSize;
private GuiImgButton ViewBox;
private GuiImgButton SortByBox;
private GuiImgButton SortDirBox;
private GuiImgButton searchBoxSettings;
private GuiImgButton terminalStyleBox;
private GuiImgButton searchStringSave;
private GuiImgButton pinsStateButton;
private final Map<TypeToggleButton, IAEStackType<?>> typeToggleButtons = new IdentityHashMap<>();
private boolean canBeAutoFocused = false;
private boolean isAutoFocused = false;
protected int currentMouseX = 0;
protected int currentMouseY = 0;
private PinsRows craftingPinsRows;
private PinsRows playerPinsRows;
public final boolean hasPinHost;
private boolean enableShiftPause = true;
private boolean needsViewUpdate = false;
private int scrollBarRepoSize = -1;
protected VirtualMEPinSlot[] pinSlots = null;
protected VirtualMEMonitorableSlot[] monitorableSlots = null;
@Nullable
protected AEStackTypeFilter typeFilters;
public GuiMEMonitorable(final InventoryPlayer inventoryPlayer, final ITerminalHost te) {
this(inventoryPlayer, te, new ContainerMEMonitorable(inventoryPlayer, te));
}
public GuiMEMonitorable(final InventoryPlayer inventoryPlayer, final ITerminalHost te,
final ContainerMEMonitorable c) {
super(c);
final GuiScrollbar scrollbar = new GuiScrollbar();
this.setScrollBar(scrollbar);
this.repo = new ItemRepo(scrollbar, this);
this.xSize = 195;
this.ySize = 204;
this.standardSize = this.xSize;
this.configSrc = ((IConfigurableObject) this.inventorySlots).getConfigManager();
craftingPinsRows = PinsRows.DISABLED;
playerPinsRows = PinsRows.DISABLED;
(this.monitorableContainer = (ContainerMEMonitorable) this.inventorySlots).setGui(this);
this.viewCell = te instanceof IViewCellStorage;
this.myName = te.getHostName();
hasPinHost = te instanceof ITerminalPins;
this.searchField = new MEGuiTextField(90, 12, ButtonToolTips.SearchStringTooltip.getLocal()) {
@Override
public void onTextChange(final String oldText) {
final String text = getText();
repo.setSearchString(text.trim());
repo.updateView();
if (AEConfig.instance.preserveSearchBar) monitorableContainer.saveSearchString(this.getText());
}
};
NEI.searchField.putFormatter(this.searchField);
if (te instanceof ITerminalTypeFilterProvider) {
this.typeFilters = new AEStackTypeFilter();
}
}
public void postUpdate(final List<IAEStack<?>> list) {
for (final IAEStack<?> is : list) {
this.repo.postUpdate(is);
}
this.needsViewUpdate = true;
}
public void updateFlowRates(final Map<IAEStack<?>, FlowRate> rates) {
this.repo.updateFlowRates(rates);
this.needsViewUpdate = true;
}
private void setScrollBar() {
this.scrollBarRepoSize = this.repo.size();
this.getScrollBar().setTop(this.offsetRepoY).setLeft(166 + this.offsetRepoX).setHeight(this.rows * 18 - 2);
int totalPinRows = craftingPinsRows.ordinal() + playerPinsRows.ordinal();
this.getScrollBar().setRange(
0,
(this.repo.size() + totalPinRows * 9 + this.perRow - 1) / this.perRow - this.rows,
Math.max(1, this.rows / 6));
}
@Override
protected void actionPerformed(final GuiButton btn) {
if (btn instanceof final TypeToggleButton tbtn) {
final IAEStackType<?> type = this.typeToggleButtons.get(tbtn);
if (type != null && this.typeFilters != null) {
// Optimistic client-side feedback; server will sync authoritative state shortly.
final boolean next = !this.typeFilters.isEnabled(type);
this.typeFilters.setEnabled(type, next);
tbtn.setEnabled(next);
this.repo.updateView();
try {
NetworkHandler.instance
.sendToServer(new PacketMonitorableTypeFilter(this.typeFilters.getImmutableFilters(), this.inventorySlots.windowId));
} catch (final IOException e) {
AELog.debug(e);
}
}
return;
}
if (actionPerformedCustomButtons(btn)) return;
if (btn == this.craftingStatusBtn || btn == this.craftingStatusImgBtn) {
NetworkHandler.instance.sendToServer(new PacketSwitchGuis(GuiBridge.GUI_CRAFTING_STATUS));
}
if (btn == this.pinsStateButton) {
try {
boolean rmb = Mouse.isButtonDown(1);
boolean ctrl = GuiScreen.isCtrlKeyDown();
int maxC = AEConfig.instance.maxCraftingPinRows;
int maxP = AEConfig.instance.maxPlayerPinRows;
int c = Math.min(craftingPinsRows.ordinal(), maxC);
int p = Math.min(playerPinsRows.ordinal(), maxP);
if (ctrl) {
if (rmb) c = Math.max(0, c - 1);
else c = Math.min(maxC, c + 1);
} else {
if (rmb) p = Math.max(0, p - 1);
else p = Math.min(maxP, p + 1);
}
if (c + p >= rows) return;
NetworkHandler.instance.sendToServer(new PacketPinsUpdate(
PinsRows.fromOrdinal(c), PinsRows.fromOrdinal(p)));
} catch (final IOException e) {
AELog.debug(e);
}
reinitalize();
return;
}
if (!(btn instanceof GuiImgButton iBtn) || iBtn.getSetting() == Settings.ACTIONS) return;
final Enum cv = iBtn.getCurrentValue();
final boolean backwards = Mouse.isButtonDown(1);
EnumSet<?> validOptions = iBtn.getSetting().getPossibleValues();
if (btn == this.ViewBox && !this.monitorableContainer.flowTrackingActive) {
validOptions = EnumSet.copyOf(validOptions);
validOptions.remove(ViewItems.FLOWING);
}
final Enum next = Platform.rotateEnum(cv, backwards, validOptions);
if (btn == this.terminalStyleBox) {
AEConfig.instance.settings.putSetting(iBtn.getSetting(), next);
} else if (btn == this.searchBoxSettings) {
AEConfig.instance.settings.putSetting(iBtn.getSetting(), next);
} else if (btn == this.searchStringSave) {
AEConfig.instance.preserveSearchBar = next == YesNo.YES;
} else {
try {
NetworkHandler.instance.sendToServer(new PacketValueConfig(iBtn.getSetting().name(), next.name()));
} catch (final IOException e) {
AELog.debug(e);
}
}
iBtn.set(next);
if (next.getClass() == SearchBoxMode.class || next.getClass() == TerminalStyle.class) {
this.reinitalize();
}
}
private int createPinSection(int slotIdx, int sectionRows, int pinsPerRow, int rowOffset, boolean isCrafting) {
int baseIndex = isCrafting ? 0 : appeng.items.contents.PinList.PLAYER_OFFSET;
for (int y = 0; y < sectionRows; y++) {
for (int x = 0; x < pinsPerRow; x++) {
VirtualMEPinSlot slot = new VirtualMEPinSlot(
this.offsetRepoX + x * 18,
(rowOffset + y) * 18 + this.offsetRepoY,
this.repo,
baseIndex + y * pinsPerRow + x,
this::checkTypeFilter,
isCrafting);
this.pinSlots[slotIdx++] = slot;
this.registerVirtualSlots(slot);
}
}
return slotIdx;
}
private void adjustPinsSize() {
final int pinMaxSize = rows - 1;
int craft = Math.min(craftingPinsRows.ordinal(), AEConfig.instance.maxCraftingPinRows);
int player = Math.min(playerPinsRows.ordinal(), AEConfig.instance.maxPlayerPinRows);
int totalPinRows = craft + player;
if (totalPinRows <= pinMaxSize) return;
try {
if (player > pinMaxSize) {
player = pinMaxSize;
craft = 0;
} else {
craft = Math.min(craft, pinMaxSize - player);
}
NetworkHandler.instance
.sendToServer(new PacketPinsUpdate(PinsRows.fromOrdinal(craft), PinsRows.fromOrdinal(player)));
} catch (final IOException e) {
AELog.debug(e);
}
}
@Override
protected void reinitalize() {
memoryText = this.searchField.getText();
super.reinitalize();
}
private boolean checkTypeFilter(IAEStackType<?> type) {
return this.typeFilters == null || this.typeFilters.isEnabled(type);
}
@Override
public void initGui() {
Keyboard.enableRepeatEvents(true);
this.perRow = AEConfig.instance.getConfigManager().getSetting(Settings.TERMINAL_STYLE) != TerminalStyle.FULL ? 9
: 9 + ((this.width - this.standardSize) / 18);
this.rows = calculateRowsCount();
// make sure we have space at least for one row of normal slots, because pins not adjusted by scroll bar
adjustPinsSize();
super.initGui();
int maxCrafting = AEConfig.instance.maxCraftingPinRows;
int maxPlayer = AEConfig.instance.maxPlayerPinRows;
int craftingRows = Math.min(craftingPinsRows.ordinal(), maxCrafting);
int playerRows = Math.min(playerPinsRows.ordinal(), maxPlayer);
int pinMaxSize = Math.max(0, this.rows - 1);
int totalRequested = craftingRows + playerRows;
if (totalRequested > pinMaxSize) {
if (playerRows > pinMaxSize) {
playerRows = pinMaxSize;
craftingRows = 0;
} else {
craftingRows = Math.min(craftingRows, pinMaxSize - playerRows);
}
}
int pinsRows = craftingRows + playerRows;
final int pinsPerRow = 9;
this.pinSlots = new VirtualMEPinSlot[craftingRows * pinsPerRow + playerRows * pinsPerRow];
int slotIdx = 0;
boolean playerFirst = AEConfig.instance.pinSectionOrder == PinSectionOrder.PLAYER_FIRST;
int firstRows = playerFirst ? playerRows : craftingRows;
int secondRows = playerFirst ? craftingRows : playerRows;
boolean firstIsCrafting = !playerFirst;
slotIdx = createPinSection(slotIdx, firstRows, pinsPerRow, 0, firstIsCrafting);
slotIdx = createPinSection(slotIdx, secondRows, pinsPerRow, firstRows, !firstIsCrafting);
int normalSlotRows = Math.max(0, this.rows - pinsRows);
this.monitorableSlots = new VirtualMEMonitorableSlot[normalSlotRows * this.perRow];
for (int y = 0; y < normalSlotRows; y++) {
for (int x = 0; x < this.perRow; x++) {
VirtualMEMonitorableSlot slot = new VirtualMEMonitorableSlot(
this.offsetRepoX + x * 18,
this.offsetRepoY + y * 18 + pinsRows * 18,
this.repo,
y * this.perRow + x,
this::checkTypeFilter);
this.monitorableSlots[y * this.perRow + x] = slot;
this.registerVirtualSlots(slot);
}
}
if (AEConfig.instance.getConfigManager().getSetting(Settings.TERMINAL_STYLE) != TerminalStyle.FULL) {
this.xSize = this.standardSize + ((this.perRow - 9) * 18);
} else {
this.xSize = this.standardSize;
}
// full size : 204
// extra slots : 72
// slot 18
this.ySize = MAGIC_HEIGHT_NUMBER + this.rows * 18 + this.reservedSpace;
final int unusedSpace = this.height - this.ySize;
this.guiTop = (int) Math.floor(unusedSpace / (unusedSpace < 0 ? 3.8f : 2.0f));
int offset = this.guiTop + 8;
buttonList.clear();
initTypeToggleButtons(this.guiLeft - 36, offset);
if (this.customSortOrder) {
this.buttonList.add(
this.SortByBox = new GuiImgButton(
this.guiLeft - 18,
offset,
Settings.SORT_BY,
this.configSrc.getSetting(Settings.SORT_BY)));
offset += 20;
}
if (this.viewCell) {
this.buttonList.add(
this.ViewBox = new GuiImgButton(
this.guiLeft - 18,
offset,
Settings.VIEW_MODE,
this.configSrc.getSetting(Settings.VIEW_MODE)));
offset += 20;
}
this.buttonList.add(
this.SortDirBox = new GuiImgButton(
this.guiLeft - 18,
offset,
Settings.SORT_DIRECTION,
this.configSrc.getSetting(Settings.SORT_DIRECTION)));
offset += 20;
this.buttonList.add(
this.searchBoxSettings = new GuiImgButton(
this.guiLeft - 18,
offset,
Settings.SEARCH_MODE,
AEConfig.instance.settings.getSetting(Settings.SEARCH_MODE)));
offset += 20;
this.buttonList.add(
this.searchStringSave = new GuiImgButton(
this.guiLeft - 18,
offset,
Settings.SAVE_SEARCH,
AEConfig.instance.preserveSearchBar ? YesNo.YES : YesNo.NO));
offset += 20;
if (!(this instanceof GuiMEPortableCell)) {
this.buttonList.add(
this.terminalStyleBox = new GuiImgButton(
this.guiLeft - 18,
offset,
Settings.TERMINAL_STYLE,
AEConfig.instance.settings.getSetting(Settings.TERMINAL_STYLE)));
offset += 20;
}
initCustomButtons(this.guiLeft - 18, offset);
if (this.viewCell) {
if (AEConfig.instance.getConfigManager().getSetting(Settings.CRAFTING_STATUS)
.equals(CraftingStatus.BUTTON)) {
this.buttonList.add(
this.craftingStatusImgBtn = new GuiImgButton(
this.guiLeft - 18,
offset,
Settings.CRAFTING_STATUS,
AEConfig.instance.settings.getSetting(Settings.CRAFTING_STATUS)));
} else {
this.buttonList.add(
this.craftingStatusBtn = new GuiTabButton(
this.guiLeft + 170,
this.guiTop - 4,
2 + 11 * 16,
GuiText.CraftingStatus.getLocal(),
itemRender));
this.craftingStatusBtn.setHideEdge(13); // GuiTabButton implementation //
}
}
if (hasPinHost) {
this.buttonList.add(
this.pinsStateButton = new GuiImgButton(
getPinButtonX(),
getPinButtonY(),
Settings.ACTIONS,
ActionItems.PINS));
this.repo.setVisiblePinRows(this.craftingPinsRows.ordinal(), this.playerPinsRows.ordinal());
}
// Enum setting = AEConfig.INSTANCE.getSetting( "Terminal", SearchBoxMode.class, SearchBoxMode.AUTOSEARCH );
final Enum searchMode = AEConfig.instance.settings.getSetting(Settings.SEARCH_MODE);
this.canBeAutoFocused = SearchBoxMode.AUTOSEARCH == searchMode || SearchBoxMode.NEI_AUTOSEARCH == searchMode;
this.searchField.x = this.guiLeft + this.offsetRepoX + 71;
this.searchField.y = this.guiTop + 4;
this.searchField.setFocused(this.canBeAutoFocused);
this.isAutoFocused = this.canBeAutoFocused;
if (this.isSubGui()) {
this.searchField.setText(memoryText);
} else if (AEConfig.instance.preserveSearchBar) {
final String localMemoryText = this.monitorableContainer.getSavedSearchString();
final String mem = localMemoryText == null ? memoryText : localMemoryText;
this.searchField.setText(mem, true);
repo.setSearchString(mem);
}
this.searchField.setCursorPositionEnd();
this.setScrollBar();
craftingGridOffsetX = Integer.MAX_VALUE;
craftingGridOffsetY = Integer.MAX_VALUE;
for (final Object s : this.inventorySlots.inventorySlots) {
if (s instanceof AppEngSlot) {
if (((Slot) s).xDisplayPosition < 197) {
this.repositionSlot((AppEngSlot) s);
}
}
if (s instanceof SlotCraftingMatrix || s instanceof SlotFakeCraftingMatrix) {
final Slot g = (Slot) s;
if (g.xDisplayPosition > 0 && g.yDisplayPosition > 0) {
craftingGridOffsetX = Math.min(craftingGridOffsetX, g.xDisplayPosition);
craftingGridOffsetY = Math.min(craftingGridOffsetY, g.yDisplayPosition);
}
}
}
craftingGridOffsetX -= 25;
craftingGridOffsetY -= 6;
this.enableShiftPause = AEConfig.instance.settings.getSetting(Settings.PAUSE_WHEN_HOLDING_SHIFT) == YesNo.YES;
}
private void initTypeToggleButtons(final int x, final int yStart) {
this.typeToggleButtons.clear();
if (this.typeFilters == null) return;
int y = yStart;
for (final IAEStackType<?> type : AEStackTypeRegistry.getSortedTypes()) {
final ResourceLocation texture = type.getButtonTexture();
final IIcon icon = type.getButtonIcon();
if (texture == null || icon == null) continue;
final TypeToggleButton btn = new TypeToggleButton(x, y, texture, icon, type.getDisplayName());
btn.setEnabled(this.typeFilters.isEnabled(type));
this.typeToggleButtons.put(btn, type);
this.buttonList.add(btn);
y += 20;
}
}
protected int calculateRowsCount() {
final boolean hasNEI = IntegrationRegistry.INSTANCE.isEnabled(IntegrationType.NEI);
final int NEIPadding = hasNEI ? 22
/** input */
+ 20
/** top panel */
: 0;
final int extraSpace = this.height - MAGIC_HEIGHT_NUMBER - NEIPadding - this.reservedSpace;
return Math.max(3, Math.min(this.getMaxRows(), (int) Math.floor(extraSpace / 18)));
}
protected int getPinButtonX() {
return this.guiLeft + 178;
}
protected int getPinButtonY() {
return this.guiTop + 18 + (rows * 18) + 25;
}
@Override
public void drawFG(final int offsetX, final int offsetY, final int mouseX, final int mouseY) {
this.fontRendererObj.drawString(
this.getGuiDisplayName(this.myName.getLocal()),
8,
6,
ColorUtils.guiTextColorGray.getColor());
this.fontRendererObj.drawString(
GuiText.inventory.getLocal(),
8,
this.ySize - 96 + 3,
ColorUtils.guiTextColorGray.getColor());
VirtualMEPinSlot.drawSlotsBackground(this.pinSlots, this.mc, this.zLevel);
this.currentMouseX = mouseX;
this.currentMouseY = mouseY;
}
@Override
public List<String> handleItemTooltip(ItemStack stack, int mouseX, int mouseY, List<String> currentToolTip) {
super.handleItemTooltip(stack, mouseX, mouseY, currentToolTip);
VirtualMESlot hoveredSlot = this.getVirtualMESlotUnderMouse();
final boolean isMonitorableSlot = hoveredSlot instanceof VirtualMEMonitorableSlot;
if (isMonitorableSlot || hoveredSlot instanceof VirtualMEPatternSlot) {
IAEStack<?> aes = hoveredSlot.getAEStack();
final int threshold = AEConfig.instance.getTerminalFontSize() == TerminalFontSize.SMALL ? 9999 : 999;
if (aes != null) {
if (aes.getStackSize() > threshold) {
final String local = isMonitorableSlot ? ButtonToolTips.ItemsStored.getLocal()
: ButtonToolTips.ItemCount.getLocal();
final String formattedAmount = NumberFormat.getNumberInstance(Locale.US).format(aes.getStackSize());
currentToolTip.add(EnumChatFormatting.GRAY + String.format(local, formattedAmount));
}
final FlowRate rate = this.repo.getFlowRate(aes);
if (rate != null && (rate.in() != 0 || rate.out() != 0)) {
currentToolTip.add(EnumChatFormatting.GRAY + FlowRateFormatter.formatTooltipLine(rate));
}
}
}
return currentToolTip;
}
@Override
protected void mouseClicked(final int xCoord, final int yCoord, final int btn) {
searchField.mouseClicked(xCoord, yCoord, btn);
isAutoFocused = false;
if (handleViewCellClick(xCoord, yCoord, btn)) return;
super.mouseClicked(xCoord, yCoord, btn);
}
private void sendAction(MonitorableAction action, @Nullable IAEStack<?> stack, int custom) {
((AEBaseContainer) this.inventorySlots).setTargetStack(stack);
final PacketMonitorableAction p = new PacketMonitorableAction(action, custom);
NetworkHandler.instance.sendToServer(p);
}
@Override
protected boolean handleVirtualSlotClick(VirtualMESlot virtualSlot, final int mouseButton) {
if (this.handleMonitorableSlotClick(virtualSlot, mouseButton)) return true;
else return super.handleVirtualSlotClick(virtualSlot, mouseButton);
}
@Override
protected boolean canDragVirtualSlot(VirtualMESlot slot, @Nullable ItemStack holding) {
return ((Keyboard.isKeyDown(Keyboard.KEY_SPACE) || isShiftKeyDown())
&& slot instanceof VirtualMEMonitorableSlot) || super.canDragVirtualSlot(slot, holding);
}
@Override
protected void handleDragVirtualSlot(VirtualMESlot slot, int mouseButton) {
if ((!Keyboard.isKeyDown(Keyboard.KEY_SPACE) && !isShiftKeyDown())
|| !this.handleMonitorableSlotClick(slot, mouseButton)) {
super.handleDragVirtualSlot(slot, mouseButton);
}
}
private boolean handleMonitorableSlotClick(VirtualMESlot virtualSlot, final int mouseButton) {
if (!(virtualSlot instanceof VirtualMEMonitorableSlot slot)) return false;
IAEItemStack slotStack = slot.getAEStack() instanceof IAEItemStack ais ? ais : null;
if (Keyboard.isKeyDown(Keyboard.KEY_SPACE)) {
if (slotStack != null) {
this.sendAction(MonitorableAction.MOVE_REGION, slotStack, -1);
return true;
}
return false;
}
final EntityPlayer player = Minecraft.getMinecraft().thePlayer;
final boolean isLShiftDown = isShiftKeyDown();
final boolean isLControlDown = isCtrlKeyDown();
final boolean nonItemInteraction = isLControlDown
|| (this.typeFilters != null && !this.typeFilters.isEnabled(ITEM_STACK_TYPE));
switch (mouseButton) {
case 0 -> { // left click
if (slot instanceof VirtualMEPinSlot pinSlot) {
if (this.handleSetPinAction(pinSlot)) {
return true;
}
}
if (nonItemInteraction) {
this.sendAction(
isLShiftDown ? MonitorableAction.FILL_CONTAINERS : MonitorableAction.FILL_SINGLE_CONTAINER,
slot.getAEStack(),
-1);
return true;
}
if (isLShiftDown) {
this.sendAction(MonitorableAction.SHIFT_CLICK, slotStack, -1);
return true;
}
if (slot.getAEStack() != null && slot.getAEStack().getStackSize() == 0
&& player.inventory.getItemStack() == null) {
this.sendAction(MonitorableAction.AUTO_CRAFT, slot.getAEStack(), -1);
return true;
}
this.sendAction(MonitorableAction.PICKUP_OR_SET_DOWN, slotStack, -1);
return true;
}
case 1 -> { // right click
if (slot instanceof VirtualMEPinSlot pinSlot) {
if (isLShiftDown && player.inventory.getItemStack() == null) {
this.sendAction(MonitorableAction.UNSET_PIN, null, slot.getSlotIndex());
return true;
}
if (this.handleSetPinAction(pinSlot)) {
return true;
}
}
if (nonItemInteraction) {
this.sendAction(
isLShiftDown ? MonitorableAction.DRAIN_CONTAINERS
: MonitorableAction.DRAIN_SINGLE_CONTAINER,
slot.getAEStack(),
-1);
return true;
}
if (isLShiftDown) {
this.sendAction(MonitorableAction.PICKUP_SINGLE, slotStack, -1);
return true;
}
this.sendAction(MonitorableAction.SPLIT_OR_PLACE_SINGLE, slotStack, -1);
return true;
}
case keyBindPickBlockAction -> {
if (player.inventory.getItemStack() != null) {
// middle click while holding an item; conflict with other feature like MatterManipulator pick
// block.
return false;
}
if (slot.getAEStack() != null && slot.getAEStack().isCraftable()) {
this.sendAction(MonitorableAction.AUTO_CRAFT, slot.getAEStack(), -1);
return true;
} else if (player.capabilities.isCreativeMode) {
this.sendAction(MonitorableAction.CREATIVE_DUPLICATE, slotStack, -1);
return true;
}
}
default -> {}
}
return false;
}
private boolean handleViewCellClick(final int xCoord, final int yCoord, final int btn) {
if (this.viewCell && monitorableContainer.canAccessViewCells && btn == 1) {
Slot slot = getSlot(xCoord, yCoord);
if (slot instanceof SlotRestrictedInput cvs) {
// if it has an item
if (!cvs.getHasStack()) return false;
// if its a view cell
if (!(cvs.getStack().getItem() instanceof ItemViewCell)) return false;
// update the view cell
this.monitorableContainer.toggleViewCellAction.send(cvs.getSlotIndex());
// eat the right-click input if a view cell was successfully toggled
return true;
}
}
return false;
}
@Override
protected boolean mouseWheelEvent(int x, int y, int wheel) {
VirtualMESlot slot = this.getVirtualMESlotUnderMouse();
if (!isShiftKeyDown() || !(slot instanceof VirtualMEMonitorableSlot)) return false;
final MonitorableAction direction = wheel > 0 ? MonitorableAction.ROLL_DOWN : MonitorableAction.ROLL_UP;
if (direction == MonitorableAction.ROLL_DOWN
&& Minecraft.getMinecraft().thePlayer.inventory.getItemStack() == null) {
return false;
}
if (direction == MonitorableAction.ROLL_UP && !(slot.getAEStack() instanceof IAEItemStack)) {
return false;
}
((AEBaseContainer) this.inventorySlots).setTargetStack(slot.getAEStack());
final int times = Math.abs(wheel);
for (int i = 0; i < times; i++) {
final PacketMonitorableAction p = new PacketMonitorableAction(direction, -1);
NetworkHandler.instance.sendToServer(p);
}
return true;
}
@Override
public void onGuiClosed() {
super.onGuiClosed();
memoryText = this.searchField.getText();
// Reacquire focus on reopening so native text input restarts after a screen change.
this.searchField.setFocused(false);
Keyboard.enableRepeatEvents(false);
}
@Override
public void drawBG(final int offsetX, final int offsetY, final int mouseX, final int mouseY) {
this.bindTexture(this.getBackground());
final int x_width = 195;
this.drawTexturedModalRect(offsetX, offsetY, 0, 0, x_width, 18);
if (this.viewCell || (this instanceof GuiSecurity)) {
this.drawTexturedModalRect(offsetX + x_width, offsetY, x_width, 0, 47, 128);
}
for (int x = 0; x < this.rows; x++) {
this.drawTexturedModalRect(offsetX, offsetY + 18 + x * 18, 0, 18, x_width, 18);
}
this.drawTexturedModalRect(
offsetX,
offsetY + 16 + this.rows * 18 + this.lowerTextureOffset,
0,
106 - 18 - 18,
x_width,
99 + this.reservedSpace - this.lowerTextureOffset);
this.searchField.drawTextBox();
this.updateViewCells();
}
protected String getBackground() {
return "guis/terminal.png";
}
protected void updateViewCells() {
if (this.viewCell) {
boolean update = false;
for (int i = 0; i < 5; i++) {
if (this.myCurrentViewCells[i] != this.monitorableContainer.getCellViewSlot(i).getStack()) {
update = true;
this.myCurrentViewCells[i] = this.monitorableContainer.getCellViewSlot(i).getStack();
}
}
if (update) {
this.repo.setViewCell(this.myCurrentViewCells);
}
}
}
protected int getMaxRows() {
return AEConfig.instance.getConfigManager().getSetting(Settings.TERMINAL_STYLE) == TerminalStyle.SMALL
? AEConfig.instance.MEMonitorableSmallSize
: Integer.MAX_VALUE;
}
protected void repositionSlot(final AppEngSlot s) {
s.yDisplayPosition = s.getY() + this.ySize - 78 - 5;
}
public boolean isSearchFieldManuallyFocused() {
return searchField.isFocused() && !isAutoFocused;
}
@Override
protected void keyTyped(final char character, final int key) {
// Opening NEI can leave text events queued for this now-hidden terminal.
if (this.mc.currentScreen != this) {
return;
}
if (!searchField.isFocused() && (!NEI.searchField.existsSearchField() || !NEI.searchField.focused())
&& CommonHelper.proxy.isActionKey(ActionKey.TOGGLE_FOCUS, key)) {
searchField.setFocused(true);
isAutoFocused = false;
return;
}
if (NEI.searchField.existsSearchField()) {
if (NEI.searchField.focused() || searchField.isFocused()) {
if (CommonHelper.proxy.isActionKey(ActionKey.TOGGLE_FOCUS, key)) {
final boolean focused = searchField.isFocused();
searchField.setFocused(!focused);
NEI.searchField.setFocus(focused);
isAutoFocused = false;
return;
}
if (NEI.searchField.focused()) {
return;
}
} else {
if (CommonHelper.proxy.isActionKey(ActionKey.SEARCH_CONNECTED_INVENTORIES, key)) {
final VirtualMESlot slot = this.getVirtualMESlotUnderMouse();
if (slot instanceof VirtualMEMonitorableSlot monitorableSlot) {
IAEStack<?> stack = monitorableSlot.getAEStack();
this.monitorableContainer.setTargetStack(stack);
if (stack != null) {
final PacketInventoryAction p = new PacketInventoryAction(
InventoryAction.FIND_ITEMS,
this.getInventorySlots().size(),
0);
NetworkHandler.instance.sendToServer(p);
this.closeGui();
return;
}
}
}
if (CommonHelper.proxy.isActionKey(ActionKey.LOCATE_ITEM_FLOW, key)) {
final VirtualMESlot slot = this.getVirtualMESlotUnderMouse();
if (slot instanceof VirtualMEMonitorableSlot monitorableSlot) {
IAEStack<?> stack = monitorableSlot.getAEStack();
this.monitorableContainer.setTargetStack(stack);
if (stack != null) {
final PacketInventoryAction p = new PacketInventoryAction(
InventoryAction.LOCATE_ITEM_FLOW,
this.getInventorySlots().size(),
0);
NetworkHandler.instance.sendToServer(p);
this.closeGui();
return;
}
}
}
}
}
if (searchField.isFocused() && (key == Keyboard.KEY_RETURN || key == Keyboard.KEY_NUMPADENTER)) {
searchField.setFocused(false);
isAutoFocused = false;
return;
}
boolean skipHotbarCheck = searchField.isFocused()
&& (AEConfig.instance.searchBoxFocusPriority == SearchBoxFocusPriority.ALWAYS
|| (AEConfig.instance.searchBoxFocusPriority == SearchBoxFocusPriority.NO_AUTOSEARCH
&& !isAutoFocused));
if (!skipHotbarCheck && checkHotbarKeys(key)) {
return;
}
final boolean mouseInGui = this
.isPointInRegion(0, 0, this.xSize, this.ySize, this.currentMouseX, this.currentMouseY);
if (this.canBeAutoFocused && !searchField.isFocused() && mouseInGui) {
searchField.setFocused(true);
isAutoFocused = true;
}
if (!searchField.textboxKeyTyped(character, key)) {
super.keyTyped(character, key);
}
}
@Override
public void updateScreen() {
this.repo.setPowered(this.monitorableContainer.isPowered());
super.updateScreen();
}
@Override
public Enum getSortBy() {
return this.configSrc.getSetting(Settings.SORT_BY);
}
@Override
public Enum getSortDir() {
return this.configSrc.getSetting(Settings.SORT_DIRECTION);
}
@Override
@Nullable
public Reference2BooleanMap<IAEStackType<?>> getTypeFilter() {
return this.typeFilters == null ? null : this.typeFilters.getFiltersMap();
}
@Override
public Enum getSortDisplay() {
return this.configSrc.getSetting(Settings.VIEW_MODE);
}
@Override
public void updateSetting(final IConfigManager manager, final Enum settingName, final Enum newValue) {
if (this.SortByBox != null) {
this.SortByBox.set(this.configSrc.getSetting(Settings.SORT_BY));
}
if (this.SortDirBox != null) {
this.SortDirBox.set(this.configSrc.getSetting(Settings.SORT_DIRECTION));
}
if (this.ViewBox != null) {
this.ViewBox.set(this.configSrc.getSetting(Settings.VIEW_MODE));
}
this.repo.updateView();
}
public void updateTypeFilters(Reference2BooleanMap<IAEStackType<?>> map) {
if (this.typeFilters == null) return;
for (Reference2BooleanMap.Entry<IAEStackType<?>> entry : map.reference2BooleanEntrySet()) {
this.typeFilters.setEnabled(entry.getKey(), entry.getBooleanValue());
}
// Update Buttons
for (final Map.Entry<TypeToggleButton, IAEStackType<?>> entry : this.typeToggleButtons.entrySet()) {
final boolean enabled = this.typeFilters.isEnabled(entry.getValue());
entry.getKey().setEnabled(enabled);
}
this.repo.updateView();
}
@SuppressWarnings("SameParameterValue")
protected boolean isPointInRegion(int rectX, int rectY, int rectWidth, int rectHeight, int pointX, int pointY) {
pointX -= this.guiLeft;
pointY -= this.guiTop;
return pointX >= rectX - 1 && pointX < rectX + rectWidth + 1
&& pointY >= rectY - 1
&& pointY < rectY + rectHeight + 1;
}
protected int getReservedSpace() {
return this.reservedSpace;
}
protected void setReservedSpace(final int reservedSpace) {
this.reservedSpace = reservedSpace;
}
public boolean isCustomSortOrder() {
return this.customSortOrder;
}
void setCustomSortOrder(final boolean customSortOrder) {
this.customSortOrder = customSortOrder;
}
public int getStandardSize() {
return this.standardSize;
}
void setStandardSize(final int standardSize) {
this.standardSize = standardSize;
}
@Override
public void drawScreen(final int mouseX, final int mouseY, final float btn) {
if (this.needsViewUpdate) {
this.needsViewUpdate = false;
this.repo.updateViewIfChanged();
}
if (this.repo.size() != this.scrollBarRepoSize) this.setScrollBar();
handleTooltip(mouseX, mouseY, searchField);
super.drawScreen(mouseX, mouseY, btn);
}
public boolean isOverTextField(final int mousex, final int mousey) {
return searchField.isMouseIn(mousex, mousey);
}
public void setTextFieldValue(final String displayName, final int mousex, final int mousey, final ItemStack stack) {
searchField.setText(NEI.searchField.getEscapedSearchText(displayName));
}
@Override
protected void handleMouseClick(Slot slot, int slotIdx, int clickedButton, int clickType) {
// Hack for view cells, because they are outside the container
if (slot != null && clickType == 4 && slot.xDisplayPosition > this.xSize) {
clickType = 0;
}
super.handleMouseClick(slot, slotIdx, clickedButton, clickType);
}
@Override
protected boolean handleClickOrDragSlot(@NotNull Slot slot, @org.jetbrains.annotations.Nullable ItemStack stack,
int mouseButton) {
if (isCtrlKeyDown() && mouseButton == 0
&& stack == null
&& slot instanceof AppEngSlot appEngSlot
&& appEngSlot.isPlayerSide()) {
ItemStack stackInSlot = appEngSlot.getStack();
if (stackInSlot != null) {
for (IAEStackType<?> type : AEStackTypeRegistry.getAllTypes()) {
if (type.isContainerItemForType(stackInSlot)) {
this.sendAction(MonitorableAction.CONTAINER_QUICK_TRANSFER, null, slot.slotNumber);
return true;
}
}
}
}
return super.handleClickOrDragSlot(slot, stack, mouseButton);
}
@Override
public void handleKeyboardInput() {
super.handleKeyboardInput();
// Pause the terminal when holding shift
if (enableShiftPause) this.repo.setPaused(isShiftKeyDown());
}
public boolean hideItemPanelSlot(int tx, int ty, int tw, int th) {
if (this.viewCell) {
int rw = 33;
int rh = 14 + myCurrentViewCells.length * 18;
if (monitorableContainer.isAPatternTerminal()) rh += 21;
if (rw <= 0 || rh <= 0 || tw <= 0 || th <= 0) {
return false;
}
int rx = this.guiLeft + this.xSize;
int ry = this.guiTop + 0;
rw += rx;
rh += ry;
tw += tx;
th += ty;
// overflow || intersect
return (rw < rx || rw > tx) && (rh < ry || rh > ty) && (tw < tx || tw > rx) && (th < ty || th > ry);
}
return false;
}
@Override
public void setAEPins(IAEStack<?>[] pins) {
repo.setAEPins(pins);
}
@Override
public void setPinsRows(PinsRows craftingRows, PinsRows playerRows) {
if (this.pinsStateButton != null) {
if (craftingRows != craftingPinsRows || playerRows != playerPinsRows) {
craftingPinsRows = craftingRows;
playerPinsRows = playerRows;
reinitalize();
}
}
}
/**
* @return true if this click was handled as a pin-setting action, false if normal slot handling should continue
*/
private boolean handleSetPinAction(VirtualMEPinSlot pinSlot) {
ItemStack itemStack = this.getStackFromHand();
if (itemStack == null) return false;
final boolean isRealItem = this.mc.thePlayer.inventory.getItemStack() != null;
final boolean nonItemInteraction = isCtrlKeyDown()
|| (this.typeFilters != null && !this.typeFilters.isEnabled(ITEM_STACK_TYPE));
if (nonItemInteraction) {
for (IAEStackType<?> type : AEStackTypeRegistry.getAllTypes()) {
if (type.isContainerItemForType(itemStack)) {
IAEStack<?> stackInContainer = type.getStackFromContainerItem(itemStack);
IAEStack<?> stackInSlot = pinSlot.getAEStack();
if (isRealItem && stackInSlot != null
&& stackInSlot.getStackType() == type
&& (stackInContainer == null || stackInSlot.equals(stackInContainer))) {
return false;
}
if (stackInContainer == null) return true;
this.sendAction(MonitorableAction.SET_PIN, stackInContainer, pinSlot.getSlotIndex());
return true;
}
}
return true;
} else {
IAEStack<?> stackInSlot = pinSlot.getAEStack();
if (isRealItem && stackInSlot instanceof IAEItemStack ais && ais.getItemStack().isItemEqual(itemStack)) {
return false;
}
this.sendAction(MonitorableAction.SET_PIN, AEItemStack.create(itemStack), pinSlot.getSlotIndex());
return true;
}
}
public void memoryTextUpdated() {
this.reinitalize();
}
}