From 78c7235bea96db0dd376d4806861dfb24a2afc64 Mon Sep 17 00:00:00 2001 From: Serenibyss <10861407+serenibyss@users.noreply.github.com> Date: Fri, 24 May 2024 06:15:15 -0500 Subject: [PATCH] Improve ME Interface Terminal UI (#368) Co-authored-by: NotMyWing --- .../java/appeng/api/config/ActionItems.java | 7 +- .../java/appeng/client/gui/AEBaseGui.java | 2 +- .../implementations/GuiInterfaceTerminal.java | 548 +++++++++--------- .../client/gui/widgets/GuiImgButton.java | 5 +- .../gui/widgets/MEGuiTooltipTextField.java | 236 ++++++++ .../core/localization/ButtonToolTips.java | 11 +- .../core/localization/PlayerMessages.java | 10 +- .../reporting/PartInterfaceTerminal.java | 8 - .../appliedenergistics2/lang/en_us.lang | 16 +- .../textures/guis/newinterfaceterminal.png | Bin 11806 -> 3098 bytes .../textures/guis/states.png | Bin 13804 -> 14483 bytes 11 files changed, 565 insertions(+), 278 deletions(-) create mode 100644 src/main/java/appeng/client/gui/widgets/MEGuiTooltipTextField.java diff --git a/src/api/java/appeng/api/config/ActionItems.java b/src/api/java/appeng/api/config/ActionItems.java index 0d8ea51c9..0b988205a 100644 --- a/src/api/java/appeng/api/config/ActionItems.java +++ b/src/api/java/appeng/api/config/ActionItems.java @@ -38,8 +38,11 @@ public enum ActionItems DIVIDE_BY_THREE, DECREASE_BY_ONE, MAX_COUNT, - FREE_MOLECULAR_SLOT_SHORTCUT, + MOLECULAR_ASSEMBLERS_ON, + MOLECULAR_ASSEMBLERS_OFF, TOGGLE_SHOW_FULL_INTERFACES_ON, TOGGLE_SHOW_FULL_INTERFACES_OFF, + TOGGLE_SHOW_ONLY_INVALID_PATTERNS_ON, + TOGGLE_SHOW_ONLY_INVALID_PATTERNS_OFF, HIGHLIGHT_INTERFACE -} \ No newline at end of file +} diff --git a/src/main/java/appeng/client/gui/AEBaseGui.java b/src/main/java/appeng/client/gui/AEBaseGui.java index ea695d166..7b76eed1f 100644 --- a/src/main/java/appeng/client/gui/AEBaseGui.java +++ b/src/main/java/appeng/client/gui/AEBaseGui.java @@ -258,7 +258,7 @@ public abstract class AEBaseGui extends GuiContainer { } } - private void drawTooltip(ITooltip tooltip, int mouseX, int mouseY) { + protected void drawTooltip(ITooltip tooltip, int mouseX, int mouseY) { final int x = tooltip.xPos(); // ((GuiImgButton) c).x; int y = tooltip.yPos(); // ((GuiImgButton) c).y; diff --git a/src/main/java/appeng/client/gui/implementations/GuiInterfaceTerminal.java b/src/main/java/appeng/client/gui/implementations/GuiInterfaceTerminal.java index 4e8f75794..826370af0 100644 --- a/src/main/java/appeng/client/gui/implementations/GuiInterfaceTerminal.java +++ b/src/main/java/appeng/client/gui/implementations/GuiInterfaceTerminal.java @@ -27,14 +27,18 @@ import appeng.api.storage.channels.IItemStorageChannel; import appeng.client.gui.AEBaseGui; import appeng.client.gui.widgets.GuiImgButton; import appeng.client.gui.widgets.GuiScrollbar; -import appeng.client.gui.widgets.MEGuiTextField; +import appeng.client.gui.widgets.MEGuiTooltipTextField; import appeng.client.me.ClientDCInternalInv; import appeng.client.me.SlotDisconnected; import appeng.container.implementations.ContainerInterfaceTerminal; import appeng.container.slot.AppEngSlot; import appeng.core.AEConfig; +import appeng.core.AppEng; +import appeng.core.localization.ButtonToolTips; import appeng.core.localization.GuiText; +import appeng.core.localization.PlayerMessages; import appeng.helpers.DualityInterface; +import appeng.helpers.PatternHelper; import appeng.parts.reporting.PartInterfaceTerminal; import appeng.util.BlockPosUtils; import appeng.util.Platform; @@ -42,13 +46,12 @@ import com.google.common.collect.HashMultimap; import net.minecraft.client.gui.GuiButton; import net.minecraft.client.renderer.GlStateManager; import net.minecraft.entity.player.InventoryPlayer; -import net.minecraft.inventory.Slot; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraft.nbt.NBTUtil; import net.minecraft.util.math.BlockPos; -import net.minecraft.util.text.TextComponentString; +import net.minecraft.world.World; import net.minecraftforge.common.DimensionManager; import net.minecraftforge.common.util.Constants; import net.minecraftforge.fml.common.Loader; @@ -71,15 +74,12 @@ import static appeng.helpers.ItemStackHelper.stackFromNBT; public class GuiInterfaceTerminal extends AEBaseGui { - private int rows = 6; + private static final int OFFSET_X = 21; + private static final int MAGIC_HEIGHT_NUMBER = 52 + 99; + private static final String MOLECULAR_ASSEMBLER = "molecular assembler"; - // TODO: copied from GuiMEMonitorable. It looks not changed, maybe unneeded? - private final int offsetX = 21; - private int maxRows = Integer.MAX_VALUE; - - protected int jeiOffset = Loader.isModLoaded("jei") ? 24 : 0; - - private int reservedSpace = 0; + private final boolean jeiEnabled; + private final int jeiButtonPadding; private final HashMap byId = new HashMap<>(); private final HashMultimap byName = HashMultimap.create(); @@ -89,99 +89,116 @@ public class GuiInterfaceTerminal extends AEBaseGui { private final ArrayList names = new ArrayList<>(); private final ArrayList lines = new ArrayList<>(); private final Set matchedStacks = new HashSet<>(); - private final Set matchedInterfaces = new HashSet<>(); private final Map> cachedSearches = new WeakHashMap<>(); + private final Map dimHashMap = new HashMap<>(); + + private final MEGuiTooltipTextField searchFieldOutputs; + private final MEGuiTooltipTextField searchFieldInputs; + private final MEGuiTooltipTextField searchFieldNames; + + private final GuiImgButton guiButtonHideFull; + private final GuiImgButton guiButtonAssemblersOnly; + private final GuiImgButton guiButtonBrokenRecipes; + private final GuiImgButton terminalStyleBox; private boolean refreshList = false; - private MEGuiTextField searchFieldOutputs; - private MEGuiTextField searchFieldInputs; - private final PartInterfaceTerminal partInterfaceTerminal; - private GuiButton guiButtonHide; - private GuiButton guiButtonNextAssembler; - private GuiImgButton terminalStyleBox; - private final HashMap dimHashMap = new HashMap<>(); - private int drawnRows = 0; + /* These are worded so that the intended default is false */ + private boolean onlyShowWithSpace = false; + private boolean onlyMolecularAssemblers = false; + private boolean onlyBrokenRecipes = false; + private int rows = 6; public GuiInterfaceTerminal(final InventoryPlayer inventoryPlayer, final PartInterfaceTerminal te) { super(new ContainerInterfaceTerminal(inventoryPlayer, te)); - this.partInterfaceTerminal = te; final GuiScrollbar scrollbar = new GuiScrollbar(); this.setScrollBar(scrollbar); this.xSize = 208; this.ySize = 255; - this.setReservedSpace(82); + this.jeiEnabled = Loader.isModLoaded("jei"); + this.jeiButtonPadding = jeiEnabled ? 22 : 0; + + searchFieldInputs = createTextField(86, 12, ButtonToolTips.SearchFieldInputs.getLocal()); + searchFieldOutputs = createTextField(86, 12, ButtonToolTips.SearchFieldOutputs.getLocal()); + searchFieldNames = createTextField(71, 12, ButtonToolTips.SearchFieldNames.getLocal()); + searchFieldNames.setFocused(true); + + guiButtonAssemblersOnly = new GuiImgButton(0, 0, Settings.ACTIONS, null); + guiButtonHideFull = new GuiImgButton(0, 0, Settings.ACTIONS, null); + guiButtonBrokenRecipes = new GuiImgButton(0, 0, Settings.ACTIONS, null); + terminalStyleBox = new GuiImgButton(0, 0, Settings.TERMINAL_STYLE, null); + } + + private MEGuiTooltipTextField createTextField(final int width, final int height, final String tooltip) { + MEGuiTooltipTextField textField = new MEGuiTooltipTextField(width, height, tooltip) { + @Override + public void onTextChange(String oldText) { + refreshList(); + } + }; + textField.setEnableBackgroundDrawing(false); + textField.setMaxStringLength(25); + textField.setTextColor(0xFFFFFF); + textField.setCursorPositionZero(); + return textField; + } + + private void setScrollBar() { + this.getScrollBar().setTop(52).setLeft(189).setHeight(this.rows * 18 - 2); + this.getScrollBar().setRange(0, this.lines.size() - 1, 1); + } + + private int calculateRowsCount() { + final int maxRows = getMaxRows(); + final int jeiPadding = jeiEnabled ? 22 + 18 : 0; + final int extraSpace = this.height - MAGIC_HEIGHT_NUMBER - jeiPadding; + + return Math.max(6, Math.min(maxRows, extraSpace / 18)); } @Override public void initGui() { - this.maxRows = AEConfig.instance() - .getConfigManager() - .getSetting( - Settings.TERMINAL_STYLE) != TerminalStyle.TALL ? 6 : Integer.MAX_VALUE; - - final int magicNumber = 82 + 14; - final int extraSpace = this.height - magicNumber - this.reservedSpace; - - this.rows = (int) Math.floor(extraSpace / 18); - if (this.rows > this.maxRows) { - this.rows = this.maxRows; - } - - if (this.rows < 6) { - this.rows = 6; - } - - this.ySize = magicNumber + this.rows * 18 + this.reservedSpace; - // this.guiTop = top; - final int unusedSpace = this.height - this.ySize; - this.guiTop = (int) Math.floor(unusedSpace / (unusedSpace < 0 ? 3.8f : 2.0f)); + this.rows = calculateRowsCount(); super.initGui(); - this.getScrollBar().setLeft(189); - this.getScrollBar().setHeight(106); - this.getScrollBar().setTop(51); + this.ySize = MAGIC_HEIGHT_NUMBER + this.rows * 18; + final int unusedSpace = this.height - this.ySize; + this.guiTop = (int) Math.floor(unusedSpace / (unusedSpace < 0 ? 3.8f : 2.0f)); - this.searchFieldInputs = new MEGuiTextField(this.fontRenderer, this.guiLeft + Math.max(32, this.offsetX), this.guiTop + 25, 65, 12); - this.searchFieldInputs.setEnableBackgroundDrawing(false); - this.searchFieldInputs.setMaxStringLength(25); - this.searchFieldInputs.setTextColor(0xFFFFFF); - this.searchFieldInputs.setVisible(true); - this.searchFieldInputs.setFocused(false); + searchFieldInputs.x = guiLeft + 32; + searchFieldInputs.y = guiTop + 25; + searchFieldOutputs.x = guiLeft + 32; + searchFieldOutputs.y = guiTop + 38; + searchFieldNames.x = guiLeft + 32 + 99; + searchFieldNames.y = guiTop + 38; - this.searchFieldOutputs = new MEGuiTextField(this.fontRenderer, this.guiLeft + Math.max(32, this.offsetX), this.guiTop + 38, 65, 12); - this.searchFieldOutputs.setEnableBackgroundDrawing(false); - this.searchFieldOutputs.setMaxStringLength(25); - this.searchFieldOutputs.setTextColor(0xFFFFFF); - this.searchFieldOutputs.setVisible(true); - this.searchFieldOutputs.setFocused(true); - - this.searchFieldInputs.setText(partInterfaceTerminal.in); - this.searchFieldOutputs.setText(partInterfaceTerminal.out); - - this.fontRenderer.drawString(this.getGuiDisplayName(GuiText.InterfaceTerminal.getLocal()), 8, 6, 4210752); - this.fontRenderer.drawString(GuiText.inventory.getLocal(), this.offsetX + 2, this.ySize - 96 + 3, 4210752); + terminalStyleBox.x = guiLeft - 18; + terminalStyleBox.y = guiTop + 8 + jeiButtonPadding; + guiButtonBrokenRecipes.x = guiLeft - 18; + guiButtonBrokenRecipes.y = terminalStyleBox.y + 20; + guiButtonHideFull.x = guiLeft - 18; + guiButtonHideFull.y = guiButtonBrokenRecipes.y + 20; + guiButtonAssemblersOnly.x = guiLeft - 18; + guiButtonAssemblersOnly.y = guiButtonHideFull.y + 20; + this.setScrollBar(); + this.repositionSlots(); } - protected void repositionSlot(final AppEngSlot s) { - this.ySize = 82 + 14 + this.drawnRows * 18 + this.reservedSpace; - - s.yPos = s.getY() + this.ySize - 112; - s.xPos = s.getX() + 14; - } - - @Override - public void onGuiClosed() { - partInterfaceTerminal.saveSearchStrings(this.searchFieldInputs.getText().toLowerCase(), this.searchFieldOutputs.getText().toLowerCase()); - super.onGuiClosed(); + protected void repositionSlots() { + for (final Object obj : this.inventorySlots.inventorySlots) { + if (obj instanceof AppEngSlot slot) { + slot.yPos = this.ySize + slot.getY() - 78 - 7; + slot.xPos = slot.getX() + 14; + } + } } @Override public List getJEIExclusionArea() { - Rectangle tallButton = new Rectangle(this.guiLeft - 18, this.guiTop + 24 + jeiOffset, 18, 18); + Rectangle tallButton = new Rectangle(this.guiLeft - 18, this.guiTop + 24 + 24, 18, 18); List area = new ArrayList<>(); area.add(tallButton); return area; @@ -189,93 +206,98 @@ public class GuiInterfaceTerminal extends AEBaseGui { @Override public void drawFG(final int offsetX, final int offsetY, final int mouseX, final int mouseY) { - this.buttonList.clear(); + this.fontRenderer.drawString(this.getGuiDisplayName(GuiText.InterfaceTerminal.getLocal()), OFFSET_X + 2, 6, 4210752); + this.fontRenderer.drawString(GuiText.inventory.getLocal(), OFFSET_X + 2, this.ySize - 96, 4210752); final int currentScroll = this.getScrollBar().getCurrentScroll(); - this.guiButtonNextAssembler = new GuiImgButton(guiLeft + 123, guiTop + 25, Settings.ACTIONS, ActionItems.FREE_MOLECULAR_SLOT_SHORTCUT); - this.buttonList.add(guiButtonNextAssembler); - - guiButtonHide = new GuiImgButton(guiLeft + 141, guiTop + 25, Settings.ACTIONS, this.partInterfaceTerminal.onlyInterfacesWithFreeSlots ? ActionItems.TOGGLE_SHOW_FULL_INTERFACES_OFF : ActionItems.TOGGLE_SHOW_FULL_INTERFACES_ON); - this.buttonList.add(guiButtonHide); - - this.buttonList.add(this.terminalStyleBox = new GuiImgButton(this.guiLeft - 18, guiTop + 24 + jeiOffset, Settings.TERMINAL_STYLE, AEConfig.instance() - .getConfigManager() - .getSetting(Settings.TERMINAL_STYLE))); - - this.inventorySlots.inventorySlots.removeIf(slot -> slot instanceof SlotDisconnected); - - for (final Slot s : this.inventorySlots.inventorySlots) { - if (s instanceof AppEngSlot) { - if (s.xPos < 197) { - this.repositionSlot((AppEngSlot) s); - } - } - } - - int offset = 52; + int offset = 51; int linesDraw = 0; for (int x = 0; x < rows && linesDraw < rows && currentScroll + x < this.lines.size(); x++) { final Object lineObj = this.lines.get(currentScroll + x); - if (lineObj instanceof ClientDCInternalInv) { - final ClientDCInternalInv inv = (ClientDCInternalInv) lineObj; - - GuiButton guiButton = new GuiImgButton(guiLeft + 4, guiTop + offset, Settings.ACTIONS, ActionItems.HIGHLIGHT_INTERFACE); - guiButtonHashMap.put(guiButton, inv); - this.buttonList.add(guiButton); - int extraLines = numUpgradesMap.get(inv); + if (lineObj instanceof ClientDCInternalInv inv) { + final int extraLines = numUpgradesMap.get(inv); for (int row = 0; row < 1 + extraLines && linesDraw < rows; ++row) { for (int z = 0; z < 9; z++) { - this.inventorySlots.inventorySlots.add(new SlotDisconnected(inv, z + (row * 9), (z * 18 + 22), offset)); if (this.matchedStacks.contains(inv.getInventory().getStackInSlot(z + (row * 9)))) { - drawRect(z * 18 + 22, offset, z * 18 + 22 + 16, offset + 16, 0x8A00FF00); - } else if (!matchedInterfaces.contains(inv)) { - drawRect(z * 18 + 22, offset, z * 18 + 22 + 16, offset + 16, 0x6A000000); + drawRect(z * 18 + 22, 1 + offset, z * 18 + 22 + 16, 1 + offset + 16, 0x2A00FF00); } } linesDraw++; offset += 18; } - } else if (lineObj instanceof String) { - String name = (String) lineObj; + } else if (lineObj instanceof String name) { final int rows = this.byName.get(name).size(); if (rows > 1) { name = name + " (" + rows + ')'; } - while (name.length() > 2 && this.fontRenderer.getStringWidth(name) > 155) { + while (name.length() > 2 && this.fontRenderer.getStringWidth(name) > 158) { name = name.substring(0, name.length() - 1); } - this.fontRenderer.drawString(name, this.offsetX + 2, 5 + offset, 4210752); + this.fontRenderer.drawString(name, OFFSET_X + 3, 6 + offset, 4210752); + linesDraw++; + offset += 18; + } + } + } + + @Override + public void drawScreen(int mouseX, int mouseY, float partialTicks) { + buttonList.clear(); + guiButtonHashMap.clear(); + inventorySlots.inventorySlots.removeIf(slot -> slot instanceof SlotDisconnected); + + guiButtonAssemblersOnly.set(onlyMolecularAssemblers ? ActionItems.MOLECULAR_ASSEMBLERS_ON : ActionItems.MOLECULAR_ASSEMBLERS_OFF); + guiButtonHideFull.set(onlyShowWithSpace ? ActionItems.TOGGLE_SHOW_FULL_INTERFACES_OFF : ActionItems.TOGGLE_SHOW_FULL_INTERFACES_ON); + guiButtonBrokenRecipes.set(onlyBrokenRecipes ? ActionItems.TOGGLE_SHOW_ONLY_INVALID_PATTERNS_ON : ActionItems.TOGGLE_SHOW_ONLY_INVALID_PATTERNS_OFF); + terminalStyleBox.set(AEConfig.instance().getConfigManager().getSetting(Settings.TERMINAL_STYLE)); + + buttonList.add(guiButtonAssemblersOnly); + buttonList.add(guiButtonHideFull); + buttonList.add(guiButtonBrokenRecipes); + buttonList.add(terminalStyleBox); + + int offset = 51; + final int currentScroll = this.getScrollBar().getCurrentScroll(); + int linesDraw = 0; + + for (int x = 0; x < rows && linesDraw < rows && currentScroll + x < this.lines.size(); x++) { + final Object lineObj = this.lines.get(currentScroll + x); + if (lineObj instanceof ClientDCInternalInv inv) { + + GuiButton guiButton = new GuiImgButton(guiLeft + 4, guiTop + offset + 1, Settings.ACTIONS, ActionItems.HIGHLIGHT_INTERFACE); + guiButtonHashMap.put(guiButton, inv); + this.buttonList.add(guiButton); + + final int extraLines = numUpgradesMap.get(inv); + for (int row = 0; row < 1 + extraLines && linesDraw < rows; ++row) { + for (int z = 0; z < 9; z++) { + this.inventorySlots.inventorySlots.add(new SlotDisconnected(inv, z + (row * 9), z * 18 + 22, 1+ offset)); + } + linesDraw++; + offset += 18; + } + + } else if (lineObj instanceof String) { linesDraw++; offset += 18; } } - if (searchFieldInputs.isMouseIn(mouseX, mouseY)) { - drawTooltip(Mouse.getEventX() * this.width / this.mc.displayWidth - offsetX, mouseY - guiTop, "Inputs OR names"); - } else if (searchFieldOutputs.isMouseIn(mouseX, mouseY)) { - drawTooltip(Mouse.getEventX() * this.width / this.mc.displayWidth - offsetX, mouseY - guiTop, "Outputs OR names"); - } + super.drawScreen(mouseX, mouseY, partialTicks); + drawTooltip(searchFieldInputs, mouseX, mouseY); + drawTooltip(searchFieldOutputs, mouseX, mouseY); + drawTooltip(searchFieldNames, mouseX, mouseY); } @Override protected void mouseClicked(final int xCoord, final int yCoord, final int btn) throws IOException { this.searchFieldInputs.mouseClicked(xCoord, yCoord, btn); - - if (btn == 1 && this.searchFieldInputs.isMouseIn(xCoord, yCoord)) { - this.searchFieldInputs.setText(""); - this.refreshList(); - } - this.searchFieldOutputs.mouseClicked(xCoord, yCoord, btn); - - if (btn == 1 && this.searchFieldOutputs.isMouseIn(xCoord, yCoord)) { - this.searchFieldOutputs.setText(""); - this.refreshList(); - } + this.searchFieldNames.mouseClicked(xCoord, yCoord, btn); super.mouseClicked(xCoord, yCoord, btn); } @@ -289,54 +311,37 @@ public class GuiInterfaceTerminal extends AEBaseGui { int interfaceDim = dimHashMap.get(guiButtonHashMap.get(this.selectedButton)); if (playerDim != interfaceDim) { try { - mc.player.sendStatusMessage(new TextComponentString("Interface located at dimension: " + interfaceDim + " [" + DimensionManager.getWorld(interfaceDim).provider.getDimensionType().getName() + "] and cant be highlighted"), false); + mc.player.sendStatusMessage(PlayerMessages.InterfaceInOtherDimParam.get(interfaceDim, DimensionManager.getWorld(interfaceDim).provider.getDimensionType().getName()), false); } catch (Exception e) { - mc.player.sendStatusMessage(new TextComponentString("Interface is located in another dimension and cannot be highlighted"), false); + mc.player.sendStatusMessage(PlayerMessages.InterfaceInOtherDim.get(), false); } } else { hilightBlock(blockPos, System.currentTimeMillis() + 500 * BlockPosUtils.getDistance(blockPos, blockPos2), playerDim); - mc.player.sendStatusMessage(new TextComponentString("The interface is now highlighted at " + "X: " + blockPos.getX() + " Y: " + blockPos.getY() + " Z: " + blockPos.getZ()), false); + mc.player.sendStatusMessage(PlayerMessages.InterfaceHighlighted.get(blockPos.getX(), blockPos.getY(), blockPos.getZ()), false); } mc.player.closeScreen(); - } - - if (btn instanceof GuiImgButton) { - final boolean backwards = Mouse.isButtonDown(1); - - final GuiImgButton iBtn = (GuiImgButton) btn; + } else if (btn == guiButtonHideFull) { + onlyShowWithSpace = !onlyShowWithSpace; + this.refreshList(); + } else if (btn == guiButtonAssemblersOnly) { + onlyMolecularAssemblers = !onlyMolecularAssemblers; + this.refreshList(); + } else if (btn == guiButtonBrokenRecipes) { + onlyBrokenRecipes = !onlyBrokenRecipes; + this.refreshList(); + } else if (btn instanceof GuiImgButton iBtn) { if (iBtn.getSetting() != Settings.ACTIONS) { - final Enum cv = iBtn.getCurrentValue(); - final Enum next = Platform.rotateEnum(cv, backwards, iBtn.getSetting().getPossibleValues()); + final Enum cv = iBtn.getCurrentValue(); + final boolean backwards = Mouse.isButtonDown(1); + final Enum next = Platform.rotateEnum(cv, backwards, iBtn.getSetting().getPossibleValues()); if (btn == this.terminalStyleBox) { AEConfig.instance().getConfigManager().putSetting(iBtn.getSetting(), next); - } - - iBtn.set(next); - - if (next.getClass() == TerminalStyle.class) { this.reinitalize(); } + iBtn.set(next); } } - if (btn == guiButtonHide) { - partInterfaceTerminal.onlyInterfacesWithFreeSlots = !partInterfaceTerminal.onlyInterfacesWithFreeSlots; - this.refreshList(); - } - - if (btn == guiButtonNextAssembler) { - // Set Search to "Molecular Assembler" and set "Only Free Interface" - boolean currentOnlyInterfacesWithFreeSlots = this.partInterfaceTerminal.onlyInterfacesWithFreeSlots; - String currentSearchText = this.searchFieldOutputs.getText(); - - this.partInterfaceTerminal.onlyInterfacesWithFreeSlots = true; - this.searchFieldOutputs.setText("Molecular Assembler"); - - this.refreshList(); - - this.partInterfaceTerminal.onlyInterfacesWithFreeSlots = currentOnlyInterfacesWithFreeSlots; - this.searchFieldOutputs.setText(currentSearchText); - } } private void reinitalize() { @@ -347,74 +352,66 @@ public class GuiInterfaceTerminal extends AEBaseGui { @Override public void drawBG(final int offsetX, final int offsetY, final int mouseX, final int mouseY) { this.bindTexture("guis/newinterfaceterminal.png"); - //split the texture for the top - this.drawTexturedModalRect(offsetX, offsetY, 0, 0, this.xSize, 166); + + // draw the top portion of the background, above the interface list + this.drawTexturedModalRect(offsetX, offsetY, 0, 0, this.xSize, 53); + + for (int x = 0; x < this.rows; x++) { + // draw the background of the rows in the interface list + this.drawTexturedModalRect(offsetX, offsetY + 53 + x * 18, 0, 52, this.xSize, 18); + } int offset = 51; final int ex = this.getScrollBar().getCurrentScroll(); int linesDraw = 0; - for (int x = 0; x < rows && linesDraw < rows && ex + x < this.lines.size(); x++) { + for (int x = 0; x < this.rows && linesDraw < rows && ex + x < this.lines.size(); x++) { final Object lineObj = this.lines.get(ex + x); if (lineObj instanceof ClientDCInternalInv) { GlStateManager.color(1, 1, 1, 1); + final int width = 9 * 18; + final int extraLines = numUpgradesMap.get(lineObj); - int extraLines = numUpgradesMap.get(lineObj); - + // draw the slot backgrounds for (int row = 0; row < 1 + extraLines && linesDraw < rows; ++row) { - if (linesDraw == 6) { - this.drawTexturedModalRect(offsetX, offsetY + offset + 7, 0, 166, 190, 7); - this.drawTexturedModalRect(offsetX, offsetY + offset + 14, 0, 166, 190, 4); - } else if (linesDraw >= 7) { - this.drawTexturedModalRect(offsetX, offsetY + offset, 0, 173, 190, 18); - } this.drawTexturedModalRect(offsetX + 20, offsetY + offset, 20, 173, width, 18); offset += 18; linesDraw++; } } else { - if (linesDraw == 6) { - this.drawTexturedModalRect(offsetX, offsetY + offset + 7, 0, 166, 190, 7); - this.drawTexturedModalRect(offsetX, offsetY + offset + 14, 0, 166, 190, 4); - this.drawTexturedModalRect(offsetX, offsetY + offset, 0, 53, 183, 18); - } else if (linesDraw >= 7) { - this.drawTexturedModalRect(offsetX, offsetY + offset, 0, 53, 183, 18); - this.drawTexturedModalRect(offsetX + 183, offsetY + offset, 183, 166, 7, 18); - } offset += 18; linesDraw++; } } - offset = 51 + Math.max(6 * 18, linesDraw * 18); - if (linesDraw > 6) { - this.drawTexturedModalRect(offsetX, offsetY + offset, 0, 166, 190, 7); - } - this.drawTexturedModalRect(offsetX, offsetY + offset + 7, 0, 166, this.xSize, 90); - this.drawnRows = Math.max(6, linesDraw); + // draw the background below the interface list + this.drawTexturedModalRect(offsetX, offsetY + 50 + this.rows * 18, 0, 158, this.xSize, 99); - if (this.searchFieldInputs != null) { - this.searchFieldInputs.drawTextBox(); - } - - if (this.searchFieldOutputs != null) { - this.searchFieldOutputs.drawTextBox(); - } + // draw the text boxes + this.searchFieldInputs.drawTextBox(); + this.searchFieldOutputs.drawTextBox(); + this.searchFieldNames.drawTextBox(); } @Override protected void keyTyped(final char character, final int key) throws IOException { if (!this.checkHotbarKeys(key)) { - if (character == ' ' && this.searchFieldInputs.getText().isEmpty() && this.searchFieldInputs.isFocused()) { - return; + if (character == ' ') { + if ((this.searchFieldInputs.getText().isEmpty() && this.searchFieldInputs.isFocused()) + || (this.searchFieldOutputs.getText().isEmpty() && this.searchFieldOutputs.isFocused()) + || (this.searchFieldNames.getText().isEmpty() && this.searchFieldNames.isFocused())) { + return; + } + } else if (character == '\t') { + if (handleTab()) { + return; + } } - if (character == ' ' && this.searchFieldOutputs.getText().isEmpty() && this.searchFieldOutputs.isFocused()) { - return; - } - - if (this.searchFieldInputs.textboxKeyTyped(character, key) || this.searchFieldOutputs.textboxKeyTyped(character, key)) { + if (this.searchFieldInputs.textboxKeyTyped(character, key) + || this.searchFieldOutputs.textboxKeyTyped(character, key) + || this.searchFieldNames.textboxKeyTyped(character, key)) { this.refreshList(); } else { super.keyTyped(character, key); @@ -422,6 +419,27 @@ public class GuiInterfaceTerminal extends AEBaseGui { } } + /** Cycle to the next search bar if tab is pressed, going in reverse if shift is held. */ + private boolean handleTab() { + if (searchFieldInputs.isFocused()) { + searchFieldInputs.setFocused(false); + if (isShiftKeyDown()) searchFieldNames.setFocused(true); + else searchFieldOutputs.setFocused(true); + return true; + } else if (searchFieldOutputs.isFocused()) { + searchFieldOutputs.setFocused(false); + if (isShiftKeyDown()) searchFieldInputs.setFocused(true); + else searchFieldNames.setFocused(true); + return true; + } else if (searchFieldNames.isFocused()) { + searchFieldNames.setFocused(false); + if (isShiftKeyDown()) searchFieldOutputs.setFocused(true); + else searchFieldInputs.setFocused(true); + return true; + } + return false; + } + public void postUpdate(final NBTTagCompound in) { if (in.getBoolean("clear")) { this.byId.clear(); @@ -467,12 +485,13 @@ public class GuiInterfaceTerminal extends AEBaseGui { this.byName.clear(); this.buttonList.clear(); this.matchedStacks.clear(); - this.matchedInterfaces.clear(); final String searchFieldInputs = this.searchFieldInputs.getText().toLowerCase(); final String searchFieldOutputs = this.searchFieldOutputs.getText().toLowerCase(); + final String searchFieldNames = this.searchFieldNames.getText().toLowerCase(); - final Set cachedSearch = this.getCacheForSearchTerm("IN:" + searchFieldInputs + " OUT:" + searchFieldOutputs + partInterfaceTerminal.onlyInterfacesWithFreeSlots); + final Set cachedSearch = this.getCacheForSearchTerm("IN:" + searchFieldInputs + " OUT:" + searchFieldOutputs + + "NAME:" + searchFieldNames + onlyShowWithSpace + onlyMolecularAssemblers + onlyBrokenRecipes); final boolean rebuild = cachedSearch.isEmpty(); for (final ClientDCInternalInv entry : this.byId.values()) { @@ -483,78 +502,100 @@ public class GuiInterfaceTerminal extends AEBaseGui { // Shortcut to skip any filter if search term is ""/empty - boolean found = (searchFieldInputs.isEmpty() && searchFieldOutputs.isEmpty() && !partInterfaceTerminal.onlyInterfacesWithFreeSlots); + boolean found = searchFieldInputs.isEmpty() && searchFieldOutputs.isEmpty(); boolean interfaceHasFreeSlots = false; + boolean interfaceHasBrokenRecipes = false; // Search if the current inventory holds a pattern containing the search term. - if (!found) { + if (!found || onlyShowWithSpace || onlyBrokenRecipes) { int slot = 0; for (final ItemStack itemStack : entry.getInventory()) { if (slot > 8 + numUpgradesMap.get(entry) * 9) { break; } - if (!searchFieldInputs.isEmpty() && !searchFieldOutputs.isEmpty()) { - if (this.itemStackMatchesSearchTerm(itemStack, searchFieldInputs, 0) || this.itemStackMatchesSearchTerm(itemStack, searchFieldOutputs, 1)) { - found = true; - matchedStacks.add(itemStack); - } - } else if (!searchFieldInputs.isEmpty()) { - if (this.itemStackMatchesSearchTerm(itemStack, searchFieldInputs, 0)) { - found = true; - matchedStacks.add(itemStack); - } - } else if (!searchFieldOutputs.isEmpty()) { - if (this.itemStackMatchesSearchTerm(itemStack, searchFieldOutputs, 1)) { - found = true; - matchedStacks.add(itemStack); - } - } - // If only Interfaces with empty slots should be shown, check that here + if (itemStack.isEmpty()) { interfaceHasFreeSlots = true; } + + if (onlyBrokenRecipes && recipeIsBroken(itemStack)) { + interfaceHasBrokenRecipes = true; + } + + if ((!searchFieldInputs.isEmpty() && itemStackMatchesSearchTerm(itemStack, searchFieldInputs, 0)) + || (!searchFieldOutputs.isEmpty() && itemStackMatchesSearchTerm(itemStack, searchFieldOutputs, 1))) { + found = true; + matchedStacks.add(itemStack); + } + slot++; } } - // if found, filter skipped or machine name matching the search term, add it - if ((searchFieldInputs.isEmpty() && searchFieldOutputs.isEmpty()) || - !searchFieldInputs.isEmpty() && entry.getName().toLowerCase().contains(searchFieldInputs) || - (!searchFieldOutputs.isEmpty() && entry.getName().toLowerCase().contains(searchFieldOutputs))) { - this.matchedInterfaces.add(entry); - found = true; - } - if (found) { - if (!partInterfaceTerminal.onlyInterfacesWithFreeSlots) { - this.byName.put(entry.getName(), entry); - cachedSearch.add(entry); - } else if (interfaceHasFreeSlots) { - this.byName.put(entry.getName(), entry); - cachedSearch.add(entry); - } - } else { + + // Exit if not found + if (!found) { cachedSearch.remove(entry); + continue; } + // Exit if the interface does not match the name search + if (!entry.getName().toLowerCase().contains(searchFieldNames)) { + cachedSearch.remove(entry); + continue; + } + // Exit if molecular assembler filter is on and this is not a molecular assembler + if (onlyMolecularAssemblers && !entry.getName().toLowerCase().contains(MOLECULAR_ASSEMBLER)) { + cachedSearch.remove(entry); + continue; + } + // Exit if we are only showing interfaces with free slots and there are none free in this interface + if (onlyShowWithSpace && !interfaceHasFreeSlots) { + cachedSearch.remove(entry); + continue; + } + // Exit if we are only showing interfaces with broken patterns and there are no broken patterns in this interface + if (onlyBrokenRecipes && !interfaceHasBrokenRecipes) { + cachedSearch.remove(entry); + continue; + } + + // Successful search + this.byName.put(entry.getName(), entry); + cachedSearch.add(entry); } this.names.clear(); this.names.addAll(this.byName.keySet()); - Collections.sort(this.names); this.lines.clear(); - this.lines.ensureCapacity(this.getMaxRows()); + this.lines.ensureCapacity(this.names.size() + this.byId.size()); for (final String n : this.names) { this.lines.add(n); - - final ArrayList clientInventories = new ArrayList<>(); - clientInventories.addAll(this.byName.get(n)); - + final ArrayList clientInventories = new ArrayList<>(this.byName.get(n)); Collections.sort(clientInventories); this.lines.addAll(clientInventories); } - this.getScrollBar().setRange(0, this.lines.size() - 1, 1); + this.setScrollBar(); + } + + private boolean recipeIsBroken(final ItemStack stack) { + if (stack == null) return false; + if (stack.isEmpty()) return false; + + final NBTTagCompound encodedValue = stack.getTagCompound(); + if (encodedValue == null) return true; + + final World w = AppEng.proxy.getWorld(); + if (w == null) return false; + + try { + new PatternHelper(stack, w); + return false; + } catch (Throwable ignored) { + return true; + } } private boolean itemStackMatchesSearchTerm(final ItemStack itemStack, final String searchTerm, int pass) { @@ -624,13 +665,8 @@ public class GuiInterfaceTerminal extends AEBaseGui { return cache; } - /** - * The max amount of unique names and each inv row. Not affected by the filtering. - * - * @return max amount of unique names and each inv row - */ private int getMaxRows() { - return this.names.size() + this.byId.size(); + return AEConfig.instance().getConfigManager().getSetting(Settings.TERMINAL_STYLE) != TerminalStyle.TALL ? 6 : Integer.MAX_VALUE; } private ClientDCInternalInv getById(final long id, final long sortBy, final String string) { @@ -643,12 +679,4 @@ public class GuiInterfaceTerminal extends AEBaseGui { return o; } - - int getReservedSpace() { - return this.reservedSpace; - } - - void setReservedSpace(final int reservedSpace) { - this.reservedSpace = reservedSpace; - } } diff --git a/src/main/java/appeng/client/gui/widgets/GuiImgButton.java b/src/main/java/appeng/client/gui/widgets/GuiImgButton.java index a72ac2e9b..638cf5681 100644 --- a/src/main/java/appeng/client/gui/widgets/GuiImgButton.java +++ b/src/main/java/appeng/client/gui/widgets/GuiImgButton.java @@ -116,10 +116,13 @@ public class GuiImgButton extends GuiButton implements ITooltip { this.registerApp(11 + 4 * 16, Settings.ACTIONS, ActionItems.DECREASE_BY_ONE, ButtonToolTips.DecreaseByOne, ButtonToolTips.DecreaseByOneDesc); this.registerApp(12 + 4 * 16, Settings.ACTIONS, ActionItems.MAX_COUNT, ButtonToolTips.MaxCount, ButtonToolTips.MaxCountDesc); - this.registerApp(6 + 5 * 16, Settings.ACTIONS, ActionItems.FREE_MOLECULAR_SLOT_SHORTCUT, ButtonToolTips.FreeMolecularSlotShortcut, ButtonToolTips.FreeMolecularSlotShortcutDesc); + this.registerApp(6 + 5 * 16, Settings.ACTIONS, ActionItems.MOLECULAR_ASSEMBLERS_ON, ButtonToolTips.ToggleMolecularAssemblers, ButtonToolTips.ToggleMolecularAssemblersOnDesc); this.registerApp(7 + 5 * 16, Settings.ACTIONS, ActionItems.TOGGLE_SHOW_FULL_INTERFACES_ON, ButtonToolTips.ToggleShowFullInterfaces, ButtonToolTips.ToggleShowFullInterfacesOnDesc); this.registerApp(8 + 5 * 16, Settings.ACTIONS, ActionItems.TOGGLE_SHOW_FULL_INTERFACES_OFF, ButtonToolTips.ToggleShowFullInterfaces, ButtonToolTips.ToggleShowFullInterfacesOffDesc); + this.registerApp(9 + 5 * 16, Settings.ACTIONS, ActionItems.MOLECULAR_ASSEMBLERS_OFF, ButtonToolTips.ToggleMolecularAssemblers, ButtonToolTips.ToggleMolecularAssemblersOffDesc); this.registerApp(6 + 6 * 16, Settings.ACTIONS, ActionItems.HIGHLIGHT_INTERFACE, ButtonToolTips.HighlightInterface, ""); + this.registerApp(4 + 5 * 16, Settings.ACTIONS, ActionItems.TOGGLE_SHOW_ONLY_INVALID_PATTERNS_OFF, ButtonToolTips.ToggleShowOnlyInvalidInterface, ButtonToolTips.ToggleShowOnlyInvalidInterfaceOffDesc); + this.registerApp(5 + 5 * 16, Settings.ACTIONS, ActionItems.TOGGLE_SHOW_ONLY_INVALID_PATTERNS_ON, ButtonToolTips.ToggleShowOnlyInvalidInterface, ButtonToolTips.ToggleShowOnlyInvalidInterfaceOnDesc); this.registerApp(8, Settings.ACTIONS, ActionItems.ENCODE, ButtonToolTips.Encode, ButtonToolTips.EncodeDescription); this.registerApp(4 + 3 * 16, Settings.ACTIONS, ItemSubstitution.ENABLED, ButtonToolTips.Substitutions, ButtonToolTips.SubstitutionsDescEnabled); diff --git a/src/main/java/appeng/client/gui/widgets/MEGuiTooltipTextField.java b/src/main/java/appeng/client/gui/widgets/MEGuiTooltipTextField.java new file mode 100644 index 000000000..6a247928f --- /dev/null +++ b/src/main/java/appeng/client/gui/widgets/MEGuiTooltipTextField.java @@ -0,0 +1,236 @@ +package appeng.client.gui.widgets; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.client.gui.GuiTextField; +import org.lwjgl.input.Keyboard; + +/** + * Different implementation of a text field that wraps instead of extends + * MC's {@link GuiTextField}. This is necessary because of deobfuscated name + * collision between {@link ITooltip} and GuiTextField, which would cause + * crashes in an obfuscated environment. Additionally, since we are not extending that + * class, we can construct this object differently and allow its position to be + * mutable like most other widgets. + */ +public class MEGuiTooltipTextField implements ITooltip { + + protected GuiTextField field; + + private static final int PADDING = 2; + private static boolean previousKeyboardRepeatEnabled; + private static MEGuiTooltipTextField previousKeyboardRepeatEnabledField; + private String tooltip; + private int fontPad; + + public int x; + public int y; + public int w; + public int h; + + /** + * Uses the values to instantiate a padded version of a text field. Pays attention to the '_' caret. + * + * @param width absolute width + * @param height absolute height + * @param tooltip tooltip message + */ + public MEGuiTooltipTextField(final int width, final int height, final String tooltip) { + final FontRenderer fontRenderer = Minecraft.getMinecraft().fontRenderer; + field = new GuiTextField(0, fontRenderer, 0, 0, 0, 0); + + w = width; + h = height; + + setMessage(tooltip); + + this.fontPad = fontRenderer.getCharWidth('_'); + + setDimensionsAndColor(); + } + + public MEGuiTooltipTextField(final int width, final int height) { + this(width, height, ""); + } + + public MEGuiTooltipTextField() { + this(0, 0); + } + + protected void setDimensionsAndColor() { + field.x = this.x + PADDING; + field.y = this.y + PADDING; + field.width = this.w - PADDING * 2 - this.fontPad; + field.height = this.h - PADDING * 2; + } + + public void onTextChange(final String oldText) {} + + public void mouseClicked(final int xPos, final int yPos, final int button) { + + if (!this.isMouseIn(xPos, yPos)) { + setFocused(false); + return; + } + + field.setCanLoseFocus(false); + setFocused(true); + + if (button == 1) { + setText(""); + } else { + field.mouseClicked(xPos, yPos, button); + } + + field.setCanLoseFocus(true); + } + + /** + * Checks if the mouse is within the element + * + * @param xCoord current x coord of the mouse + * @param yCoord current y coord of the mouse + * @return true if mouse position is within the getText field area + */ + public boolean isMouseIn(final int xCoord, final int yCoord) { + final boolean withinXRange = this.x <= xCoord && xCoord < this.x + this.w; + final boolean withinYRange = this.y <= yCoord && yCoord < this.y + this.h; + + return withinXRange && withinYRange; + } + + public boolean textboxKeyTyped(final char keyChar, final int keyID) { + if (!isFocused()) { + return false; + } + + final String oldText = getText(); + boolean handled = field.textboxKeyTyped(keyChar, keyID); + + if (!handled && (keyID == Keyboard.KEY_RETURN || keyID == Keyboard.KEY_NUMPADENTER + || keyID == Keyboard.KEY_ESCAPE)) { + setFocused(false); + } + + if (handled) { + onTextChange(oldText); + } + + return handled; + } + + public void drawTextBox() { + if (field.getVisible()) { + setDimensionsAndColor(); + GuiTextField.drawRect( + this.x + 1, + this.y + 1, + this.x + this.w - 1, + this.y + this.h - 1, + isFocused() ? 0xFF606060 : 0xFFA8A8A8); + field.drawTextBox(); + } + } + + public void setText(String text, boolean ignoreTrigger) { + final String oldText = getText(); + + int currentCursorPos = field.getCursorPosition(); + field.setText(text); + field.setCursorPosition(currentCursorPos); + + if (!ignoreTrigger) { + onTextChange(oldText); + } + } + + public void setText(String text) { + setText(text, false); + } + + public void setCursorPositionEnd() { + field.setCursorPositionEnd(); + } + + public void setFocused(boolean focus) { + if (field.isFocused() == focus) { + return; + } + + field.setFocused(focus); + + if (focus) { + + if (previousKeyboardRepeatEnabledField == null) { + previousKeyboardRepeatEnabled = Keyboard.areRepeatEventsEnabled(); + } + + previousKeyboardRepeatEnabledField = this; + Keyboard.enableRepeatEvents(true); + } else { + + if (previousKeyboardRepeatEnabledField == this) { + previousKeyboardRepeatEnabledField = null; + Keyboard.enableRepeatEvents(previousKeyboardRepeatEnabled); + } + } + } + + public void setMaxStringLength(final int size) { + field.setMaxStringLength(size); + } + + public void setEnableBackgroundDrawing(final boolean b) { + field.setEnableBackgroundDrawing(b); + } + + public void setTextColor(final int color) { + field.setTextColor(color); + } + + public void setCursorPositionZero() { + field.setCursorPositionZero(); + } + + public boolean isFocused() { + return field.isFocused(); + } + + public String getText() { + return field.getText(); + } + + public void setMessage(String t) { + tooltip = t; + } + + @Override + public String getMessage() { + return tooltip; + } + + @Override + public boolean isVisible() { + return field.getVisible(); + } + + @Override + public int xPos() { + return x; + } + + @Override + public int yPos() { + return y; + } + + @Override + public int getWidth() { + return w; + } + + @Override + public int getHeight() { + return h; + } +} diff --git a/src/main/java/appeng/core/localization/ButtonToolTips.java b/src/main/java/appeng/core/localization/ButtonToolTips.java index fbbdd3223..42f65e46a 100644 --- a/src/main/java/appeng/core/localization/ButtonToolTips.java +++ b/src/main/java/appeng/core/localization/ButtonToolTips.java @@ -150,13 +150,20 @@ public enum ButtonToolTips { DecreaseByOneDesc, MaxCount, MaxCountDesc, - FreeMolecularSlotShortcut, - FreeMolecularSlotShortcutDesc, + ToggleMolecularAssemblers, + ToggleMolecularAssemblersOnDesc, + ToggleMolecularAssemblersOffDesc, ToggleShowFullInterfaces, ToggleShowFullInterfacesOnDesc, ToggleShowFullInterfacesOffDesc, + ToggleShowOnlyInvalidInterface, + ToggleShowOnlyInvalidInterfaceOnDesc, + ToggleShowOnlyInvalidInterfaceOffDesc, HighlightInterface, HighlightInterfaceDesc, + SearchFieldInputs, + SearchFieldOutputs, + SearchFieldNames, // Used in the tooltips of the items in the terminal, when moused over ItemsStored, diff --git a/src/main/java/appeng/core/localization/PlayerMessages.java b/src/main/java/appeng/core/localization/PlayerMessages.java index dff07f64c..5a3b3b338 100644 --- a/src/main/java/appeng/core/localization/PlayerMessages.java +++ b/src/main/java/appeng/core/localization/PlayerMessages.java @@ -41,7 +41,15 @@ public enum PlayerMessages { DeviceNotLinked, StationCanNotBeLocated, SettingCleared, - MissingPatternsToEncode; + MissingPatternsToEncode, + InterfaceInOtherDimParam, + InterfaceInOtherDim, + InterfaceHighlighted, + ; + + public ITextComponent get(Object... params) { + return new TextComponentTranslation(this.getName(), params); + } public ITextComponent get() { return new TextComponentTranslation(this.getName()); diff --git a/src/main/java/appeng/parts/reporting/PartInterfaceTerminal.java b/src/main/java/appeng/parts/reporting/PartInterfaceTerminal.java index dd72e071e..c507a1c0a 100644 --- a/src/main/java/appeng/parts/reporting/PartInterfaceTerminal.java +++ b/src/main/java/appeng/parts/reporting/PartInterfaceTerminal.java @@ -42,9 +42,6 @@ public class PartInterfaceTerminal extends AbstractPartDisplay { public static final IPartModel MODELS_OFF = new PartModel(MODEL_BASE, MODEL_OFF, MODEL_STATUS_OFF); public static final IPartModel MODELS_ON = new PartModel(MODEL_BASE, MODEL_ON, MODEL_STATUS_ON); public static final IPartModel MODELS_HAS_CHANNEL = new PartModel(MODEL_BASE, MODEL_ON, MODEL_STATUS_HAS_CHANNEL); - public String in = ""; - public String out = ""; - public boolean onlyInterfacesWithFreeSlots = false; public PartInterfaceTerminal(final ItemStack is) { super(is); @@ -64,9 +61,4 @@ public class PartInterfaceTerminal extends AbstractPartDisplay { public IPartModel getStaticModels() { return this.selectModel(MODELS_OFF, MODELS_ON, MODELS_HAS_CHANNEL); } - - public void saveSearchStrings(String in, String out) { - this.in = in; - this.out = out; - } } diff --git a/src/main/resources/assets/appliedenergistics2/lang/en_us.lang b/src/main/resources/assets/appliedenergistics2/lang/en_us.lang index 74ac79e39..71726f2ff 100644 --- a/src/main/resources/assets/appliedenergistics2/lang/en_us.lang +++ b/src/main/resources/assets/appliedenergistics2/lang/en_us.lang @@ -90,6 +90,9 @@ chat.appliedenergistics2.ResetSettings=New device configuration created and copi chat.appliedenergistics2.AmmoDepleted=Ammo Depleted. chat.appliedenergistics2.isNowLocked=Monitor is now Locked. chat.appliedenergistics2.isNowUnlocked=Monitor is now Unlocked. +chat.appliedenergistics2.InterfaceInOtherDimParam=Interface located at dimension: %d [ %s ] and cannot be highlighted +chat.appliedenergistics2.InterfaceInOtherDim=Interface is located in another dimension and cannot be highlighted +chat.appliedenergistics2.InterfaceHighlighted=The interface is now highlighted at X: %s Y: %s Z: %s // Creative Tabs itemGroup.appliedenergistics2=Applied Energistics 2 @@ -374,12 +377,19 @@ gui.tooltips.appliedenergistics2.DecreaseByOne=Decrease one gui.tooltips.appliedenergistics2.DecreaseByOneDesc=Will try to add decrease the items set in the crafting slots by one DOES NOT MAINTAIN INGREDIENT RATIO gui.tooltips.appliedenergistics2.MaxCount=Maximize slot count gui.tooltips.appliedenergistics2.MaxCountDesc=Will maximize the output maintaing the input ratio of the items set in the crafting slots -gui.tooltips.appliedenergistics2.FreeMolecularSlotShortcut=Show Crafting interfaces with free slots -gui.tooltips.appliedenergistics2.FreeMolecularSlotShortcutDesc=Shortcut that toggles interfaces with free slots and searches for molecular assemblers +gui.tooltips.appliedenergistics2.ToggleMolecularAssemblers=Toggles showing crafting interfaces only +gui.tooltips.appliedenergistics2.ToggleMolecularAssemblersOffDesc=Showing all interfaces +gui.tooltips.appliedenergistics2.ToggleMolecularAssemblersOnDesc=Showing only interfaces on molecular assemblers gui.tooltips.appliedenergistics2.ToggleShowFullInterfaces=Toggles showing interfaces with full slots gui.tooltips.appliedenergistics2.ToggleShowFullInterfacesOnDesc=Showing all interfaces gui.tooltips.appliedenergistics2.ToggleShowFullInterfacesOffDesc=Showing only interfaces with free slots -gui.tooltips.appliedenergistics2.HighlightInterface=Highlight interface +gui.tooltips.appliedenergistics2.ToggleShowOnlyInvalidInterface=Toggles showing interfaces with invalid patterns +gui.tooltips.appliedenergistics2.ToggleShowOnlyInvalidInterfaceOffDesc=Showing all interfaces +gui.tooltips.appliedenergistics2.ToggleShowOnlyInvalidInterfaceOnDesc=Showing only interfaces with invalid patterns +gui.tooltips.appliedenergistics2.HighlightInterface=Highlight Interface +gui.tooltips.appliedenergistics2.SearchFieldInputs=Recipe Inputs +gui.tooltips.appliedenergistics2.SearchFieldOutputs=Recipe Outputs +gui.tooltips.appliedenergistics2.SearchFieldNames=Interface Names gui.tooltips.appliedenergistics2.PartialTransfer=Move stored items into the crafting grid gui.tooltips.appliedenergistics2.MissingItem=§cMissing item gui.tooltips.appliedenergistics2.CraftableItem=§9Craftable item diff --git a/src/main/resources/assets/appliedenergistics2/textures/guis/newinterfaceterminal.png b/src/main/resources/assets/appliedenergistics2/textures/guis/newinterfaceterminal.png index 9d0f70c424c92fd16c4c774154fd03713e4d8ba3..6a194d58e02e3908fce5c6bd33e50c947caa0468 100644 GIT binary patch literal 3098 zcmeAS@N?(olHy`uVBq!ia0y~yU}OMc4mJh`hM1xiX$%b94?SHRLn`9l&S~tO7s}_l zU0Z7*%c_NfS_}EC60hff7moZLXS}CS=;-~OmnL2~qwl@(y*1B-59{myPPJ!zaCptB z`7IOr)$jS#x!7lYTHh=1$^7Tv@At2JEckPXVZrm~^Xu)7{65M5%ALDk{(*cG14A4S z6GMd|2g8G-3JeY6%~TO+s}SQX{`2eg`qc9Vr#HW@u>a}!H$zg~LF9w-k@We84#`a3 z&ce%-aiX>4NV?zw8wjtW>&MgnrS^ZnT>k&JLkdM=2I~ip2S4j-#2C*QS9)JiXlPsB zaenc6oBRFel^QD5@;RkgE_@I%w69_LkUL|KYKDNr;b514f)9Qhz*I*voR7ChRz89I zWd}kt3eVt3aD>t9J?S%885#CSuoxWK>@h>RZq=#ke;k#3f5f+*e_s8u?o;!xi$;14 zf6g!I`|)OT!i>kKqo2LE+|la3mzU+j1o0Og=Yw`t3LMaRm8;OumR=bDPvYo%<{xDm zj0N4*S8h8n@ScBE^zz5thWl;(91YPmGGEOE91d^Z@h6?($7Y86Y^n`4%y}ORVJdHG z+}+N|cxKT(Hl~aUvE%)7@3X5i?|>+jE<#$KOUE7xX*k2`E$qr!dG-yE;KMasDAvbhapRVVa?&Ml8jdr z7$VwV8#AqNWZ1xWJ)I?>nIS=T-Fc1$Y>Wr2V&)4v@G>=&?yysCkYZtYT~O=BV9LR8 z?ct~aqhT3_lM3`1H|F z;Jv#23VB@)hB(K%J@ZxX`PKQ@CkZg@Y5G#29wErWus-4T{hiC37#@f!FvzkntY=_+ zp}EdG^n2bA#pSpXc21W?Aq# zlx4#IHJtfquv~pobtgl~xlLCN<;}EG7=Ff7@_o2zu$;g67btLKcW;P3 z^kBZK*M$9L%*9n^=TGi+VX%sKWhiVoFOb;RZfQFa6s*P5mWq{~zQQ>3aiX8U%6B2g z7jsfqw%#==b$C`WDi literal 11806 zcmeAS@N?(olHy`uVBq!ia0y~yU}OMc4rT@hhU_&FAq)&6x2i%SN`ey06$*;-(=u~X z6-p`#QWa7wGSe6sDsG*f9a*Mq*ys9xXIB){^F<^DAY)e0tr)b90i8CHr&t+nL@g zI}{vj9xy6on*M*k-}`U-dXcr^c1vWuduHj!*KC|>9yx#gu6pUW-{0S!zO&B0W}V%= zs5sg6?_y4Tta|lrXQhPO>{Gi7%mdzCZ~a!W@W&CY~% zjIU-+>r-9+TXRcg-0|E+KmTa_oO-JC_SX-e|JNR>QdT$~X+5`a*8OWY7dK3nUmWwa z_wn}9nbKRe_mt~>yuG_7v&k{;rsw_4rFG&{%NOjZunVvGka6b6$6ud{h2x~-bA|7F zmi=}9bojaDtbaChZ=a7+UcL3%%-FAjlaFNwZD0AO{LT%7owxg4_X*oKe$T8I-?y1* zCf^h1^1DCYEtBBZIbC7wV|x0`-&?-!uKCM-_e9-h^s+mxcQa}CWiErgHM3KNJ9wub zT+h7zaq#bj-8YL)i(U71`(SVp9o)u^culPHPI?ZtTMS?33O?M!FIsq1fNE- z49AO%7=w#1QgV&htkXWM~Fg|u1dCS z2cv(F#ibLAd#uhLnYwI#;<1@&^HQ(1uGZcDhf90imQPx%>lUP4Y5aQn`}@Udc`I)A zPPsF~F5vOSDS6Hk3nwegXq@qt?Z{+>2IuI5KIxD5hBD1fSk|qo)#Q3w;Q^0wN8^I| z$J$cuHvD#Z$7ufaZ-2pN^CwdrzUxm2X1Xi=WZ$E2Oj{K>7iBLua}sJx3VF-1KQg*J zZ`P&~{?gRhRw)w)5Fh6-rnstFFtdUdB?f~U&P$bZd^OF zIj%~rV~gwdX)2mZNxB>brPfb`Uu@E}lUfk$8Ew^Kx;wsViRpoyS$f+`n_sWX{Ik7z zHJgvU^rM~yb#_N29skyJ7&ra;DUi#+=dG&d_Jv4O7Hx&jG8q9a--&!Oj*`h zv@p|p70-Qz+q0hf%~(@CecId!5^pT`$e#_L5&gn#hK!%&)~OxoyZJI(ZY96nkUX)! z`D^g(i%V0E@uitfk=CAZL6OIP(&mLh+PznTc$Z4V&xzXTbno(7!)(t?X)!sSmATv! zE$dR7dQ4JOtgp|L@QAJma@Z7bQ)#_#^yddRuZr*4m4E4AvANoUXMPSdOR{ZF3jCe1 zBm2nusoAAU(Whp4|4@xubxr-D{G#m-_*e3jJ&6mt_#$|N{*t}7Jj1sAUbFf8$xB{S zayN;_=$oGJb$WLH@#;$>B_@w95IW78I zC6>KEy>P8&;jd|X7F(R&Y<0!N^QULXS*29Q{gN6BD-T+)Sk61|=viCC=7`%frcJz) zRkbGWx03AcXCI7;)(0|HKRFOury*Vvp}Wo~tz)vw9=6DGlT9@}OKfWHK7YD4KSa?b zP02>%u4dAzuB8*xz5OqWI!zSt5P9Ftl_67Rcze#qn>RX59x+It4O=lkS2rQ!x{Nb- zZ^|7O_Y>Q?`NPH2?aGW+KFs;hzC(^PL}0Jn?_X1X?LNNGZs|L@;@m*#42FY>8y!FT zJ=%57jWajcl&v{XZs+OhhRC|0j%+v4)pZZ8^W)>K7XMhZYH@vO3K#br30d_kUzOxs zYAs6cST6ka;n3q|#U=3-hF>@X)9rKGE0Ql}PU&;2y-@d6Y}fXQi3>tJGB2?w9f|vN zgnJeLlTJ@fk7$Wb@yy$5Ci}d77JS$kW$J2utMr+z#+DBgA6bbmkPT?cR(lq(ao4)I z9Sfy27&D?4zBlgcF-mR_5pq1eqO^FP@`Ror2_M^MYUo@yW1l`t@yoXqUMB zBx@IjR9sP5z4pM|3QvP4vJn$k)o>(SS+(!L;j_J=-%>8ETDsQplEfnohP^Ar*u~!q zu0B(pn!|Nltm2K%YoFgwdOcmwwft#0EOxfDqMv7p|2v2ERr%A5PRw?ROlEUpe$2Bi zCC*(^@&4Mrl!=S^4(|zi9@MtleI|RTO3HSlri6fE*`9q`heh;CRW8r1+4fF!*A>fS zf#+Nkzf%9njaBvjoZ82jH= zetTHcl0IqrG*vH`BVucPukK0@m>pX9t!n`Kti zx%;Wh*U3vN<_ll#ULx^ER`o>7^L_suxlgukSs&s2#KUjll-Jv{8(o9V{Q1? zAB-Elf4o01<9M>B+l;jMAi?iCUvKTq>YFz6mwM;3O$`cd?Q{9!jRhew zhGiksSRdYtmyXLl9o+Eo;9b+2f0EPQ2Wr0BX#6+FXTC$FkKp-^hW1_QFS+};d2|#C z-f;ex;{DqG;m(B{5hu>%MXazk;1ike@Z#}5>2_bK`4$gWhzk|PZU_*nC@ zZiPVm)iU-U1N60z@8=Loy|$lI=qukedtRZ^N$<}$yVxH1)T|JA{HxIbnr* z#6K&KE7wiW?dKNy8uLE*ZS?yLGwtF_n_pQNDO`wkd!1S-TI;;&il%0e@+_;*vl5HT zW~#3{CUyJBroGQs>w2!6-~C*O_x_#oJylMBe=4t^A5j1A#B22{_x+y!{a4(tJ+k-Y&+RJ$<6oSy^e)fmo~nD#?fsmdt*M3QRRUhfIq^0#ly7ajQ+D$9 z?3br3=O*d2ExXQkr#J1|4A0hm&98QEu>81ev(CBPD>3$sr-OICE!`}p_H~=F_HT*J z72m$x>`Rnz{??hIeoy4`<;c}rriOogYxV52*0aY_&vH47^%GVd7dA!UI%7fx=-5$LZ{)1JW-~cCGpCcv>;qgY$Mt zf32HiV^?JJSCi!zytX&Umx~84(kjW^$g%jMYwiYHv2U|~ud&ljTz{zf!o?+?F2A%N zehBomwO~eRxn4sj1Ii} z;c#B*tj!BOIi06fB!s+Ao|;q=cAIN&?zgkICYw53oU>)lx28sEzi0Q0409KXY;3-E zb$ZkE8`GHUQht96xEW{s@9UrW%k~@pSN*ECS!3?HR-v_%%k5l}T$Y(k4CXK}6wWkm zo5CEsESHTZW%yfrRq;7#TRp*ENNSkntNxp2Q%-A${+vM zPPG%!3vlmGS#P&}m!*#QqBrZ-t5owRXm1hBT6K-}`8~^fAFFEm5~oS-^L@0v&vCY@ zw(*RspBI>46qW91ezEv)v%u}3w|WPv4v9M+GWc|CXHIZY>xa`tla8;x^i+jqvaHLV z$OYb~WF$6(N*>EEniC+@x5YMlt;y%JeLZ(Lc;*EtR>qVY1T=kJG-rXqO_M2;w#lbY zERD%`%awije!9lhbmO1fMH%d-&sLUQcu`X`b@hUuuTnK@cn++zN)?eiT$Y@Zw^m%B z&raNbM#%nUy<#=7Zz8X`8AN|gFI;{;d*{l9TV6aoR+fG9xM$*=q{S>3X8o9#<0_wR zxtPu6y3;a&iwF7hO{YLAKE;@2XV#^YT$YkFe-pzM+ zm2lbpFs}b@kn_MWSMA1fXKPQ9Nt>zR5sE*+j2 z7rf{MXC;@d&en}oGEoMzNm-=ARY8q>DpP35<>-Ekr>Csm~gp8S4D zc~Pa{$!A9rOg35gDT-8COj^3eH}`qziFI2y3nd2MTy+1^U$L62=Q8%+Zc~ZY?`^8> zs9!mC+mEoLcVFv061@NFWBD#N<;nMJsyD7psIr=Lc*pOLiv#19T1v)-eM%Snd-PpO ziOHe8eNWz<+u1z(;X7sBc}q7&FHu~*R44n-y|+odk3tMM^(IJ!+qZ}-MD1X`pkZ;# zF0D|^WbMtT-rBdP=O~^&J#m?B=dN2fbr12MGMjoM-ip`qRQ3N=cJ?01Q`g%bbMFq! zJhR_2ZDG?K-gj=NHn_PiQ`bCj!mpjNuk+q9Gyk%vvgf}NzAk8!-?}baKi$*Ta_cLm zBS&JLcXx};oDy&(eait)-s6tKlh$1LcV*IxxqKn7Lr!W<5?rpn(P75q7{Pdt-JGZS zFMgGXDPlM}FFkS6@^n?zz<(EasofGXIR9no3!gM0XI{(yL5E)jF=|&uth9Y`{f&9v zOV;k~v!+d*BeY2I)a*HP7Itt>yKr*ulKpB+RE|H7nOAo%POa_srCZwSVb)x|G6p+t zeNeMb(Y6lG`^gkku<57&A^&SRl@;3^I{Z!?c4134UgWpw@zLD6?ms-gwO=rv{qMCv zsMAE&sim-VyYJ0Sb7H^Bwz(_hxiUxdG+upTU|IWzdtShNmydHTKR4aHzO{CzpS@=F zgY@;hpPBXxTz#+jhq2(#xy_&TB{*C5vqsc9TGYLs_tW&p!qv`+@5%-0n0^#Cy*jz# z{et_Cp3IPVtD+_ocG`}FVHJN5r>+7s~p zb@ztNFOimxeETr*9JOJ~=bT+2qm8O@|KNDLf#P$$aL( zTgG{b!l_x?Rb0$3h3{_TII-@_zCGf~H zk(Fs0=bq)6tjSH?qgQ+-eePkNN09xS70O{&?!qj;XhfyxzDr@x`RICym=4 zZT5^*y_sIPB+x5eYkfu0OTj-8YBeF8`mL+fS3WbpxbJ|TUcg-MwJYy2{MuU>9~1a| zlF!k1pHugy8q<7e3Q=l zNlEDkrpy(wp1Y9SDc=5wTG5@wspZp5SDzDdO!lqcG-un}os~P^$42eh_Ii)5)-I2I zd7GcM#IyaJ-1C5S(?d;$G{O73Rbq=$f|iS0ZaVeu!3Jo}iXEZ3};I zV6H*7*UW|5tuDF^M`Ac8D%MLnnLbcYyu2cXBZ%edhg|;>o`0EQUvs7h$?i8?_~v)! zOe4XTe}$G@-0Oc|nfOuq=WhPY7=5M?eF=k(=Z|;{w{LDNgXo` zqxUv_E6mlj^H|&!>oe89IQUj{q03U7YdxJ2re_om?4Z`jzVSb&81m-OyazN4C*W35#1*?pW2S9O#p`*i#~v`V6)RbH?~z&Rjk*}PMF&mLIrd-w41 z>)1KzmGgfW^JuwFX0)4F(f6{=b&sRX=FFFKzIPs8qojG{`H{zLu@inwe7)&({+!c% z+&0EJPmDevNle;!=Wr4K=B|Z8{=)8}p^Ix2-)}lp*}XX`d&4=~pWM!_xvHm2X9=oo zY!SI6pt!hED{F(m@=)cYE^Xh0<^Ba8pRYFEXPuUWrC2~a*U68~{|~;;JA3KMlG!3& z41e{v{S{AA^Y;DY+wR&i{yBeO>b0-eqYu1Me|dJDm@QM=w#M$)>(t&ePPaaA zVzQp(B;}u(TuX&M)VbXcVQ9;5d@$>Fbh`X7FrLM2uQ@vpO_WJWB zzkW03J}z2%!SL1v1_mL^%#etZ2wxwokg&dz0$52&wyjcxZ-9bxeo?A|iJpm`fv#&sW|@(a9hZVl zQA(Oskc%7Ch@zAer{{GxPyLrY6beFGzXBO~3Slr-Jq%Dj@q z3f;V7WsngNGh9-OlZ!G7N;32F6hLMsCgqow*eWS;DJUpF4bLnnNp`I$fm;CLgN-jP zNzwN#C`mS{$SrWKDA6}EFtE@yFw!-$)JN7|kz3&F3)h`j3=PHP{9OHt!~%UoJp=vR zTofI}C9Y)%`>^UL4yi0i)elN7&Mz%WPIWCy%_~txwzMFpv>4gW(&AK*5ZK*0DQWr@ zxe%L?^|*Wb2Eg@zf-yZaw*agIq#9XeN-|tsL1|GASWQZ@erir?ZfaghvA&_6A&Q4e zGSd(dgyJ8N3^)jojjzZpfSZq^8Wvt)#o%zYa>-9F1zGCpVygsluT@HZa%PGZn3-yv zXp)wcYOI@@VrHmol4_c!Yhhqyq-$tlVUcK@l9HH`WQk;yXI^nhVqS78$f%0k0=>-4 z6syFPq(mcQBLiJ?(=;<(lQauMT}w;zMBUWHWW&U?6oW)Fb3-H}{EITvGxHL2kX;2b zDkU?;$}%N6CDGi#Qr9BQC{5QS$udPZ(ZtYH*UUJ{GQ~J4ImOVx3~UrA9;_S#JZ+VX z^b8Op0Xc~!Y57IDwo2idc`5nj#R#d8%-q!Al0;Cj8JZcISsI%fm{}N@8X23KA{2$C z78Pga=YdQ$G|)3L0?VW%Te;;I&Qaie>y((T z59ZqF<5P@bQgB9oc^+6JB=6!e2cZ;hiephpd45s0f02JuR%&tyesi!XhMN?Wnx0u) zQdH?lz*A`IY#<5NBeS?9zo-(){@~O?2oK_|oJ?>~DJX!80jtDhNbnaYmZheEqecNH zm7I~7m!6tps|3w#F!4+*3E42&*wi37)lk^5?dwr%-jNSJS%8G)1M})`tpoaP~tK$G6WZb#vw+=RwkBK zCPqq-*jAvW_l%NL4NQ{^Omz*6lTvg|%##dslZ?$xbS+a25=~Q5EsZS_Az2+Bcocfi zBE-2O*Bb1FtSKBPcun@lpR#@ zp0TcxMTmj9m9eRnu^~OYXKH4al#*y^s%w~HVXkXpY-pyNWMP!7YiVkcWN2V)WNen2 zjGEXei9Lw-EUk>qtxPOn-m}pMRsX@ztY`zOAbowUkTa_dr~bf>R42$=pUChdLzv;4(3vpmtKnGXmF7f z0wgIOObiyeFU_qw;_2(k{*+mUN7X=pX|*Q<1LGY}7srr_TW{~4 z?mDa>!4{A|bI*Ti{~1o2$zd0prhnFr6|r>@uD>MV)WNN4<6$H2b*JXfb3u7VnbRuI ze=t1270U1D=XlQ)TCk`1$)#z+2j(zbKRSEe>+<}DEyfPNxEX>ED7`S_NN{iP?|wGj zET=7}Z4D!X4SR*q7IB6It_YU9Y>YQ}Zt|REoZ#laa(Cn|rawIM!siEXy@AU*nu_c2 zU#K~B>w58J-}beP3>$16?(sZ$x8eI-hFv}Nc?}V&Icx&AKufC`7#RMnXAe=g?Z5UZ zx8W7f&x=tQ?%K=v>N~SrcA%gUD~6nwla|vm3{eNy6ZfAnFfc68dSk|cE*l^kCdAB; xAlPt+UE#=Wev$JN^%xZM?EZNtT>B#NNB`rMkEbjDdlrMVdAjUEIlOQ3tE2OC$G;3Ri`s-g^zMQk-G8lr)~UzZG{~ z{NiiY#jqW(%pXSShiHiCvW6VV&uQ!ojEge?%g+epZ3JBuYLROj7|DEwa=C3itCI8TDUF3K2_YCpBR%f>8kpy{r6AS zGBOlDJ7c)FJ}*y?VZoLySFY@vds%v$$Q=Dgb!MXTelo~Kvm9Gy_3!ZyW2XNy=Odrj zM;u~&@0MY1WA$y0`*n81V-_+UhEd^VoGuOe1>o9x4dd2)z z$!R}~4@~6Ru;)PeK0$}M)$R|nKYiQvvt!%d=vAv$SulKgdD;E-KdbA-Yo2^8Vc>au z!1B2LzmNSK!hxUj|9{JGxc9%){qsM*ciaxLj3*w6&#+|d|E%-sG4qb|i5*9ND!pTs z(9hrY_$lMZ;(aHKbmb5Hm3;8#IKyf72tx^Nw&Y3%Y1SY5Oy8@o{**i~Aus>oz_QHh zQ;g^Tq$#>zjtdA4J=*%Ar0;c5Xz0=1s{ zyUFcdpxwZI=56`S)}oo+4ad){FU-zY+m+F2XliP@?7HyBqZAzsHcQJo`3V%BH#sU90aqkC@cz zANZ4_buY;BCdAnje?srPu4(gPB`>TG_BBA=KDyvJEFYC^2SjxPidd1p; zPX|s{{7_x4XX4Jp$#ys74@B@C*v_b3cj)TR@Qjz%fAt#8m5&Iz5V!^7JbyZ7I^cE9y-hPR)>t*`ZtdsZ>}1-5?M zWj1Xu!~K`#oO{^!_v(am*nFtJ6~Xt$ZcXmt8zIk_Elx94fBfjZ z`{j4^{~|^Q=-G)4EBGQV zZcKLn@%LZH`oiq@VxggqA&R$xOfF0_Z2PrHFyJW%%QU_NZ<${oo40YL&yCx?j34H( zK9)Y*`Y-BPhQwTk`7Gzm+sZkwUA?++<3_>9l8+-9!uNJ(hjbJ_x@GEXt59QSq0j!j zX5!Ml4q`>Iv9TVd2YgPreO$h5*`xS>PvZ~8Sshs=-jpHP<9ER8PlKrda~N6J^XSrooIdFbMb)vFI1GSqwc zvidQWo_@vf=jJxW^BVsayZ19WY>;)ZVBEZ_p+M|W@sbl)ISS($4*n77bj{4n>^QJ? z6~m+562b{D8OmzQWb#%;=w`mRQnDZ1hLWF}R6d*%LE4nb4a# z@yBH5#ljOj{r%nZ^73wEIC!%MgjJpi<+y+8o1DCaALGBRjSCO(KUsU2`9IeOA(vE* z0`COX0@>RcUs+yQ$SQp_C}z_7$ZW=!aPpN=`2QajH>=MzJ`=sEAI0&|&3TK; zpC>LBjEz@Q7PQRemEgTK=|gH%%#Oov^*dME3c6)xW)}Igt3|a{oPVzV$oq`!flY@l z{+Ty3f9uIf7MUHNYwE6lbCKBfn(;`9)rU=oF2>aPmo4+1z3i*?lh>zCpN>A7uRf{y z&Ye2~AG=RFJlZjFh9$$|7t!6qGi)k1U3-6F?>V0B_3Ojums{{o?wHatqfdb)E-FfD z4krhLsi`T$EO%BZSx29uyssbJf@CY+Y&`yBW$^M3x3bqCJrlOP|?TdEpFOyn5?cB4g|83Y9c6^_{f9sb1 z?_G9zZzYc%jjv^_xctWP`ks%+q#Kr*JwG$^aIZGQ(&fw9TlJOY#ji5mHe*;``h)#g zN;FsIi34&qr{5ipda0`|bGAJEyVLf(y9_K|q3V7QHXfIIyldO0jJFnbe|AjU`^qYI zTV0KSdE9N`(_HSKKkaPaAQJsUgy-VFx3YRKdVU$Je|)a~{lotM=XTmYl=yZ`I$x#a z%F-gPX?zar4!^c)T(+;e?(tUJ!{>Bsv)Si#$TMhZX%+nYQyEkFbSlFfOGfo=R+)bD zY-Y;a*U7xTww8N$*tY5GXWQ4;9pP^+V2bqL)ADI2tHY1~=O^)rmmIsFE_bX${>!np z{dEs-%q#G5vWS>^;jBR}bH^6G{|9E|fUMg1^>MS}@#*`vG6%cG_bW12-&m%6Kl|>k z(l?*a+aF)(+efvL>$6vD z&2>FI@A5JC&0FR!*`87=xw-gQn9r=J6LIk-#tWsFcDz_DwDZs12;P7&Mi&YF$4eN` zNAO%Z`mx9tpubNtTaQc8n|Af|o_@XlZfRD^r7KR~ zUjLtWa=Gtpw{r`oDz*LOQdii=?XK|s@9F1^dtV+9w6Do~)0*2-7N|ID?a#bR(K`#* zueP!IIbVcx3v0^;S5E!&tP{?kzR-N}*useoCw*7nm7S5{_{ett{Ist}S+pN=-`etB zWzzhV`11+x<}`m3KRRK>c~%dT-!4aTw=atRT_ODS{`@1MGb$F_YUv9J>S@cI{p*^4 zFxbRO>G#g3mn)v1skDeRQE>wSwo&P|R(q?6;$t>d#Z{{KtKKRiL1;jZn!5~u1| zpZ|=j68HPL=p4~6c*WB4gLUm$&HxsHx%cl!$utyttrrc`{&V7m36oo`Uc(MQF?)s| z-S;kSGw+gTuu4C7_|B&j$1bT$@7iId{Hn(us8vdz9<9&w7#U zVj=Rl?19^%e-l0>iDvqf79?9SH(apmuWq!@K zwYp3K!c{%O)gj%LGZe$vCS5k%9B9VWmAJ>PWc%J<1vzGU5usvbH8&T?ZQFLQ{rBS( z*Ln$M2894#iNERz-eEs15AvGdSuoXIVaF$~o7w{N%9W*=F{ zdtk@c@Z8+o3w#{CEGg;`nR>v{``mc9y~uc^K(E^c1G!gwe#2X zSlH)sZhz!i_B-Wxj@9~gz4zbqTQ4h8NX%vM-?!snk=(AkN9L)=-#H*>G3o5tTMQ*{ zp4;8I!(DLry&D6EMeAhI%NtU+*M>h3_O}r%%XxQaXIWaH^1biu0%1*E=eOUlt2U_L z|Kq5>(kB15-|E(@1nzaNJ(ZZD&y}@JqIL3D1_QGzoG&`!Ke$}8d6p(~;LgtCjb(3d zZFs!ct@n~)x$mC$`YuT|;xnH5U$?vXe*PJW9v_iXX;+w(H+Zc$?w@a;KwCt( z_T598`zZ7DDZRawm6bN{pSUp`n9(lJB@^?b+OBr_ukUAO8n;iMK0SI_WMpLL+1cjD zkKF6f`SJVRZvUDm(&l+Sr~bP~yQSyt|M+XE_jD$PWj-^LveI8Mbai!A1O^H+B;44L zSaW%qZ((i5tWS?k_+|L!_6M5S|2%1JvG@D>gP)V-+Exl{=<6T9^#6~3&HHZm->I_21sn)s^*0vV!^qkrT0%pM>t0`5nj+S#T{NK_TQ1 z!-mYu%XU~dE?T6t+|R-KPG~0^M~=Q;w>*R1-*<1P`8jOJxTtjZ_U>)lPwm&KV~{Y( z5U^}yaZ|05+?=_WY4=&jUO$C1j1ounJ&e>Jc>GB|*2Ae;*th)22;p z<9#{xvcOB9nMSHVB70awzA;T@o*@5Xg4Lg8zOzm0y0UeSWlr zMDLv4KN%LC<8Qh4+r39Zut-l^Tl>e%U5v9O>}o9T)qaoVk+-vPG2xeT;b(9^^*wX> z@4c*>o%j8gedc}rp3Z-_i_8c9{hjFlQ7x%?PtA1Ui@R0D_#8qX6){Md}q|`~RJt z#fpjlzrDS!e5+wCW?kbt;fyTK z=MI}2QZ9aB*-&{p{owm;i|+4Le{K0Di- z-QK)&o`-{t+}9m8o7rqc!vtH(%oC=7dUTH&V!G`wnUv(Fs4EC8^0fRsv&=I-GE(xI z|9kc=hwF^1LvQFSTQf3t+Er@k>bl0o$(iNdk+6B@^2II0$vir+Lyo6&&mNn&*jQc# zOIKmff0JviayQ+ct;->F_U-;P3*s0$Q{*KkzxkQ}ddrj-sV^EcA73bW@WY9_>BxjR zZ+!R|ZlBURadGAWgT8-edLCu$QpM~W))bhO?=r3MD^n}izBT8KPu=@w-`(80^U`PD zo1z&k^H+H8nG?#l7!0HM54_}YxaoIYZ5f}#8@4w`IG?1X7y0)qo?uieV^Vy?Uhuy8 za?acX+Uxf$n!E4LExifl+jDbrTy~eeZJN)>vY)%|V|TpDYUVPS` znOV~NdwXx}tF7J;FUP>fC)45B%(k%p|G$YU*=IsjE-$)!WA@U^LErON{yF`8t=p1g zR~k=Pvoe1(W=K8qk6{;|vYIxN((7R)XKfF_IDYNq!CN;voiP{tD`DK3J51QZFy?6iZ`}sb{-|e|-n-q6{`z>jQ^7{1q2}eWmI}4>B zx9|UJ(_XiO2~@sV-dpI*&Ld+XAZuNAq)SvgB(UMt!;+v&5jWjeZ|MrZaYyK9_}{;0 z{>?KiW;(VZUTxB=2EM3JhR6?Y=?N((Um3AA+^@(zzoFRs{;h>~6g~KUKb;i6T`FO^ zx^m{+U5Pg~Q&a8TU)vP?S87PTD|(?a{IrW^J~9JuG{rWtKeMW>$Te- zy;{AV@7=8|2b%~b`%md}ZK7^&Hz{)}%DO*khHm2ZM~U_q?);cOd-m#3*$K)lt1}K3 z8BN&pqP>t&U@ohpoW$g_`FpmhgL)8~gd=`kdoqm7pywcCywv#;}Ay?XV=jzZ;qpXa_`uxuGy^!B`?y4&w0O=}6|SpWHes+XvO*jveu zOOCE$Uc@Tz;8D6@*|K9rQX2#x^BoR*&7JX`iAUP3r+fO@{gYqgYxZDS~uk7 zv*Px>c>X=*VLyNB!OO`W9@+o4ZQ}a&>d~{cM_PpiB#!=n^{DcchEv{K^Xi$3*1e3( zY!AZs{}K&bAJ?ns+_qrTCLvc>*B3W7DzDr9&MH*YAylb&a`CC#w{IL5-rkhC@=x^C zABIOYYK%*ynOYwk>|4!IE%bC!Tuqsk==BKR4wpY}b#?w>0b5o%I^8~{B_Vd6`y0!N zuQjqCncpcjWUt>V_J03=yS$xG#r}L*ZqI93{`G1&b3^EheDQO)uI8?N6RLYX>_7Y5 zUt6cHcza9!&AY`}E&;(2Zq3N!LZ7=R+U;^))pA73t?&%`0R$Cr75=$R)^)p|WT zOnOsJ?!WzcouM0(vRjWqDNERnE`~*&9V~Y`;oGc+WwmJ$@AyM3>uo63-@p4JZU%I?%s}4g|+K~R?T`N+}TjP$@u4+%5Fx6%gb$F zRD>nf)!D^|M=>$%(6yiM{)&Z*i|dEC{$3Gt_Df3N{U4eB6*_c}b?qs}Cn@Ql;`jX& z+-351ezD}Aiprbs8U7qB{C$wuIG*hRGs7K&PO)V($NBl+M)B$JuehuXWxgcO+X z6km%x5*iS2K)p|%pJAKQo@cA(*_fKLGF&*9eRpSY?K7EE-|I7Hi>rT^H%M@}@G&er zd~xt{ziFB8Z*Fd1v`8ZHn%IIYcaB1aU6ZrU9AiCl*-qVhvi<*Od@*ef<+mTdar)CX z|JdK}f2uEwR{1U1`Q&@zS4n+_gfpymJu|o8FcbQ~I470i&GE+fM;9|a7C5GCpmcG+ zoJ=}Hzy2M;6HH7E()oJ?jnmFJtdHA!VRLyt!-MGUd8`Y>{Eu(ptEf3vfAg=+l>UH+}gT2Q!cLht?BI=p>rFfLM_bA z!*?EvvJSwce|H;zrXdZ?ddPeeP^?s zvvli|>6|oa(uVV&pP%2pMP12VY+Igw(pg#LFOh3==gZyJ!}13_`o4$ZJ`Yc( z1eXct{#C2EWUWdLO#6SMNAcdm59>|W^KmfRemKDV;^uDis;{qFnc4XgDhmxaZQA4` z>#*3r{&wKit#0g__Uy2D^Y8BN^5c1v?`E%By?SHv@xBKstN#3{dHP(^IL+Zrtb>cZ zVcdb7T^l}DoN;41Hv9O#d)?f6Z(6<`pDn-bz5h?mwtN3JZ)iJ|c(^UoQq3{Jw(;4k zQ*+Djot!B3W6#rnXA>+L?z8bkT7=AX$eP^S66oMPP3Pd6$ju-ARliTXwk9&+evbR| zSNuLlADix&3~IjT-`@usg8udGZFZ%uz&jBwZSBMF?oWPiRBAHUs`Sv;>+$^)mED!> z?d2J!%->!1HtV*X!w&cI+=iAC2ItFH^kw@FHtYM#9AXt`lQW7{4~U59czIdw$SdQx zo}CBUzx(AK(w*D>aN4=UAMTy>l_+*pFq&ca)=@;P%}qo5`hL!7>^*k4v-i)6{b~=0P zfBc>3Jp1E`h0koifw&j=5)M5%Ik}lP#!a1pMQXo`?fsrVe`@!SQ?4+le>JIX&;s5kmD>-@>?mYh3aev+4 z3+v{#_6+fvAjCu&K?#X{`g3z@WDwze2ZPT zCLHgR{qbpv`0rJI2`{6qo$8u5hTPv@Uw`5>pLX@k&+|&2{&oGA%D(K^p5)_vSu8#Z zQ}!~1d8fbr@VR=^8~v_{f46PoYFqC1areAvZiNRdX0b)AX(nu5|Gd^)O}P;kb}*pA zUjA~(sq^!;OBko0yoq}O5n(*)o1%EZZn_tOE~ub5AXVtJ$o{J9Q4mvR5Q%o`Ln9V zcOrxN>gUql{|wGC{E2~*2XVbj$M;;PruhOd5x^t{lB)}irt2cN=yQ_EPZWE%b9faKg`W%30SW8lhv>7 z(keHmf7$!Bf3(U5EAktO)c#&n^4$0NpE=3T=bfL?x3_QY$sxi_8z1K4B1=P**Yg_d!G9>#d!8;8I}`%kj&#Gf02)8q@!HT$o)=RZT~y>h~x;tM;LCwQ<-n0nQQX?@P^>J>ko ze?RzM|1Do`@6^iE22Xoaf1lRdt+S)zxVe6v@Wj0dmzVjjsK^gK^6l;})?J_DVr}@_ z?N$}!=r3n<*txmDM1YB%;qeZepL3?)%HxzyxwzU)PBcJN!LdRtwZ^jVLDZV3Zw~}C zt)JLjx<|Ibq~an6pW(*q&U_6U98}9T%eF>eO<9m~jK9y{u&cHwa1GD)%@1ERG=Zs<`7PgR`Vw@Tt9Q2m@D%gHoraC#CITfaai>B z@^1f)jqmh7D(3U^+a1ibX_oz?v}A#-jYVgh#ghphW?5NTI!F0ELK$|7#?+W)ONPsu z%LOlee&n8(zTcbBmo7>2FSqVG(OdWTgV}%Gyp;2g*}loFOP6Cf|ETJl1>1)E54ok7 z9U_#je@*?k`W8dZch-(+1|^p+if)`;edJQZYs*ax3=)>0wE*{c|1INi$a^c^5mtZI zvdLgk*4OE~SyFXYGx!}!TAf{bY2)r=Z!34d7Fx)apjN;y(K)}MmkSJfRRd8|)X`pofFp{rNyauSew^6XjC*;%FsCpKhXTXRsh{LaD>CPh96v40C3 zo1OITY|Re;P_st1QQ+UX(CJJJ=kCYI*|_yJD4Y>g-EYHWU~6l8LpB4X@3IIR)iA!A*p^Z(oJ1IK3W&1cNgW0yMj?%+)J+80x%_iik|ck(2sfQ!Yk zN%uRngg90+fjX_tY`k3E3`Z3n~-r}IvG!~T0bS8A^8nRw*-iNn{q zzbE{dWqy)VpoMYsB>9F^l?1D`K4QCtA2Kt3^q*rPc;5cM&AjS&mY2;~MfLwN-PrL& z)#sk%k2k2Qz$h- z-*H59M%0MiC}U=N6;{98mT@AFSk%@mQR(XEbNAezWgfV9BhLX728Ul05B$6&&m0OlHx$wXt&KQP-`<;_B{yQeD{dX|ApZ)xCD}I)V zYb2Bz8?OZfq+V_QjVop?`-Afg?pg;v&DxjG;vV}(_TUuF;4R*d zmwHccdwO)Ot>YH9qT*s_miVYBsWKmdM+`L#>5>e;3m;fl+bUGp*$Hs|xZeDj=fTIF z1uXGtMN29gcYSs^fBw*=%BNGq5Ba>?{oYO|c9+PUVuND_B`*RlKbfutTXfFn2d7_2IBrop<5k(%!IILX^(DPwvpn+-(Fe0SW0R1+ zdB^NQ$Kpttmzs^|O?eN9iY^Om@mh08@`T?(o~g`%vdSITnlygCO=AZ2mXC0ngfj*) z=fB}k04>)LD_~j^A^2`Nlf`R=a<`A=`rAVmZhW$S&6*<@7CIMxK5L%c`rhrh;v3FI zra~{P7?v(w+Q@NWKI6GhvkupV@f<5+c*v6C9L96(lZ*=^-%I4~`#L8D^X3 zZ!1+O%G_W1`Pl>SXTIxbbe0l zZmK%h^zkdhe(#IF;(3m>F;%cXs1M`e*tMu>;WEbMHOmVx6!Tow+}ik9utRb>EWeqLQ%Y zeKs$vSOsJ`M8jcA20@Pa2ivOqc75kq)Vr4bf!HiQ9~I-=I$H)I1IHB`h4v{tlyz`) zWUPO=bov_QsTJpst5t96U%r>Si7(;ms?a{KOB<7q&+6K=V1dGnX=^KVH8eCH2p{if zXQ)(TpJGU6g4A_a<3 z42qAg7BU@Tj4dxySL>6PpLFiX$HyD@?2veSd;50pHU$pfMcYhgX#8x=V0GG4wDmwA zsGn0=XvimH!N6cq@PJ{OxcKA6JqItI(sSGr^<*n!NH)U>m#u;uR&Kd&mv?lMs`rNj z%=|3gb>GgG+1z=X{bt4fJGo|@O&Lxv8TS4D;Vr0I_Muzp-LKNR^ik_#UE{yzWUzWuh`3=v@#v+f&z z*3IHj;h3%=d*Hv_N9U(U?PvYomYQ^k^Tb{TrJDY>jMpbP1zbWdb}`JWerIX(^-6FQ zCzrZHy}Z!|cJ?o>tBV{C{p6APl>Fx2r}Q_gtrkv|Z&EnZdD6ondB^_L?++IQ7ep1U zp5q@}#p1ZbWwmsGSc2bQIh~jt7dA3%e`9y%Tj<;a*ZTRBA|GbF)jODRXSM=|siI(m z#KZ4*ieF8vvux02?D9CEQ?q^7{@c~UF42x#4LaTiJeVdGmo2vM@`t#l4mm4h#>TJ> zatyxgYPXESUNgR!@Zoaf+XRW}SNwWpBn15@-DBLs^~iSqoHvti-(wA&&Eq2A^2w2} zK_NO`SG#Z)lZYPRR9FcdoxFfAz1f z$%Cck#nDR-7I_C`M;0iGEKVqq|CQ0@JheX}`PmweFD^z3*Fd9Y{Bo0J8;gnq8b3Cg zPpe^ZRIzjVvq{*%S(0&up2+di!!NFdC2hNQ-*t=J6WPWi?+zFx?RT)`;wWZFEB#*) z!gxo>^XtJk=IWQ;D{)w-d|W-JQ6aIK@lojC8(T7kh4oi437%}p;%kVJ-H^+i;d`QY z=Erl~jz_oyKh1V!xTVZkx|(^_YbDV{m9k^=nk&Aj2)OK-u$bYZT*6j&g-)e&1#6xZ z%6oZz^US{g%$!}7!>RY&lm}tA*&GB~Y(rQ(j7?-`6g1!d%Amod&=KZ!WOC!AxpUt5 z>|47=sdCY5mAn?E-z*!zt*=ei8{W=pIQDku+YM2N{qJ#HIoao+rRG2HOnmiQ)3+u? zn~ZlZKQ8cup>K{uV4&dp+uPS)+xq+ad-uS=K#yB~|LV5>Y0}r#H7#XQ6l}OL zuiE`PQ|h7X^1L}WRm0wgDwc7ISu;BpXBUW0R`-7<9$PX|S$)IWHBC<${=Bt;E zOFq`~;9kSts;@%U%+_U0+wYc1`}_O5gH{yXaA!KUp}s)sgS)zU&Z0k`lNlJ+U+12G zUi{O|*IyUKpI^5vbH%^9vU=;Lg6_y0`gW`5I9!opbUm4WM|MZ}`nVYubu}Ja9j4Ek zB_(53!f|(3X>$il$aU7k|K9k_n>_dAbA9dpH|`1wWegb^84cH8w_bnk>g>#1V<-RW z)hi{x=?Y1mp|B{QX#*6aI9&)&xeE+rT;=bD79+Q+h#15`I8eiJGiA!LU?RI9qx{t0AEDC+@hjvI? zB|L3EUifn9^hXyKI`8;&N}F-TYG%-QwcM#kN^kc6{{B>A4%>mm=!Te2ziaj_VE*v4 z#>3O|;;Hf__4Z8#%`%TptM1*XrC{B-qLjHSwz2x#JK0Apd@>ddPmdn$t7e`i+V?El z-mb&go%wbLr+PqeF!SbZ4FVJGryNZzU^?}hXUUQ!EUo+sFL<7qFUQizqcG?jClA&QOwWbpIu4<|J$_(4|37Ub85mX>_15NeqXfc!|w~++u%xRdJoMFtLyU0&qk!a%yJ27o0$=|=vE^2BKc8PAj#;kD1!MjP2 zWuZ%YM#1V5hJL$W8u1$v9J^i3*DYsKFXZt0#+mTGX#sP@j)H}Y1HL?Lmp2oQNK8yL zSUb}=eV5Q9z_}j_4)JMLn0zhD3>ywxB1MIe}CUzW%UgS z2bsRTSls_9d)>}s6NNqe{oO-Dr-tqO_2p%9Ro^lu;m)Q#;R*}x$wsXF^6&3&ZQ(mx zv!_4h<>u!885$njx@C(AgF{r5)TPUpm5Z46|9Prkxx4J`gjut=4%i79bj6lG>J!}9 z%$3YD=ktW~J%%N3Zd|-n_x07)#N%mC$})eM$Q*9x=XZ0zQtEMp>#^BGQ&ZEb+0AZU z8b4cqux4gv?)dZRv~m#>XufpqTHa-u&sd&pkZ0Jmd9(6AS*JI3`{m{Q6Si%+|DL^J zf%AsuRm?6nhF$l&R;*)BD09BMYwMZM=k3qGdi}cDPr=kwbdgoUp0a~YtZVk|le->M z?A!6f)%3u}vw{UI3{9&0D=ZuB<^CykL_Bi&)6mI$V{7*GPpR!U*;>+$zuocUQTM|m zoNsP#=TA*d)zH>U5?cp%x*C9axmqm+|LF=rKq}eviG)~`i=#bOA zdGq9gcvq}ldvv0*yUcgCGxKa`hlhu^>l_Y^j_y8o%x%V2))To5ixdN%vaFF}vz7Dj z`0(^n{B|w}fzAfMgR4sz!sBbVetP6@|M!ZKAvk|2yZ0?vxR7yk`uS&@pSOR%!(2gCRW-siBO_zO8`%%P{~nw)&ya2Zefc2X0;aV* zE=QQPADWw(B)q+~Rl+n&#LL_Jt5U$6Y95N_*7LmYSM+@It)m zzge@SPXDWYywz=1^w+D0H)o_OEK=Pdw3};f9K+5p->dd&=;)N_oD+|$XuNvm%7R_H zq&BCY=UeS_Jm}9c`>s__{(m~HKXKl?zE7WuZh!aI+o^J-?9GjhIS&uD_B{J7{Bg;a zEh3BEdN=L$Yx&=x$RwcrM@w6KYTLxFcm*ZKP);fBkKI`fcRwWTytn@IJCP{KjttHg z;fUR3xgTa+)I7o5c@`WpK!EGRPfmina$O!?y39C5ZGKJ;Ic{CpV4fQsdRwcf4%*$ z1u|I@%lxl`G;ICsERZX^TJxY6dqUW+S3;gF3xhwM7G!V~VC+f!-yx{n6xd*6=)rS- z`rL*hg$c7c6oRVNLDCCzZfsEeA+#cmRjOZ8w&C_c{fRe@S1Is0Zb`dz`Es+q`Vkc- z4K1xhXJ#57zPA?CWl&naY?<1RA}60)3|!pYQ;)0r&FR=#{9NmNpfrP{i=WEsJjSkx z4Z><3=WEBm=%;g*#9)80NmXSpKg z@Aa(I1+!+&N>E;4#MH5bWqPMdj`{B8vugxgq7^T394HiZNEb;^3FTNG+K|!jrqYq( zX3Df-*)p{=h7z|qk}h#rBpzb%nPXx2<}Ozji%Q1{4P9N{eGL~THf(xu^!W@vmaZ)+ z%iJ8AI(Rn;8}PA)nVpDK73;oWJ?W%_lKd48k8im*?^(#+c$u5*p?g=^)3=g`JJ;{;%Uv?zh@!;J$+dUh#x5vf*jx4e8N-Bm6B&<5=I81%9Vd=oSFAs>)|NcWNgWhqrF<;xTApKv8<2&l27k7XliQCskE^ue(&*PUv(!;Y`j(c{D6e>*_}~ytjqb{-roNF`T6c5+!aQWqdx3{;u$HvO?$=Y-z$Je&*`fRsk$&v+guXDe9Zc+M4B`f=!(%NJ#o4D71 zU5Yjt|9o-vX@Yni`|eYx3Vt>$Jebj-&~Zd@#gk*l+hC*X{{IgfxTqG4d&ao=g{_Ec{oC2x8_Hj&@km+8+}Ev={A$xXhrv-r!jWCzhR|*P z_1C+5w(wNg$Zg5LFBi97T=vqX3pZ|b{QC84*{cRCCV@xn4SVJ5IT$?Np4qVXMco{` zTB*nX-rE#0u}pMenL20EHNlsXk}FoPKKk)-{6m}Xtc#fT%kysC`e2T-sw(Tsm6;jW z&q+_4AoK10?XW$_h$5kkLh3#z;zWN+AVApg(hVh3~V0bk5 zg3QW=SNa~veE9vBp@DDZ$_FwT87s`^iU<6D##Hn3ycWZO1#fa>Zts9y&4u9qto}ay;4qZEPh^)bN{(TtJQ+LLVpS!In+S@BLr})QTyCo$r81|oMpMRcxefz=Z+~o|Q z#@#f%=L`io-|HA0Y8UgfuiAIORXpXvotc|=JWuaWdRp(hxR8-Wkoky~Ly|1R=JK0? zEUOv5e*OAp+3dWgj>@p0MgfcW_JA|JMG;`;XR($gQ` zZaiO+|K;I>lbb#EZxB8=*Rp%v+*^}>a4H?-kmTG^_}Gn&LD}uuXMVH2{a3Dp{HT#L zF)>LvF+s87``z-J$I9<*^ae@%P^0VKZwCnqJdb{@6nuND)qC%fB`$llHniNnWIAi9 z@zgIY3mub_7k}E!Wz2p+bbcx83EelsCl?C1tac8))%{*8eZeZGh|V*5Ypx5fxV);- z@a(kJQ35WL6prMo)`glLkYHTFk|Q#^@5IbUoBJ{>os?O%K5{wTVD+H=a{Ii@zx&_{5|9oE#qEy^*E|RtXviEj)Q>oABD3hC=p^E*%@S z6x#o-%4&+$Zx9aQtvA}`WY#rb+fJAzu_<)#{@C4Rih&MW%-8}sgY(u1&!20eaH=VM zsjEN>W8{N0-XGZ}M* zUe$iUCS>mzA<%2qkRjxIaMBM>CuZ4?%xfJ2Ok`(doIW$R7_?SI@b>@uM8*?cb4*o| R7#J8BJYD@<);T3K0RU@2GbI23 literal 13804 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