diff --git a/src/main/java/com/glodblock/github/client/gui/GuiFCImgButton.java b/src/main/java/com/glodblock/github/client/gui/GuiFCImgButton.java index ff8404f3..75fda005 100644 --- a/src/main/java/com/glodblock/github/client/gui/GuiFCImgButton.java +++ b/src/main/java/com/glodblock/github/client/gui/GuiFCImgButton.java @@ -59,6 +59,13 @@ public class GuiFCImgButton extends GuiButton implements ITooltip { this.registerApp(15, "PATTERN_EX_TEM", "YES", "pattern_terminal_ex_w"); this.registerApp(16, "FILL_PATTERN", "DO_FILL", "fill_pattern"); this.registerApp(17, "NOT_FILL_PATTERN", "DONT_FILL", "not_fill_pattern"); + this.registerApp(20, "LEVEL_TEM", "YES", "level_terminal_w"); + this.registerApp(21, "SWITCH", "ON", "edit"); + this.registerApp(22, "SWITCH", "OFF", "view"); + this.registerApp(21, "SWITCH", "ENABLE", "enable"); + this.registerApp(22, "SWITCH", "DISABLE", "disable"); + this.registerApp(23, "CONFIG", "YES", "open_configuration"); + this.registerApp(24, "HIGHLIGHT", "YES", "block_highlight"); } } @@ -110,6 +117,10 @@ public class GuiFCImgButton extends GuiButton implements ITooltip { return this.currentValue; } + public boolean getMouseIn() { + return this.field_146123_n; + } + public void set(final String e) { if (!this.currentValue.equals(e)) { this.currentValue = e; diff --git a/src/main/java/com/glodblock/github/client/gui/GuiInterfaceTerminalWireless.java b/src/main/java/com/glodblock/github/client/gui/GuiInterfaceWireless.java similarity index 98% rename from src/main/java/com/glodblock/github/client/gui/GuiInterfaceTerminalWireless.java rename to src/main/java/com/glodblock/github/client/gui/GuiInterfaceWireless.java index 4b726d29..d4d3ade4 100644 --- a/src/main/java/com/glodblock/github/client/gui/GuiInterfaceTerminalWireless.java +++ b/src/main/java/com/glodblock/github/client/gui/GuiInterfaceWireless.java @@ -57,7 +57,7 @@ import appeng.integration.IntegrationRegistry; import appeng.integration.IntegrationType; import appeng.util.Platform; -public class GuiInterfaceTerminalWireless extends FCBaseMEGui implements IDropToFillTextField { +public class GuiInterfaceWireless extends FCBaseMEGui implements IDropToFillTextField { protected int offsetY; private static final int MAGIC_HEIGHT_NUMBER = 52 + 99; @@ -93,7 +93,7 @@ public class GuiInterfaceTerminalWireless extends FCBaseMEGui implements IDropTo private static final String MOLECULAR_ASSEMBLER = "tile.appliedenergistics2.BlockMolecularAssembler"; - public GuiInterfaceTerminalWireless(final InventoryPlayer inventoryPlayer, final IWirelessTerminal te) { + public GuiInterfaceWireless(final InventoryPlayer inventoryPlayer, final IWirelessTerminal te) { super(inventoryPlayer, new ContainerInterfaceWireless(inventoryPlayer, te)); this.setScrollBar(new GuiScrollbar()); @@ -248,7 +248,7 @@ public class GuiInterfaceTerminalWireless extends FCBaseMEGui implements IDropTo GuiColors.InterfaceTerminalTitle.getColor()); fontRendererObj.drawString( GuiText.inventory.getLocal(), - GuiInterfaceTerminalWireless.offsetX + 2, + GuiInterfaceWireless.offsetX + 2, this.ySize - 96, GuiColors.InterfaceTerminalInventory.getColor()); @@ -279,7 +279,7 @@ public class GuiInterfaceTerminalWireless extends FCBaseMEGui implements IDropTo this.fontRendererObj.drawString( name + postfix, - GuiInterfaceTerminalWireless.offsetX + 3, + GuiInterfaceWireless.offsetX + 3, 6 + offset, GuiColors.InterfaceTerminalName.getColor()); } @@ -288,6 +288,7 @@ public class GuiInterfaceTerminalWireless extends FCBaseMEGui implements IDropTo } } + @SuppressWarnings("unchecked") @Override public void drawScreen(final int mouseX, final int mouseY, final float btn) { @@ -383,7 +384,6 @@ public class GuiInterfaceTerminalWireless extends FCBaseMEGui implements IDropTo blockPos.getDimension(), blockPos.getSide())); } else { - WorldCoord blockPos2 = new WorldCoord( (int) mc.thePlayer.posX, (int) mc.thePlayer.posY, diff --git a/src/main/java/com/glodblock/github/client/gui/GuiLevelTerminal.java b/src/main/java/com/glodblock/github/client/gui/GuiLevelTerminal.java new file mode 100644 index 00000000..c72feacd --- /dev/null +++ b/src/main/java/com/glodblock/github/client/gui/GuiLevelTerminal.java @@ -0,0 +1,1349 @@ +package com.glodblock.github.client.gui; + +import static com.glodblock.github.common.item.ItemWirelessUltraTerminal.hasInfinityBoosterCard; +import static com.glodblock.github.network.SPacketLevelTerminalUpdate.CLEAR_ALL_BIT; +import static com.glodblock.github.network.SPacketLevelTerminalUpdate.DISCONNECT_BIT; +import static com.glodblock.github.network.SPacketLevelTerminalUpdate.PacketAdd; +import static com.glodblock.github.network.SPacketLevelTerminalUpdate.PacketEntry; +import static com.glodblock.github.network.SPacketLevelTerminalUpdate.PacketOverwrite; +import static com.glodblock.github.network.SPacketLevelTerminalUpdate.PacketRemove; +import static com.glodblock.github.network.SPacketLevelTerminalUpdate.PacketRename; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.renderer.RenderHelper; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.inventory.Container; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraft.util.ChatComponentTranslation; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.StatCollector; +import net.minecraftforge.common.util.ForgeDirection; + +import org.lwjgl.input.Mouse; +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL12; + +import com.glodblock.github.FluidCraft; +import com.glodblock.github.client.gui.base.FCBaseMEGui; +import com.glodblock.github.client.gui.container.ContainerLevelTerminal; +import com.glodblock.github.common.tile.TileLevelMaintainer.State; +import com.glodblock.github.common.tile.TileLevelMaintainer.TLMTags; +import com.glodblock.github.inventory.InventoryHandler; +import com.glodblock.github.inventory.gui.GuiType; +import com.glodblock.github.network.CPacketInventoryAction; +import com.glodblock.github.network.CPacketLevelTerminalCommands; +import com.glodblock.github.network.CPacketLevelTerminalCommands.Action; +import com.glodblock.github.network.CPacketRenamer; +import com.glodblock.github.util.Ae2ReflectClient; +import com.glodblock.github.util.FCGuiColors; +import com.glodblock.github.util.ModAndClassUtil; +import com.glodblock.github.util.NameConst; +import com.glodblock.github.util.Util; + +import appeng.api.config.Settings; +import appeng.api.config.TerminalStyle; +import appeng.api.config.YesNo; +import appeng.api.storage.ITerminalHost; +import appeng.api.storage.data.IAEItemStack; +import appeng.api.util.WorldCoord; +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.MEGuiTextField; +import appeng.client.render.AppEngRenderItem; +import appeng.client.render.BlockPosHighlighter; +import appeng.container.AEBaseContainer; +import appeng.container.slot.AppEngSlot; +import appeng.core.AEConfig; +import appeng.core.localization.ButtonToolTips; +import appeng.core.localization.GuiColors; +import appeng.core.localization.GuiText; +import appeng.core.localization.PlayerMessages; +import appeng.helpers.InventoryAction; +import appeng.integration.IntegrationRegistry; +import appeng.integration.IntegrationType; +import appeng.tile.inventory.AppEngInternalInventory; +import appeng.util.Platform; +import appeng.util.item.AEItemStack; +import cpw.mods.fml.common.Loader; + +public class GuiLevelTerminal extends FCBaseMEGui implements IDropToFillTextField, IGuiTooltipHandler { + + public static final int HEADER_HEIGHT = 52; + public static final int INV_HEIGHT = 98; + public static final int VIEW_WIDTH = 174; + public static final int VIEW_LEFT = 8; + private static final ResourceLocation TEX_BG = FluidCraft.resource("textures/gui/level_terminal.png"); + protected int offsetY; + private static final int offsetX = 21; + private final AppEngRenderItem stackSizeRenderer = Ae2ReflectClient.getStackSizeRenderer(this); + protected static String searchFieldOutputsText = ""; + protected static String searchFieldNamesText = ""; + protected static String currentMode = "OFF"; + private final MEGuiTextField searchFieldOutputs; + private final MEGuiTextField searchFieldNames; + private GuiImgButton searchStringSave; + private GuiImgButton terminalStyleBox; + + private ItemStack tooltipStack; + private boolean online; + private final boolean neiPresent; + private int viewHeight; + + protected GuiTabButton craftingStatusBtn; + // TODO: @Laiff: Implement command `enable_all`, `disable_all` main consern: + // TODO: Scheduling of all activated maintainers should be spreaded across ticks to not request all items on one + // tick + // private GuiFCImgButton modeSwitchView; + // private GuiFCImgButton modeSwitchEdit; + private final LevelTerminalList masterList = new LevelTerminalList(); + private final List extraOptionsText = new ArrayList<>(2); + private static final float ITEM_STACK_Z = 1.0f; + private static final float SLOT_Z = 0.5f; + private static final float ITEM_STACK_OVERLAY_Z = 20.0f; + private static final float SLOT_HOVER_Z = 31.0f; + private static final float TOOLTIP_Z = 200.0f; + + public GuiLevelTerminal(InventoryPlayer inventoryPlayer, Container container) { + super(inventoryPlayer, container); + setScrollBar(new GuiScrollbar()); + xSize = 208; + ySize = 255; + neiPresent = Loader.isModLoaded("NotEnoughItems"); + + searchFieldOutputs = new MEGuiTextField(86, 12, ButtonToolTips.SearchFieldOutputs.getLocal()) { + + @Override + public void onTextChange(final String oldText) { + masterList.markDirty(); + } + }; + + searchFieldNames = new MEGuiTextField(71, 12, ButtonToolTips.SearchFieldNames.getLocal()) { + + @Override + public void onTextChange(final String oldText) { + masterList.markDirty(); + } + }; + searchFieldNames.setFocused(true); + extraOptionsText.add(ButtonToolTips.HighlightInterface.getLocal()); + } + + public GuiLevelTerminal(final InventoryPlayer inventoryPlayer, final ITerminalHost te) { + this(inventoryPlayer, new ContainerLevelTerminal(inventoryPlayer, te)); + } + + @Override + public int getOffsetY() { + return offsetY; + } + + @Override + public void setOffsetY(int y) { + offsetY = y; + } + + private void setScrollBar() { + int maxScroll = masterList.getHeight() - viewHeight - 1; + if (maxScroll <= 0) { + getScrollBar().setTop(52).setLeft(189).setHeight(viewHeight).setRange(0, 0, 1); + } else { + getScrollBar().setTop(52).setLeft(189).setHeight(viewHeight).setRange(0, maxScroll, 12); + } + } + + @SuppressWarnings("unchecked") + @Override + public void initGui() { + super.initGui(); + viewHeight = calculateViewHeight(); + ySize = HEADER_HEIGHT + INV_HEIGHT + viewHeight; + final int unusedSpace = height - ySize; + guiTop = (int) Math.floor(unusedSpace / (unusedSpace < 0 ? 3.8f : 2.0f)); + + // modeSwitchView = new GuiFCImgButton(guiLeft + xSize - 40, guiTop + 1, "SWITCH", "OFF", false); + // modeSwitchEdit = new GuiFCImgButton(guiLeft + xSize - 40, guiTop + 1, "SWITCH", "ON", false); + offsetY = guiTop + 8; + terminalStyleBox = new GuiImgButton( + guiLeft - 18, + offsetY, + Settings.TERMINAL_STYLE, + AEConfig.instance.settings.getSetting(Settings.TERMINAL_STYLE)); + offsetY += 20; + searchStringSave = new GuiImgButton( + guiLeft - 18, + offsetY, + Settings.SAVE_SEARCH, + AEConfig.instance.settings.getSetting(Settings.SAVE_SEARCH)); + offsetY += 20; + + searchFieldOutputs.x = guiLeft + Math.max(32, offsetX); + searchFieldOutputs.y = guiTop + 38; + + searchFieldNames.x = guiLeft + Math.max(32, offsetX) + 99; + searchFieldNames.y = guiTop + 38; + + terminalStyleBox.xPosition = guiLeft - 18; + terminalStyleBox.yPosition = guiTop + 8; + craftingStatusBtn = new GuiTabButton( + guiLeft + xSize - 24, + guiTop - 4, + 2 + 11 * 16, + GuiText.CraftingStatus.getLocal(), + itemRender); + craftingStatusBtn.setHideEdge(13); + + if (ModAndClassUtil.isSearchBar && (AEConfig.instance.preserveSearchBar || isSubGui())) { + setSearchString(); + } + buttonList.add(terminalStyleBox); + buttonList.add(searchStringSave); + buttonList.add(craftingStatusBtn); + + setScrollBar(); + repositionSlots(); + initGuiDone(); + } + + @Override + public void onGuiClosed() { + super.onGuiClosed(); + searchFieldOutputsText = searchFieldOutputs.getText(); + searchFieldNamesText = searchFieldNames.getText(); + } + + public void setSearchString() { + searchFieldOutputs.setText(searchFieldOutputsText); + searchFieldNames.setText(searchFieldNamesText); + } + + protected void repositionSlots() { + for (final Object obj : inventorySlots.inventorySlots) { + if (obj instanceof final AppEngSlot slot) { + slot.yDisplayPosition = slot.getY() + ySize - 78 - 4; + } + } + } + + private int getMaxViewHeight() { + return AEConfig.instance.getConfigManager().getSetting(Settings.TERMINAL_STYLE) == TerminalStyle.SMALL + ? AEConfig.instance.InterfaceTerminalSmallSize * 18 + : Integer.MAX_VALUE; + } + + protected int calculateViewHeight() { + final int maxViewHeight = getMaxViewHeight(); + final boolean hasNEI = IntegrationRegistry.INSTANCE.isEnabled(IntegrationType.NEI); + final int NEIPadding = hasNEI ? 22 /* input */ + 18 /* top panel */ : 0; + final int availableSpace = height - HEADER_HEIGHT - INV_HEIGHT - NEIPadding; + + // screen should use 95% of the space it can, 5% margins + return Math.min((int) (availableSpace * 0.95), maxViewHeight); + } + + @Override + public void drawFG(final int offsetX, final int offsetY, final int mouseX, final int mouseY) { + fontRendererObj.drawString( + StatCollector.translateToLocal(NameConst.GUI_LEVEL_TERMINAL), + 8, + 6, + GuiColors.InterfaceTerminalTitle.getColor()); + fontRendererObj.drawString( + GuiText.inventory.getLocal(), + GuiLevelTerminal.offsetX + 2, + ySize - 96 + 3, + GuiColors.InterfaceTerminalInventory.getColor()); + + if (!neiPresent && tooltipStack != null) { + renderToolTip(tooltipStack, mouseX, mouseY); + } + } + + @SuppressWarnings("unchecked") + @Override + public void drawScreen(final int mouseX, final int mouseY, final float btn) { + terminalStyleBox.set(AEConfig.instance.settings.getSetting(Settings.TERMINAL_STYLE)); + + buttonList.clear(); + buttonList.add(terminalStyleBox); + buttonList.add(searchStringSave); + buttonList.add(craftingStatusBtn); + // buttonList.add(Objects.equals(currentMode, "OFF") ? modeSwitchView : modeSwitchEdit); + addSwitchGuiBtns(); + + super.drawScreen(mouseX, mouseY, btn); + + handleTooltip(mouseX, mouseY, searchFieldOutputs); + handleTooltip(mouseX, mouseY, searchFieldNames); + } + + @Override + protected void mouseClicked(final int xCoord, final int yCoord, final int btn) { + boolean focusOut = searchFieldOutputs.isFocused(); + boolean focusName = searchFieldNames.isFocused(); + + searchFieldOutputs.mouseClicked(xCoord, yCoord, btn); + searchFieldNames.mouseClicked(xCoord, yCoord, btn); + + if (focusOut && !searchFieldOutputs.isFocused()) { + searchFieldOutputsText = searchFieldOutputs.getText(); + } else if (focusName && !searchFieldNames.isFocused()) { + searchFieldNamesText = searchFieldNames.getText(); + } + + if (masterList.mouseClicked(xCoord - guiLeft - VIEW_LEFT, yCoord - guiTop - HEADER_HEIGHT, btn)) { + return; + } + + super.mouseClicked(xCoord, yCoord, btn); + } + + @Override + protected void actionPerformed(final GuiButton btn) { +// if (btn == modeSwitchView) { +// currentMode = "ON"; +// } else if (btn == modeSwitchEdit) { +// currentMode = "OFF"; +// } else + if (ModAndClassUtil.isSaveText && btn == searchStringSave) { + + final boolean backwards = Mouse.isButtonDown(1); + final GuiImgButton iBtn = (GuiImgButton) btn; + final Enum cv = iBtn.getCurrentValue(); + final Enum next = Platform.rotateEnum(cv, backwards, iBtn.getSetting().getPossibleValues()); + AEConfig.instance.preserveSearchBar = next == YesNo.YES; + AEConfig.instance.settings.putSetting(Settings.SAVE_SEARCH, next); + searchStringSave.set(next); + + } else if (btn == craftingStatusBtn) { + InventoryHandler.switchGui(GuiType.CRAFTING_STATUS); + } else if (btn instanceof final GuiImgButton iBtn) { + if (iBtn.getSetting() != Settings.ACTIONS) { + final Enum cv = iBtn.getCurrentValue(); + final boolean backwards = Mouse.isButtonDown(1); + final Enum next = Platform.rotateEnum(cv, backwards, iBtn.getSetting().getPossibleValues()); + + if (btn == terminalStyleBox) { + AEConfig.instance.settings.putSetting(iBtn.getSetting(), next); + + reinitialize(); + } + + iBtn.set(next); + } + } + super.actionPerformed(btn); + } + + private void reinitialize() { + buttonList.clear(); + initGui(); + } + + @Override + public void drawBG(final int offsetX, final int offsetY, final int mouseX, final int mouseY) { + mc.getTextureManager().bindTexture(TEX_BG); + /* Draws the top part. */ + drawTexturedModalRect(offsetX, offsetY, 0, 0, xSize, HEADER_HEIGHT); + /* Draws the middle part. */ + Tessellator.instance.startDrawingQuads(); + addTexturedRectToTesselator( + offsetX, + offsetY + HEADER_HEIGHT, + offsetX + xSize, + offsetY + HEADER_HEIGHT + viewHeight + 1, + 0.0f, + 0.0f, + (HEADER_HEIGHT + LevelTerminalSection.TITLE_HEIGHT + 1.0f) / 256.0f, + xSize / 256.0f, + (HEADER_HEIGHT + 106.0f) / 256.0f); + Tessellator.instance.draw(); + /* Draw the bottom part */ + drawTexturedModalRect(offsetX, offsetY + HEADER_HEIGHT + viewHeight, 0, 158, xSize, INV_HEIGHT); + if (online) { + GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS); + GL11.glEnable(GL11.GL_BLEND); + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + /* (0,0) => viewPort's (0,0) */ + GL11.glPushMatrix(); + GL11.glTranslatef(offsetX + VIEW_LEFT, offsetY + HEADER_HEIGHT, 0); + tooltipStack = null; + masterList.hoveredEntry = null; + drawViewport(mouseX - offsetX - VIEW_LEFT, mouseY - offsetY - HEADER_HEIGHT - 1); + GL11.glPopMatrix(); + GL11.glPopAttrib(); + } + searchFieldOutputs.drawTextBox(); + searchFieldNames.drawTextBox(); + } + + private void drawViewport(int relMouseX, int relMouseY) { + /* Viewport Magic */ + final int scroll = getScrollBar().getCurrentScroll(); + int viewY = -scroll; // current y in viewport coordinates + int entryIdx = 0; + List visibleSections = masterList.getVisibleSections(); + + final float guiScaleX = (float) mc.displayWidth / width; + final float guiScaleY = (float) mc.displayHeight / height; + GL11.glScissor( + (int) ((guiLeft + VIEW_LEFT) * guiScaleX), + (int) ((height - (guiTop + HEADER_HEIGHT + viewHeight)) * guiScaleY), + (int) (VIEW_WIDTH * guiScaleX), + (int) (viewHeight * guiScaleY)); + GL11.glEnable(GL11.GL_SCISSOR_TEST); + + /* + * Render each section + */ + while (viewY < viewHeight && entryIdx < visibleSections.size()) { + LevelTerminalSection section = visibleSections.get(entryIdx); + int sectionHeight = section.getHeight(); + + /* Is it viewable/in the viewport at all? */ + if (viewY + sectionHeight < 0) { + entryIdx++; + viewY += sectionHeight; + section.visible = false; + continue; + } + + section.visible = true; + int advanceY = drawSection(section, viewY, relMouseX, relMouseY); + viewY += advanceY; + entryIdx++; + } + } + + private int drawSection(LevelTerminalSection section, int viewY, int relMouseX, int relMouseY) { + int title; + int renderY = 0; + final int sectionBottom = viewY + section.getHeight() - 1; + final int fontColor = GuiColors.InterfaceTerminalInventory.getColor(); + /* + * Render title + */ + GL11.glTranslatef(0.0f, 0.0f, 50f); + mc.getTextureManager().bindTexture(TEX_BG); + if (sectionBottom > 0 && sectionBottom < LevelTerminalSection.TITLE_HEIGHT) { + /* Transition draw */ + drawTexturedModalRect( + 0, + 0, + VIEW_LEFT, + HEADER_HEIGHT + LevelTerminalSection.TITLE_HEIGHT - sectionBottom, + VIEW_WIDTH, + sectionBottom); + fontRendererObj + .drawString(section.name, 2, sectionBottom - LevelTerminalSection.TITLE_HEIGHT + 2, fontColor); + title = sectionBottom; + } else if (viewY < 0) { + /* Hidden title draw */ + drawTexturedModalRect(0, 0, VIEW_LEFT, HEADER_HEIGHT, VIEW_WIDTH, LevelTerminalSection.TITLE_HEIGHT); + fontRendererObj.drawString(section.name, 2, 2, fontColor); + title = 0; + } else { + /* Normal title draw */ + drawTexturedModalRect(0, viewY, VIEW_LEFT, HEADER_HEIGHT, VIEW_WIDTH, LevelTerminalSection.TITLE_HEIGHT); + fontRendererObj.drawString(section.name, 2, viewY + 2, fontColor); + title = 0; + } + GL11.glTranslatef(0.0f, 0.0f, -50f); + GL11.glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + + Iterator visible = section.getVisible(); + while (visible.hasNext()) { + if (viewY < viewHeight) { + renderY += drawEntry( + visible.next(), + viewY + LevelTerminalSection.TITLE_HEIGHT + renderY, + title, + relMouseX, + relMouseY); + } else { + LevelTerminalEntry entry = visible.next(); + entry.dispY = -9999; + entry.highlightButton.yPosition = -1; + entry.renameButton.yPosition = -1; + entry.configButton.yPosition = -1; + } + } + return LevelTerminalSection.TITLE_HEIGHT + renderY; + } + + private int drawEntry(LevelTerminalEntry entry, int viewY, int titleBottom, int relMouseX, int relMouseY) { + final EntityPlayer player = Minecraft.getMinecraft().thePlayer; + mc.getTextureManager().bindTexture(TEX_BG); + Tessellator.instance.startDrawingQuads(); + int relY = 0; + final int slotLeftMargin = (VIEW_WIDTH - entry.rowSize * 18); + + entry.dispY = viewY; + /* PASS 1: BG */ + for (int row = 0; row < entry.rows; ++row) { + final int rowYTop = row * 18; + final int rowYBot = rowYTop + 18; + + relY += 18; + /* Is the slot row in view? */ + if (viewY + rowYBot <= titleBottom) { + continue; + } + for (int col = 0; col < entry.rowSize; ++col) { + addTexturedRectToTesselator( + col * 18 + slotLeftMargin, + viewY + rowYTop, + 18 * col + 18 + slotLeftMargin, + viewY + rowYBot, + 0, + 21 / 256f, + 173 / 256f, + (21 + 18) / 256f, + (173 + 18) / 256f); + } + } + Tessellator.instance.draw(); + /* Draw button */ + if (viewY + entry.highlightButton.height > 0 && viewY < viewHeight) { + entry.highlightButton.yPosition = viewY + 1; + entry.renameButton.yPosition = viewY + 1; + entry.configButton.yPosition = viewY + 1; + GuiFCImgButton toRender; + if (isCtrlKeyDown() && isShiftKeyDown() && hasInfinityBoosterCard(player)) { + toRender = entry.configButton; + } else if (isShiftKeyDown()) { + toRender = entry.renameButton; + } else { + toRender = entry.highlightButton; + } + toRender.drawButton(mc, relMouseX, relMouseY); + if (toRender.getMouseIn() + && relMouseY >= Math.max(LevelTerminalSection.TITLE_HEIGHT, entry.highlightButton.yPosition)) { + // draw a tooltip + GL11.glTranslatef(0f, 0f, TOOLTIP_Z); + GL11.glDisable(GL11.GL_SCISSOR_TEST); + drawHoveringText(Arrays.asList(toRender.getMessage()), relMouseX, relMouseY); + GL11.glTranslatef(0f, 0f, -TOOLTIP_Z); + GL11.glEnable(GL11.GL_SCISSOR_TEST); + } + } else { + entry.highlightButton.yPosition = -1; + entry.renameButton.yPosition = -1; + } + /* PASS 2: Items */ + for (int row = 0; row < entry.rows; ++row) { + final int rowYTop = row * 18; + final int rowYBot = rowYTop + 18; + /* Is the slot row in view? */ + if (viewY + rowYBot <= titleBottom) { + continue; + } + AppEngInternalInventory inv = entry.getInventory(); + + for (int col = 0; col < entry.rowSize; ++col) { + final int colLeft = col * 18 + slotLeftMargin + 1; + final int colRight = colLeft + 18 + 1; + final int slotIdx = row * entry.rowSize + col; + ItemStack stack = inv.getStackInSlot(slotIdx); + + boolean tooltip = relMouseX > colLeft - 1 && relMouseX < colRight - 1 + && relMouseY >= Math.max(viewY + rowYTop, LevelTerminalSection.TITLE_HEIGHT) + && relMouseY < Math.min(viewY + rowYBot, viewHeight); + if (stack != null) { + + NBTTagCompound data = stack.getTagCompound(); + long quantity = data.getLong(TLMTags.Quantity.tagName); + long batch = data.getLong(TLMTags.Batch.tagName); + State state = State.values()[data.getInteger(TLMTags.State.tagName)]; + NBTTagCompound linkTag = data.getCompoundTag(TLMTags.Link.tagName); + + GL11.glPushMatrix(); + GL11.glTranslatef(colLeft, viewY + rowYTop + 1, ITEM_STACK_Z); + GL11.glEnable(GL12.GL_RESCALE_NORMAL); + RenderHelper.enableGUIStandardItemLighting(); + stackSizeRenderer.zLevel = 3.0f - 50.0f; + stackSizeRenderer.renderItemAndEffectIntoGUI(fontRendererObj, mc.getTextureManager(), stack, 0, 0); + stackSizeRenderer.zLevel = 0.0f; + GL11.glTranslatef(0.0f, 0.0f, ITEM_STACK_OVERLAY_Z - ITEM_STACK_Z); + stack.stackSize = (int) quantity; + stackSizeRenderer.renderItemOverlayIntoGUI(fontRendererObj, mc.getTextureManager(), stack, 0, 0); + if (batch > 0) { + stack.stackSize = (int) batch; + stackSizeRenderer + .renderItemOverlayIntoGUI(fontRendererObj, mc.getTextureManager(), stack, 0, -11); + } + int color = switch (state) { + case Idle -> FCGuiColors.StateIdle.getColor(); + case Craft -> FCGuiColors.StateCraft.getColor(); + case Export -> FCGuiColors.StateExport.getColor(); + case Error -> FCGuiColors.StateError.getColor(); + case None -> FCGuiColors.StateNone.getColor(); + }; + int offset = 0; + int size = 4; + drawRect(offset, offset, offset + size, offset + size, color); + RenderHelper.disableStandardItemLighting(); + + /* + * Mouse overlay. such large z value because items are rendered at zLevel=100.0f, whatever that is + */ + if (!tooltip) { + if (entry.filteredRecipes[slotIdx]) { + GL11.glTranslatef(0.0f, 0.0f, ITEM_STACK_OVERLAY_Z); + drawRect(0, 0, 16, 16, GuiColors.ItemSlotOverlayUnpowered.getColor()); + } + } else { + tooltipStack = stack; + } + GL11.glPopMatrix(); + } else if (entry.filteredRecipes[slotIdx]) { + GL11.glPushMatrix(); + GL11.glTranslatef(colLeft, viewY + rowYTop + 1, ITEM_STACK_OVERLAY_Z); + drawRect(0, 0, 16, 16, GuiColors.ItemSlotOverlayUnpowered.getColor()); + GL11.glPopMatrix(); + } + if (tooltip) { + // overlay highlight + GL11.glDisable(GL11.GL_LIGHTING); + GL11.glTranslatef(0.0f, 0.0f, SLOT_HOVER_Z); + drawRect(colLeft, viewY + 1 + rowYTop, -2 + colRight, viewY - 1 + rowYBot, 0x77FFFFFF); + GL11.glTranslatef(0.0f, 0.0f, -SLOT_HOVER_Z); + masterList.hoveredEntry = entry; + entry.hoveredSlotIdx = slotIdx; + } + GL11.glDisable(GL11.GL_LIGHTING); + } + } + GL11.glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + return relY + 1; + } + + @SuppressWarnings("unchecked") + @Override + public void drawHoveringText(List textLines, int x, int y, FontRenderer font) { + if (!textLines.isEmpty()) { + GL11.glDisable(GL12.GL_RESCALE_NORMAL); + RenderHelper.disableStandardItemLighting(); + int maxStrWidth = 0; + + // is this more efficient than doing 1 pass, then doing a translate before drawing the text? + for (String s : (List) textLines) { + int width = font.getStringWidth(s); + + if (width > maxStrWidth) { + maxStrWidth = width; + } + } + + // top left corner + int curX = x + 12; + int curY = y - 12; + int totalHeight = 8; + + if (textLines.size() > 1) { + totalHeight += 2 + (textLines.size() - 1) * 10; + } + + /* String is too long? Display on the left side */ + if (curX + maxStrWidth > width) { + curX -= 28 + maxStrWidth; + } + + /* String is too tall? move it up */ + if (curY + totalHeight + 6 > height) { + curY = height - totalHeight - 6; + } + + int borderColor = -267386864; + // drawing the border... + drawGradientRect(curX - 3, curY - 4, curX + maxStrWidth + 3, curY - 3, borderColor, borderColor); + drawGradientRect( + curX - 3, + curY + totalHeight + 3, + curX + maxStrWidth + 3, + curY + totalHeight + 4, + borderColor, + borderColor); + drawGradientRect( + curX - 3, + curY - 3, + curX + maxStrWidth + 3, + curY + totalHeight + 3, + borderColor, + borderColor); + drawGradientRect(curX - 4, curY - 3, curX - 3, curY + totalHeight + 3, borderColor, borderColor); + drawGradientRect( + curX + maxStrWidth + 3, + curY - 3, + curX + maxStrWidth + 4, + curY + totalHeight + 3, + borderColor, + borderColor); + int color1 = 1347420415; + int color2 = (color1 & 16711422) >> 1 | color1 & -16777216; + drawGradientRect(curX - 3, curY - 3 + 1, curX - 3 + 1, curY + totalHeight + 3 - 1, color1, color2); + drawGradientRect( + curX + maxStrWidth + 2, + curY - 3 + 1, + curX + maxStrWidth + 3, + curY + totalHeight + 3 - 1, + color1, + color2); + drawGradientRect(curX - 3, curY - 3, curX + maxStrWidth + 3, curY - 3 + 1, color1, color1); + drawGradientRect( + curX - 3, + curY + totalHeight + 2, + curX + maxStrWidth + 3, + curY + totalHeight + 3, + color2, + color2); + + for (int i = 0; i < textLines.size(); ++i) { + String line = (String) textLines.get(i); + font.drawStringWithShadow(line, curX, curY, -1); + + if (i == 0) { + // gap between name and lore text + curY += 2; + } + + curY += 10; + } + + RenderHelper.enableGUIStandardItemLighting(); + GL11.glEnable(GL12.GL_RESCALE_NORMAL); + } + } + + @Override + protected void keyTyped(final char character, final int key) { + if (!checkHotbarKeys(key)) { + if (character == ' ') { + if ((searchFieldOutputs.getText().isEmpty() && searchFieldOutputs.isFocused()) + || (searchFieldNames.getText().isEmpty() && searchFieldNames.isFocused())) + return; + } else if (character == '\t') { + if (handleTab()) return; + } + if (searchFieldOutputs.textboxKeyTyped(character, key) + || searchFieldNames.textboxKeyTyped(character, key)) { + masterList.markDirty(); + } else { + super.keyTyped(character, key); + } + } + } + + private boolean handleTab() { + if (searchFieldOutputs.isFocused()) { + searchFieldOutputs.setFocused(false); + searchFieldNames.setFocused(true); + return true; + } else if (searchFieldNames.isFocused()) { + searchFieldNames.setFocused(false); + searchFieldOutputs.setFocused(true); + return true; + } + return false; + } + + @Override + protected boolean mouseWheelEvent(int mouseX, int mouseY, int wheel) { + boolean isMouseInViewport = isMouseInViewport(mouseX, mouseY); + GuiScrollbar scrollbar = getScrollBar(); + if (isMouseInViewport && isCtrlKeyDown()) { + if (wheel < 0) { + scrollbar.setCurrentScroll(masterList.getHeight()); + } else { + getScrollBar().setCurrentScroll(0); + } + return true; + } else if (isMouseInViewport && isShiftKeyDown()) { + // advance to the next section + return masterList.scrollNextSection(wheel > 0); + } else { + return super.mouseWheelEvent(mouseX, mouseY, wheel); + } + } + + private boolean isMouseInViewport(int mouseX, int mouseY) { + return mouseX > guiLeft + VIEW_LEFT && mouseX < guiLeft + VIEW_LEFT + VIEW_WIDTH + && mouseY > guiTop + HEADER_HEIGHT + && mouseY < guiTop + HEADER_HEIGHT + viewHeight; + } + + public void postUpdate(List updates, int statusFlags) { + if ((statusFlags & CLEAR_ALL_BIT) == CLEAR_ALL_BIT) { + /* Should clear all client entries. */ + masterList.list.clear(); + } + /* Should indicate disconnected, so the terminal turns dark. */ + online = (statusFlags & DISCONNECT_BIT) != DISCONNECT_BIT; + + for (PacketEntry cmd : updates) { + parsePacketCmd(cmd); + } + masterList.markDirty(); + } + + private void parsePacketCmd(PacketEntry cmd) { + long id = cmd.entryId; + if (cmd instanceof PacketAdd addCmd) { + LevelTerminalEntry entry = new LevelTerminalEntry( + id, + addCmd.name, + addCmd.rows, + addCmd.rowSize, + addCmd.online).setLocation(addCmd.x, addCmd.y, addCmd.z, addCmd.dim, addCmd.side) + .setIcons(addCmd.selfItemStack, addCmd.displayItemStack).setItems(addCmd.items); + masterList.addEntry(entry); + } else if (cmd instanceof PacketRemove) { + masterList.removeEntry(id); + } else if (cmd instanceof PacketOverwrite owCmd) { + LevelTerminalEntry entry = masterList.list.get(id); + + if (entry == null) { + return; + } + + if (owCmd.onlineValid) { + entry.online = owCmd.online; + } + + if (owCmd.itemsValid) { + if (owCmd.allItemUpdate) { + entry.fullItemUpdate(owCmd.items, owCmd.validIndices.length); + } else { + entry.partialItemUpdate(owCmd.items, owCmd.validIndices); + } + } + masterList.isDirty = true; + } else if (cmd instanceof PacketRename renameCmd) { + LevelTerminalEntry entry = masterList.list.get(id); + + if (entry != null) { + if (StatCollector.canTranslate(renameCmd.newName)) { + entry.customName = StatCollector.translateToLocal(renameCmd.newName); + } else { + entry.customName = StatCollector.translateToFallback(renameCmd.newName); + } + } + masterList.isDirty = true; + } + } + + private boolean itemStackMatchesSearchTerm(final ItemStack itemStack, final String searchTerm) { + if (itemStack == null) { + return false; + } + + final String displayName = Platform.getItemDisplayName(itemStack).toLowerCase(); + + if (displayName.contains(searchTerm)) { + return true; + } + + return false; + } + + @Override + public boolean isOverTextField(final int mouseX, final int mouseY) { + return searchFieldOutputs.isMouseIn(mouseX, mouseY) || searchFieldNames.isMouseIn(mouseX, mouseY); + } + + @Override + public void setTextFieldValue(final String displayName, final int mousex, final int mousey, final ItemStack stack) { + if (searchFieldOutputs.isMouseIn(mousex, mousey)) { + searchFieldOutputs.setText(displayName); + } else if (searchFieldNames.isMouseIn(mousex, mousey)) { + searchFieldNames.setText(displayName); + } + } + + @Override + public List handleItemTooltip(ItemStack stack, int mouseX, int mouseY, List currentToolTip) { + return currentToolTip; + } + + @Override + public ItemStack getHoveredStack() { + return tooltipStack; + } + + /** + * Tracks the list of entries. + */ + private class LevelTerminalList { + + private final Map list = new HashMap<>(); + private final Map sections = new TreeMap<>(); + private final List visibleSections = new ArrayList<>(); + private boolean isDirty; + private int height; + private LevelTerminalEntry hoveredEntry; + + LevelTerminalList() { + isDirty = true; + } + + /** + * Performs a full update. + */ + private void update() { + height = 0; + visibleSections.clear(); + + for (LevelTerminalSection section : sections.values()) { + String query = GuiLevelTerminal.this.searchFieldNames.getText(); + if (!query.isEmpty() && !section.name.toLowerCase().contains(query.toLowerCase())) { + continue; + } + + section.isDirty = true; + if (section.getVisible().hasNext()) { + height += section.getHeight(); + visibleSections.add(section); + } + } + isDirty = false; + } + + public void markDirty() { + isDirty = true; + setScrollBar(); + } + + public int getHeight() { + if (isDirty) { + update(); + } + return height; + } + + /** + * Jump between sections. + */ + private boolean scrollNextSection(boolean up) { + GuiScrollbar scrollbar = getScrollBar(); + int viewY = scrollbar.getCurrentScroll(); + var sections = getVisibleSections(); + boolean result = false; + + if (up) { + int y = masterList.getHeight(); + int i = sections.size() - 1; + + while (y > 0 && i >= 0) { + y -= sections.get(i).getHeight(); + i -= 1; + if (y < viewY) { + result = true; + scrollbar.setCurrentScroll(y); + break; + } + } + } else { + int y = 0; + + for (LevelTerminalSection section : sections) { + if (y > viewY) { + result = true; + scrollbar.setCurrentScroll(y); + break; + } + y += section.getHeight(); + } + } + return result; + } + + public void addEntry(LevelTerminalEntry entry) { + LevelTerminalSection section = sections.get(entry.customName); + + if (section == null) { + section = new LevelTerminalSection(entry.customName); + sections.put(entry.customName, section); + } + section.addEntry(entry); + list.put(entry.id, entry); + isDirty = true; + } + + public void removeEntry(long id) { + LevelTerminalEntry entry = list.remove(id); + + if (entry != null) { + entry.section.removeEntry(entry); + } + } + + public List getVisibleSections() { + if (isDirty) { + update(); + } + return visibleSections; + } + + /** + * Mouse button click. + * + * @param relMouseX viewport coords mouse X + * @param relMouseY viewport coords mouse Y + * @param btn button code + */ + public boolean mouseClicked(int relMouseX, int relMouseY, int btn) { + if (relMouseX < 0 || relMouseX >= VIEW_WIDTH || relMouseY < 0 || relMouseY >= viewHeight) { + return false; + } + for (LevelTerminalSection section : getVisibleSections()) { + if (section.mouseClicked(relMouseX, relMouseY, btn)) { + return true; + } + } + return false; + } + } + + /** + * A section holds all the interface entries with the same name. + */ + private class LevelTerminalSection { + + public static final int TITLE_HEIGHT = 12; + + String name; + List entries = new ArrayList<>(); + Set visibleEntries = new TreeSet<>(Comparator.comparing(e -> { + if (e.displayItemStack != null) { + return e.displayItemStack.getDisplayName() + e.id; + } else { + return String.valueOf(e.id); + } + })); + int height; + private boolean isDirty = true; + boolean visible = false; + + LevelTerminalSection(String name) { + this.name = name; + } + + /** + * Gets the height. Includes title. + */ + public int getHeight() { + if (isDirty) { + update(); + } + return height; + } + + private void update() { + refreshVisible(); + if (visibleEntries.isEmpty()) { + height = 0; + } else { + height = TITLE_HEIGHT; + for (LevelTerminalEntry entry : visibleEntries) { + height += entry.guiHeight; + } + } + isDirty = false; + } + + public void refreshVisible() { + visibleEntries.clear(); + String output = GuiLevelTerminal.this.searchFieldOutputs.getText().toLowerCase(); + + for (LevelTerminalEntry entry : entries) { + entry.dispY = -9999; + // Find search terms + if (!output.isEmpty()) { + AppEngInternalInventory inv = entry.inv; + boolean shouldAdd = false; + + for (int i = 0; i < inv.getSizeInventory(); ++i) { + ItemStack stack = inv.getStackInSlot(i); + if (itemStackMatchesSearchTerm(stack, output)) { + shouldAdd = true; + entry.filteredRecipes[i] = false; + } else { + entry.filteredRecipes[i] = true; + } + } + if (!shouldAdd) { + continue; + } + } else { + Arrays.fill(entry.filteredRecipes, false); + } + visibleEntries.add(entry); + } + } + + public void addEntry(LevelTerminalEntry entry) { + entries.add(entry); + entry.section = this; + isDirty = true; + } + + public void removeEntry(LevelTerminalEntry entry) { + entries.remove(entry); + entry.section = null; + isDirty = true; + } + + public Iterator getVisible() { + if (isDirty) { + update(); + } + return visibleEntries.iterator(); + } + + public boolean mouseClicked(int relMouseX, int relMouseY, int btn) { + Iterator it = getVisible(); + boolean ret = false; + + while (it.hasNext() && !ret) { + ret = it.next().mouseClicked(relMouseX, relMouseY, btn); + } + + return ret; + } + } + + /** + * This class keeps track of an entry and its widgets. + */ + private class LevelTerminalEntry { + + String customName; + AppEngInternalInventory inv; + GuiFCImgButton highlightButton; + GuiFCImgButton renameButton; + GuiFCImgButton configButton; + /** Nullable - icon that represents the interface */ + ItemStack selfItemStack; + /** Nullable - icon that represents the interface's "target" */ + ItemStack displayItemStack; + LevelTerminalSection section; + long id; + int x, y, z, dim, side; + int rows, rowSize; + int guiHeight; + int dispY = -9999; + boolean online; + boolean[] filteredRecipes; + int numItems = 0; + private int hoveredSlotIdx = -1; + + LevelTerminalEntry(long id, String name, int rows, int rowSize, boolean online) { + this.id = id; + this.rows = rows; + this.rowSize = rowSize; + this.online = online; + if (StatCollector.canTranslate(name)) { + customName = StatCollector.translateToLocal(name); + } else { + String fallback = name + ".name"; // its whatever. save some bytes on network but looks ugly + if (StatCollector.canTranslate(fallback)) { + customName = StatCollector.translateToLocal(fallback); + } else { + customName = StatCollector.translateToFallback(name); + } + } + inv = new AppEngInternalInventory(null, rows * rowSize, 1); + highlightButton = new GuiFCImgButton(1, 0, "HIGHLIGHT", "YES"); + renameButton = new GuiFCImgButton(1, 0, "EDIT", "YES"); + configButton = new GuiFCImgButton(1, 0, "CONFIG", "YES"); + guiHeight = 18 * rows + 1; + filteredRecipes = new boolean[rows * rowSize]; + } + + LevelTerminalEntry setLocation(int x, int y, int z, int dim, int side) { + this.x = x; + this.y = y; + this.z = z; + this.dim = dim; + this.side = side; + + return this; + } + + LevelTerminalEntry setIcons(ItemStack selfItemStack, ItemStack displayItemStack) { + this.selfItemStack = selfItemStack; + this.displayItemStack = displayItemStack; + + return this; + } + + public void fullItemUpdate(NBTTagList items, int newSize) { + inv = new AppEngInternalInventory(null, newSize); + rows = newSize / rowSize; + numItems = 0; + + for (int i = 0; i < inv.getSizeInventory(); ++i) { + setItemInSlot(ItemStack.loadItemStackFromNBT(items.getCompoundTagAt(i)), i); + } + guiHeight = 18 * rows + 4; + } + + LevelTerminalEntry setItems(NBTTagList items) { + assert items.tagCount() == inv.getSizeInventory(); + + for (int i = 0; i < items.tagCount(); ++i) { + setItemInSlot(ItemStack.loadItemStackFromNBT(items.getCompoundTagAt(i)), i); + } + return this; + } + + public void partialItemUpdate(NBTTagList items, int[] validIndices) { + for (int i = 0; i < validIndices.length; ++i) { + setItemInSlot(ItemStack.loadItemStackFromNBT(items.getCompoundTagAt(i)), validIndices[i]); + } + } + + private void setItemInSlot(ItemStack stack, int idx) { + final int oldHasItem = inv.getStackInSlot(idx) != null ? 1 : 0; + final int newHasItem = stack != null ? 1 : 0; + + inv.setInventorySlotContents(idx, stack); + + // Update item count + numItems += newHasItem - oldHasItem; + assert numItems >= 0; + } + + public AppEngInternalInventory getInventory() { + return inv; + } + + public boolean mouseClicked(int mouseX, int mouseY, int mouseButton) { + final EntityPlayer player = Minecraft.getMinecraft().thePlayer; + + if (!section.visible || mouseButton < 0 || mouseButton > 2) { + return false; + } + if (mouseX >= highlightButton.xPosition && mouseX < 2 + highlightButton.width + && mouseY > Math.max(highlightButton.yPosition, LevelTerminalSection.TITLE_HEIGHT) + && mouseY <= Math.min(highlightButton.yPosition + highlightButton.height, viewHeight)) { + highlightButton.func_146113_a(mc.getSoundHandler()); + Util.DimensionalCoordSide blockPos = new Util.DimensionalCoordSide( + x, + y, + z, + dim, + ForgeDirection.getOrientation(side), + ""); + if (isCtrlKeyDown() && isShiftKeyDown() && hasInfinityBoosterCard(player)) { + FluidCraft.proxy.netHandler.sendToServer( + new CPacketLevelTerminalCommands( + Action.EDIT, + blockPos.x, + blockPos.y, + blockPos.z, + blockPos.getDimension(), + blockPos.getSide())); + } else if (isShiftKeyDown()) { + FluidCraft.proxy.netHandler.sendToServer( + new CPacketRenamer( + blockPos.x, + blockPos.y, + blockPos.z, + blockPos.getDimension(), + blockPos.getSide())); + } else { + /* View in world */ + WorldCoord blockPos2 = new WorldCoord((int) player.posX, (int) player.posY, (int) player.posZ); + if (mc.theWorld.provider.dimensionId != dim) { + player.addChatMessage( + new ChatComponentTranslation(PlayerMessages.InterfaceInOtherDim.getName(), dim)); + } else { + BlockPosHighlighter.highlightBlock( + blockPos, + System.currentTimeMillis() + 500 * WorldCoord.getTaxicabDistance(blockPos, blockPos2)); + player.addChatMessage( + new ChatComponentTranslation( + PlayerMessages.InterfaceHighlighted.getName(), + blockPos.x, + blockPos.y, + blockPos.z)); + } + player.closeScreen(); + } + return true; + } + + int offsetY = mouseY - dispY; + int offsetX = mouseX - (VIEW_WIDTH - rowSize * 18) - 1; + if (offsetX >= 0 && offsetX < (rowSize * 18) + && mouseY > Math.max(dispY, LevelTerminalSection.TITLE_HEIGHT) + && offsetY < Math.min(viewHeight - dispY, guiHeight)) { + final int col = offsetX / 18; + final int row = offsetY / 18; + final int slotIdx = row * rowSize + col; + + // send packet to server, request an update + InventoryAction action = switch (mouseButton) { + case 0 -> { + // pickup / set-down. + yield null; + } + case 1 -> { + yield null; + } + case 2 -> { + // creative dupe: + if (player.capabilities.isCreativeMode) { + yield InventoryAction.CREATIVE_DUPLICATE; + } else { + yield InventoryAction.AUTO_CRAFT; + } + } + default -> { + // drop item: + yield null; + } + }; + + if (action != null) { + ItemStack itemStack = getInventory().getStackInSlot(slotIdx); + if (itemStack.hasTagCompound() && itemStack.getTagCompound().hasKey(TLMTags.Stack.tagName)) { + long batch = itemStack.getTagCompound().getLong(TLMTags.Batch.tagName); + NBTTagCompound stackData = itemStack.getTagCompound().getCompoundTag(TLMTags.Stack.tagName); + ItemStack is = ItemStack.loadItemStackFromNBT(stackData); + IAEItemStack stack = AEItemStack.create(is); + + if (stack == null) return true; + stack.setStackSize(batch); + ((AEBaseContainer) inventorySlots).setTargetStack(stack); + FluidCraft.proxy.netHandler.sendToServer(new CPacketInventoryAction(action, slotIdx, 0, stack)); + } + } + + return true; + } + + return false; + } + } +} diff --git a/src/main/java/com/glodblock/github/client/gui/GuiLevelWireless.java b/src/main/java/com/glodblock/github/client/gui/GuiLevelWireless.java new file mode 100644 index 00000000..45b716b3 --- /dev/null +++ b/src/main/java/com/glodblock/github/client/gui/GuiLevelWireless.java @@ -0,0 +1,24 @@ +package com.glodblock.github.client.gui; + +import net.minecraft.entity.player.InventoryPlayer; + +import com.glodblock.github.client.gui.container.ContainerLevelWireless; +import com.glodblock.github.inventory.item.IWirelessTerminal; + +import appeng.container.slot.AppEngSlot; + +public class GuiLevelWireless extends GuiLevelTerminal { + + public GuiLevelWireless(final InventoryPlayer inventoryPlayer, final IWirelessTerminal te) { + super(inventoryPlayer, new ContainerLevelWireless(inventoryPlayer, te)); + } + + @Override + protected void repositionSlots() { + for (final Object obj : this.inventorySlots.inventorySlots) { + if (obj instanceof final AppEngSlot slot) { + slot.yDisplayPosition = this.ySize + slot.getY() - 78 - 4; + } + } + } +} diff --git a/src/main/java/com/glodblock/github/client/gui/base/FCBaseMEGui.java b/src/main/java/com/glodblock/github/client/gui/base/FCBaseMEGui.java index 7cf6ff2d..90f29f01 100644 --- a/src/main/java/com/glodblock/github/client/gui/base/FCBaseMEGui.java +++ b/src/main/java/com/glodblock/github/client/gui/base/FCBaseMEGui.java @@ -13,7 +13,8 @@ import com.glodblock.github.client.gui.GuiFluidCraftingWireless; import com.glodblock.github.client.gui.GuiFluidPatternExWireless; import com.glodblock.github.client.gui.GuiFluidPatternWireless; import com.glodblock.github.client.gui.GuiFluidPortableCell; -import com.glodblock.github.client.gui.GuiInterfaceTerminalWireless; +import com.glodblock.github.client.gui.GuiInterfaceWireless; +import com.glodblock.github.client.gui.GuiLevelWireless; import com.glodblock.github.client.gui.container.base.FCBaseContainer; import com.glodblock.github.common.item.ItemWirelessUltraTerminal; import com.glodblock.github.inventory.gui.GuiType; @@ -29,7 +30,8 @@ public abstract class FCBaseMEGui extends AEBaseMEGui { protected GuiFCImgButton PatternTerminal; protected GuiFCImgButton EssentiaTerminal; protected GuiFCImgButton InterfaceTerminal; - protected GuiFCImgButton PatternTerminaleEx; + protected GuiFCImgButton LevelTerminal; + protected GuiFCImgButton PatternTerminalEx; protected List termBtns = new ArrayList<>(); protected boolean drawSwitchGuiBtn; @@ -87,13 +89,13 @@ public abstract class FCBaseMEGui extends AEBaseMEGui { } if (!(this instanceof GuiFluidPatternExWireless)) { this.buttonList.add( - this.PatternTerminaleEx = new GuiFCImgButton( + this.PatternTerminalEx = new GuiFCImgButton( this.guiLeft - 18, this.getOffsetY(), "PATTERN_EX_TEM", "YES")); this.setOffsetY(this.getOffsetY() + 20); - termBtns.add(this.PatternTerminaleEx); + termBtns.add(this.PatternTerminalEx); } if (!(this instanceof GuiFluidPortableCell)) { this.buttonList.add( @@ -101,7 +103,7 @@ public abstract class FCBaseMEGui extends AEBaseMEGui { this.setOffsetY(this.getOffsetY() + 20); termBtns.add(this.FluidTerminal); } - if (!(this instanceof GuiInterfaceTerminalWireless)) { + if (!(this instanceof GuiInterfaceWireless)) { this.buttonList.add( this.InterfaceTerminal = new GuiFCImgButton( this.guiLeft - 18, @@ -111,6 +113,12 @@ public abstract class FCBaseMEGui extends AEBaseMEGui { this.setOffsetY(this.getOffsetY() + 20); termBtns.add(this.InterfaceTerminal); } + if (!(this instanceof GuiLevelWireless)) { + this.buttonList.add( + this.LevelTerminal = new GuiFCImgButton(this.guiLeft - 18, this.getOffsetY(), "LEVEL_TEM", "YES")); + this.setOffsetY(this.getOffsetY() + 20); + termBtns.add(this.LevelTerminal); + } if (ModAndClassUtil.ThE && !(this instanceof GuiEssentiaTerminal)) { this.buttonList.add( this.EssentiaTerminal = new GuiFCImgButton( @@ -123,6 +131,7 @@ public abstract class FCBaseMEGui extends AEBaseMEGui { } } + @SuppressWarnings("unchecked") protected void addSwitchGuiBtns() { if (!drawSwitchGuiBtn) return; this.buttonList.addAll(termBtns); @@ -141,7 +150,9 @@ public abstract class FCBaseMEGui extends AEBaseMEGui { ItemWirelessUltraTerminal.switchTerminal(this.mc.thePlayer, GuiType.WIRELESS_FLUID_PATTERN_TERMINAL); } else if (btn == this.InterfaceTerminal) { ItemWirelessUltraTerminal.switchTerminal(this.mc.thePlayer, GuiType.WIRELESS_INTERFACE_TERMINAL); - } else if (btn == this.PatternTerminaleEx) { + } else if (btn == this.LevelTerminal) { + ItemWirelessUltraTerminal.switchTerminal(this.mc.thePlayer, GuiType.WIRELESS_LEVEL_TERMINAL); + } else if (btn == this.PatternTerminalEx) { ItemWirelessUltraTerminal.switchTerminal(this.mc.thePlayer, GuiType.WIRELESS_FLUID_PATTERN_TERMINAL_EX); } } diff --git a/src/main/java/com/glodblock/github/client/gui/container/ContainerLevelTerminal.java b/src/main/java/com/glodblock/github/client/gui/container/ContainerLevelTerminal.java new file mode 100644 index 00000000..f3e98da5 --- /dev/null +++ b/src/main/java/com/glodblock/github/client/gui/container/ContainerLevelTerminal.java @@ -0,0 +1,406 @@ +package com.glodblock.github.client.gui.container; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.inventory.IInventory; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraftforge.common.util.ForgeDirection; + +import com.glodblock.github.FluidCraft; +import com.glodblock.github.api.registries.ILevelViewable; +import com.glodblock.github.client.gui.container.base.FCBaseContainer; +import com.glodblock.github.common.parts.PartLevelTerminal; +import com.glodblock.github.coremod.registries.LevelTerminalRegistry; +import com.glodblock.github.inventory.item.IWirelessTerminal; +import com.glodblock.github.network.SPacketLevelTerminalUpdate; +import com.google.common.primitives.Ints; + +import appeng.api.networking.IGrid; +import appeng.api.networking.IGridHost; +import appeng.api.networking.IGridNode; +import appeng.api.networking.security.IActionHost; +import appeng.api.storage.ITerminalHost; +import appeng.api.util.DimensionalCoord; +import appeng.helpers.InventoryAction; +import appeng.items.misc.ItemEncodedPattern; +import appeng.util.InventoryAdaptor; +import appeng.util.Platform; +import appeng.util.inv.AdaptorPlayerHand; +import appeng.util.inv.ItemSlot; + +public class ContainerLevelTerminal extends FCBaseContainer { + + /** + * this stuff is all server side + */ + private int nextId = 0; + + private final Map tracked = new HashMap<>(); + private final Map trackedById = new HashMap<>(); + + private IGrid grid; + private SPacketLevelTerminalUpdate dirty; + private boolean isDirty; + private boolean wasOff; + private int ticks = 0; + + public ContainerLevelTerminal(final InventoryPlayer ip, final ITerminalHost monitorable) { + super(ip, monitorable); + if (Platform.isServer()) { + if (monitorable instanceof PartLevelTerminal terminal) { + grid = terminal.getActionableNode().getGrid(); + } else if (monitorable instanceof IWirelessTerminal wirelessTerminal) { + grid = wirelessTerminal.getActionableNode().getGrid(); + } + dirty = this.updateList(); + if (dirty != null) { + FluidCraft.proxy.netHandler.sendTo(dirty, (EntityPlayerMP) ip.player); + this.isDirty = true; + } else { + dirty = new SPacketLevelTerminalUpdate(); + } + } + + this.bindPlayerInventory(ip, 14, 0); + } + + @Override + public void detectAndSendChanges() { + if (Platform.isClient()) { + return; + } + + super.detectAndSendChanges(); + + if (this.grid == null) { + return; + } + + final IActionHost host = this.getActionHost(); + + if (!host.getActionableNode().isActive()) { + if (!wasOff) { + SPacketLevelTerminalUpdate update = new SPacketLevelTerminalUpdate(); + + update.setDisconnect(); + wasOff = true; + FluidCraft.proxy.netHandler.sendTo(update, (EntityPlayerMP) this.getPlayerInv().player); + } + return; + } + wasOff = false; + + if (isDirty) { + FluidCraft.proxy.netHandler.sendTo(dirty, (EntityPlayerMP) this.getPlayerInv().player); + dirty = new SPacketLevelTerminalUpdate(); + isDirty = false; + } else if (++ticks % 20 == 0) { + ticks = 0; + SPacketLevelTerminalUpdate update = this.updateList(); + if (update != null) { + FluidCraft.proxy.netHandler.sendTo(update, (EntityPlayerMP) this.getPlayerInv().player); + } + } + } + + /** + * Merge from slot -> player inv. Returns the items not added. + */ + private ItemStack mergeToPlayerInventory(InventoryAdaptor playerInv, ItemStack stack) { + if (stack == null) return null; + for (ItemSlot slot : playerInv) { + if (Platform.isSameItemPrecise(slot.getItemStack(), stack)) { + if (slot.getItemStack().stackSize < slot.getItemStack().getMaxStackSize()) { + ++slot.getItemStack().stackSize; + return null; + } + } + } + return playerInv.addItems(stack); + } + + @Override + public void doAction(final EntityPlayerMP player, final InventoryAction action, final int slotIdx, final long id) { + final ContainerLevelTerminal.InvTracker invTracker = this.trackedById.get(id); + if (invTracker != null) { + final ItemStack handStack = player.inventory.getItemStack(); + + if (handStack != null && !(handStack.getItem() instanceof ItemEncodedPattern)) { + // Why even bother if we're not dealing with an encoded pattern in hand + return; + } + + final ItemStack slotStack = invTracker.requests.getStackInSlot(slotIdx); + final InventoryAdaptor playerHand = new AdaptorPlayerHand(player); + + switch (action) { + /* Set down/pickup. This is the same as SPLIT_OR_PLACE_SINGLE as our max stack sizes are 1 in slots. */ + case PICKUP_OR_SET_DOWN -> { + if (handStack != null) { + for (int s = 0; s < invTracker.requests.getSizeInventory(); s++) { + /* Is there a duplicate pattern here? */ + if (Platform.isSameItemPrecise(invTracker.requests.getStackInSlot(s), handStack)) { + /* We're done here - dupe found. */ + return; + } + } + } + + if (slotStack == null) { + /* Insert to container, if valid */ + if (handStack == null) { + /* Nothing happens */ + return; + } + invTracker.requests.setInventorySlotContents(slotIdx, playerHand.removeItems(1, null, null)); + } else { + /* Exchange? */ + if (handStack != null && handStack.stackSize > 1) { + /* Exchange is impossible, abort */ + return; + } + invTracker.requests.setInventorySlotContents(slotIdx, playerHand.removeItems(1, null, null)); + playerHand.addItems(slotStack.copy()); + } + syncLevelTerminalSlot(invTracker, id, slotIdx, invTracker.requests.getStackInSlot(slotIdx)); + } + /* Shift click from slotIdx -> player. Player -> slotIdx is not supported. */ + case SHIFT_CLICK -> { + InventoryAdaptor playerInv = InventoryAdaptor.getAdaptor(player.inventory, ForgeDirection.UNKNOWN); + ItemStack leftOver = mergeToPlayerInventory(playerInv, slotStack); + + if (leftOver == null) { + invTracker.requests.setInventorySlotContents(slotIdx, null); + syncLevelTerminalSlot(invTracker, id, slotIdx, null); + } + } + /* Move all blank patterns -> player */ + case MOVE_REGION -> { + final InventoryAdaptor playerInv = InventoryAdaptor.getAdaptor(player, ForgeDirection.UNKNOWN); + List valid = new ArrayList<>(); + + for (int i = 0; i < invTracker.requests.getSizeInventory(); i++) { + ItemStack toExtract = invTracker.requests.getStackInSlot(i); + + if (toExtract == null) { + continue; + } + + ItemStack leftOver = mergeToPlayerInventory(playerInv, toExtract); + + if (leftOver != null) { + break; + } else { + invTracker.requests.setInventorySlotContents(i, null); + } + valid.add(i); + } + if (valid.size() > 0) { + int[] validIndices = Ints.toArray(valid); + NBTTagList tag = new NBTTagList(); + for (int i = 0; i < valid.size(); ++i) { + tag.appendTag(new NBTTagCompound()); + } + dirty.addOverwriteEntry(id).setItems(validIndices, tag); + isDirty = true; + } + } + case CREATIVE_DUPLICATE -> { + if (player.capabilities.isCreativeMode) { + playerHand.addItems(handStack); + } + } + default -> { + return; + } + } + + this.updateHeld(player); + } + } + + private void syncLevelTerminalSlot(ContainerLevelTerminal.InvTracker inv, long id, int slot, ItemStack stack) { + int[] validIndices = { slot }; + NBTTagList list = new NBTTagList(); + NBTTagCompound item = new NBTTagCompound(); + + if (stack != null) { + stack.writeToNBT(item); + } + list.appendTag(item); + inv.updateNBT(); + this.dirty.addOverwriteEntry(id).setItems(validIndices, list); + this.isDirty = true; + } + + private SPacketLevelTerminalUpdate updateList() { + SPacketLevelTerminalUpdate update = null; + var supported = LevelTerminalRegistry.instance().getSupportedClasses(); + Set visited = new HashSet<>(); + + for (Class clz : supported) { + boolean isAdopted = LevelTerminalRegistry.instance().isAdopted(clz); + Class machineClass = isAdopted ? LevelTerminalRegistry.instance().getAdopted(clz) + : clz; + for (IGridNode gridNode : grid.getMachines(machineClass)) { + final ILevelViewable machine = isAdopted + ? LevelTerminalRegistry.instance().getAdapter(clz).adapt(gridNode.getMachine()) + : (ILevelViewable) gridNode.getMachine(); + + /* First check if we are already tracking this node */ + if (tracked.containsKey(machine)) { + /* Check for updates */ + ContainerLevelTerminal.InvTracker knownTracker = tracked.get(machine); + + /* Name changed? */ + String name = machine.getCustomName(); + + if (!Objects.equals(knownTracker.name, name)) { + if (update == null) update = new SPacketLevelTerminalUpdate(); + update.addRenamedEntry(knownTracker.id, name); + knownTracker.name = name; + } + + /* Status changed? */ + boolean isActive = gridNode.isActive() || machine.shouldDisplay(); + + if (!knownTracker.online && isActive) { + /* Node offline -> online */ + knownTracker.online = true; + if (update == null) update = new SPacketLevelTerminalUpdate(); + knownTracker.updateNBT(); + update.addOverwriteEntry(knownTracker.id).setOnline(true) + .setItems(knownTracker.requests.getSizeInventory(), knownTracker.inventoryNbt); + } else if (knownTracker.online && !isActive) { + /* Node online -> offline */ + knownTracker.online = false; + if (update == null) update = new SPacketLevelTerminalUpdate(); + update.addOverwriteEntry(knownTracker.id).setOnline(false); + } + + for (int i = 0; i < knownTracker.requests.getSizeInventory(); i++) { + ItemStack knowmItemStack = ItemStack + .loadItemStackFromNBT(knownTracker.inventoryNbt.getCompoundTagAt(i)); + ItemStack machineItemStack = machine.getInventoryByName("config").getStackInSlot(i); + if (isDifferent(knowmItemStack, machineItemStack)) { + if (update == null) update = new SPacketLevelTerminalUpdate(); + knownTracker.updateNBT(); + update.addOverwriteEntry(knownTracker.id) + .setItems(knownTracker.requests.getSizeInventory(), knownTracker.inventoryNbt); + } + } + } else { + /* Add a new entry */ + if (update == null) update = new SPacketLevelTerminalUpdate(); + ContainerLevelTerminal.InvTracker entry = new ContainerLevelTerminal.InvTracker( + nextId++, + machine, + gridNode.isActive()); + update.addNewEntry(entry.id, entry.name, entry.online) + .setLocation(entry.x, entry.y, entry.z, entry.dim, entry.side.ordinal()) + .setItems(entry.rows, entry.rowSize, entry.inventoryNbt) + .setViewItemStack(machine.getSelfItemStack(), machine.getDisplayItemStack()); + tracked.put(machine, entry); + trackedById.put(entry.id, entry); + } + visited.add(machine); + } + } + + /* Now find any entries that we need to remove */ + Iterator> it = tracked.entrySet().iterator(); + while (it.hasNext()) { + var entry = it.next(); + if (visited.contains(entry.getKey())) { + continue; + } + + if (update == null) update = new SPacketLevelTerminalUpdate(); + + trackedById.remove(entry.getValue().id); + it.remove(); + update.addRemovalEntry(entry.getValue().id); + } + return update; + } + + @Override + protected boolean isWirelessTerminal() { + return false; + } + + private boolean isDifferent(final ItemStack a, final ItemStack b) { + if (a == null && b == null) { + return false; + } + + if (a == null || b == null) { + return true; + } + + return !ItemStack.areItemStacksEqual(a, b); + } + + private static class InvTracker { + + private final long id; + private String name; + private final IInventory requests; + private final int rowSize; + private final int rows; + private final int x; + private final int y; + private final int z; + private final int dim; + private final ForgeDirection side; + private boolean online; + private NBTTagList inventoryNbt; + + public InvTracker(long id, ILevelViewable machine, boolean online) { + + DimensionalCoord location = machine.getLocation(); + + this.id = id; + this.name = machine.getCustomName(); + this.requests = machine.getInventoryByName("config"); + this.rowSize = machine.rowSize(); + this.rows = machine.rows(); + this.x = location.x; + this.y = location.y; + this.z = location.z; + this.dim = location.getDimension(); + this.side = machine.getSide(); + this.online = online; + this.inventoryNbt = new NBTTagList(); + updateNBT(); + } + + private void updateNBT() { + this.inventoryNbt = new NBTTagList(); + for (int i = 0; i < this.rows; ++i) { + for (int j = 0; j < this.rowSize; ++j) { + final int offset = this.rowSize * i; + ItemStack stack = this.requests.getStackInSlot(offset + j); + + if (stack != null) { + this.inventoryNbt.appendTag(stack.writeToNBT(new NBTTagCompound())); + } else { + this.inventoryNbt.appendTag(new NBTTagCompound()); + } + } + } + } + + } +} diff --git a/src/main/java/com/glodblock/github/client/gui/container/ContainerLevelWireless.java b/src/main/java/com/glodblock/github/client/gui/container/ContainerLevelWireless.java new file mode 100644 index 00000000..286b9921 --- /dev/null +++ b/src/main/java/com/glodblock/github/client/gui/container/ContainerLevelWireless.java @@ -0,0 +1,17 @@ +package com.glodblock.github.client.gui.container; + +import net.minecraft.entity.player.InventoryPlayer; + +import com.glodblock.github.inventory.item.IWirelessTerminal; + +public class ContainerLevelWireless extends ContainerLevelTerminal { + + public ContainerLevelWireless(InventoryPlayer ip, IWirelessTerminal monitorable) { + super(ip, monitorable); + } + + @Override + protected boolean isWirelessTerminal() { + return true; + } +} diff --git a/src/main/java/com/glodblock/github/client/textures/FCPartsTexture.java b/src/main/java/com/glodblock/github/client/textures/FCPartsTexture.java index 5d19f19a..daf003f1 100644 --- a/src/main/java/com/glodblock/github/client/textures/FCPartsTexture.java +++ b/src/main/java/com/glodblock/github/client/textures/FCPartsTexture.java @@ -18,6 +18,9 @@ public enum FCPartsTexture { PartFluidPatternTerminal_Bright("pattern_terminal_bright"), PartFluidPatternTerminal_Dark("pattern_terminal_dark"), PartFluidPatternTerminal_Colored("pattern_terminal_medium"), + PartLevelTerminal_Bright("level_terminal_bright"), + PartLevelTerminal_Dark("level_terminal_dark"), + PartLevelTerminal_Colored("level_terminal_medium"), PartTerminalBroad("terminal_broad"), PartFluidImportBus("fluid_import_face"), PartFluidExportBus("fluid_export_face"), diff --git a/src/main/java/com/glodblock/github/common/item/ItemPartLevelTerminal.java b/src/main/java/com/glodblock/github/common/item/ItemPartLevelTerminal.java new file mode 100644 index 00000000..f951d862 --- /dev/null +++ b/src/main/java/com/glodblock/github/common/item/ItemPartLevelTerminal.java @@ -0,0 +1,71 @@ +package com.glodblock.github.common.item; + +import static net.minecraft.client.gui.GuiScreen.isShiftKeyDown; + +import java.util.List; + +import javax.annotation.Nullable; + +import net.minecraft.client.renderer.texture.IIconRegister; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.world.World; + +import com.glodblock.github.FluidCraft; +import com.glodblock.github.common.parts.PartLevelTerminal; +import com.glodblock.github.common.tabs.FluidCraftingTabs; +import com.glodblock.github.util.NameConst; + +import appeng.api.AEApi; +import appeng.api.parts.IPartItem; +import cpw.mods.fml.common.registry.GameRegistry; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; + +public class ItemPartLevelTerminal extends FCBaseItem implements IPartItem { + + public ItemPartLevelTerminal() { + this.setMaxStackSize(64); + this.setUnlocalizedName(NameConst.ITEM_PART_LEVEL_TERMINAL); + AEApi.instance().partHelper().setItemBusRenderer(this); + } + + @Nullable + @Override + public PartLevelTerminal createPartFromItemStack(ItemStack is) { + return new PartLevelTerminal(is); + } + + @Override + public boolean onItemUse(ItemStack stack, EntityPlayer player, World world, int x, int y, int z, int side, + float xOffset, float yOffset, float zOffset) { + return AEApi.instance().partHelper().placeBus(player.getHeldItem(), x, y, z, side, player, world); + } + + @Override + @SuppressWarnings("unchecked") + @SideOnly(Side.CLIENT) + public void addInformation(ItemStack stack, EntityPlayer player, List toolTip, boolean flag) { + if (isShiftKeyDown()) { + toolTip.add(NameConst.i18n(NameConst.TT_LEVEL_TERMINAL)); + } else { + toolTip.add(NameConst.i18n(NameConst.TT_SHIFT_FOR_MORE)); + } + } + + @Override + public ItemPartLevelTerminal register() { + GameRegistry.registerItem(this, NameConst.ITEM_PART_LEVEL_TERMINAL, FluidCraft.MODID); + setCreativeTab(FluidCraftingTabs.INSTANCE); + return this; + } + + @Override + public void registerIcons(IIconRegister _iconRegister) {} + + @Override + @SideOnly(Side.CLIENT) + public int getSpriteNumber() { + return 0; + } +} diff --git a/src/main/java/com/glodblock/github/common/item/ItemWirelessLevelTerminal.java b/src/main/java/com/glodblock/github/common/item/ItemWirelessLevelTerminal.java new file mode 100644 index 00000000..a762a143 --- /dev/null +++ b/src/main/java/com/glodblock/github/common/item/ItemWirelessLevelTerminal.java @@ -0,0 +1,51 @@ +package com.glodblock.github.common.item; + +import java.util.EnumSet; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.world.World; + +import com.glodblock.github.FluidCraft; +import com.glodblock.github.common.tabs.FluidCraftingTabs; +import com.glodblock.github.inventory.gui.GuiType; +import com.glodblock.github.inventory.item.WirelessLevelTerminalInventory; +import com.glodblock.github.loader.IRegister; +import com.glodblock.github.util.NameConst; +import com.glodblock.github.util.Util; + +import appeng.api.AEApi; +import appeng.api.networking.IGridNode; +import appeng.core.features.AEFeature; +import appeng.core.localization.PlayerMessages; +import cpw.mods.fml.common.registry.GameRegistry; + +public class ItemWirelessLevelTerminal extends ItemBaseWirelessTerminal + implements IRegister { + + public ItemWirelessLevelTerminal() { + super(GuiType.WIRELESS_LEVEL_TERMINAL); + AEApi.instance().registries().wireless().registerWirelessHandler(this); + this.setFeature(EnumSet.of(AEFeature.WirelessAccessTerminal, AEFeature.PoweredTools)); + setUnlocalizedName(NameConst.ITEM_WIRELESS_LEVEL_TERMINAL); + setTextureName(FluidCraft.resource(NameConst.ITEM_WIRELESS_LEVEL_TERMINAL).toString()); + } + + @Override + public ItemWirelessLevelTerminal register() { + GameRegistry.registerItem(this, NameConst.ITEM_WIRELESS_LEVEL_TERMINAL, FluidCraft.MODID); + setCreativeTab(FluidCraftingTabs.INSTANCE); + return this; + } + + @Override + public Object getInventory(ItemStack stack, World world, int x, int y, int z, EntityPlayer player) { + try { + IGridNode gridNode = Util.getWirelessGrid(stack); + return new WirelessLevelTerminalInventory(stack, x, gridNode, player); + } catch (Exception e) { + player.addChatMessage(PlayerMessages.OutOfRange.get()); + } + return null; + } +} diff --git a/src/main/java/com/glodblock/github/common/item/ItemWirelessUltraTerminal.java b/src/main/java/com/glodblock/github/common/item/ItemWirelessUltraTerminal.java index 51807e85..93979954 100644 --- a/src/main/java/com/glodblock/github/common/item/ItemWirelessUltraTerminal.java +++ b/src/main/java/com/glodblock/github/common/item/ItemWirelessUltraTerminal.java @@ -24,6 +24,7 @@ import com.glodblock.github.inventory.gui.GuiType; import com.glodblock.github.inventory.item.WirelessCraftingTerminalInventory; import com.glodblock.github.inventory.item.WirelessFluidTerminalInventory; import com.glodblock.github.inventory.item.WirelessInterfaceTerminalInventory; +import com.glodblock.github.inventory.item.WirelessLevelTerminalInventory; import com.glodblock.github.inventory.item.WirelessPatternTerminalExInventory; import com.glodblock.github.inventory.item.WirelessPatternTerminalInventory; import com.glodblock.github.loader.IRegister; @@ -63,6 +64,7 @@ public class ItemWirelessUltraTerminal extends ItemBaseWirelessTerminal guis.add(GuiType.WIRELESS_FLUID_PATTERN_TERMINAL_EX); guis.add(GuiType.WIRELESS_FLUID_TERMINAL); guis.add(GuiType.WIRELESS_INTERFACE_TERMINAL); + guis.add(GuiType.WIRELESS_LEVEL_TERMINAL); if (ModAndClassUtil.ThE) { guis.add(GuiType.WIRELESS_ESSENTIA_TERMINAL); } @@ -115,6 +117,8 @@ public class ItemWirelessUltraTerminal extends ItemBaseWirelessTerminal return new WirelessFluidTerminalInventory(stack, x, gridNode, player); } else if (gui == GuiType.WIRELESS_INTERFACE_TERMINAL) { return new WirelessInterfaceTerminalInventory(stack, x, gridNode, player); + } else if (gui == GuiType.WIRELESS_LEVEL_TERMINAL) { + return new WirelessLevelTerminalInventory(stack, x, gridNode, player); } else if (gui == GuiType.WIRELESS_FLUID_PATTERN_TERMINAL_EX) { return new WirelessPatternTerminalExInventory(stack, x, gridNode, player); } else { @@ -197,6 +201,15 @@ public class ItemWirelessUltraTerminal extends ItemBaseWirelessTerminal } } + public static boolean hasInfinityBoosterCard(EntityPlayer player) { + ImmutablePair term = Util.getUltraWirelessTerm(player); + if (term == null) return false; + if (term.getRight().getItem() instanceof ItemWirelessUltraTerminal) { + return Util.hasInfinityBoosterCard(term.getRight()); + } + return false; + } + public static List getGuis() { return guis; } diff --git a/src/main/java/com/glodblock/github/common/parts/PartLevelTerminal.java b/src/main/java/com/glodblock/github/common/parts/PartLevelTerminal.java new file mode 100644 index 00000000..7df1d991 --- /dev/null +++ b/src/main/java/com/glodblock/github/common/parts/PartLevelTerminal.java @@ -0,0 +1,83 @@ +package com.glodblock.github.common.parts; + +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; + +import com.glodblock.github.client.textures.FCPartsTexture; +import com.glodblock.github.common.parts.base.FCPart; +import com.glodblock.github.inventory.gui.GuiType; +import com.glodblock.github.inventory.item.IClickableInTerminal; +import com.glodblock.github.util.Util; + +public class PartLevelTerminal extends FCPart implements IClickableInTerminal { + + private static final FCPartsTexture FRONT_BRIGHT_ICON = FCPartsTexture.PartLevelTerminal_Bright; + private static final FCPartsTexture FRONT_DARK_ICON = FCPartsTexture.PartLevelTerminal_Colored; + private static final FCPartsTexture FRONT_COLORED_ICON = FCPartsTexture.PartLevelTerminal_Dark; + + private Util.DimensionalCoordSide tile; + + public PartLevelTerminal(ItemStack is) { + super(is, true); + } + + @Override + public void readFromNBT(final NBTTagCompound data) { + super.readFromNBT(data); + if (data.hasKey("clickedInterface")) { + NBTTagCompound tileMsg = (NBTTagCompound) data.getTag("clickedInterface"); + this.tile = Util.DimensionalCoordSide.readFromNBT(tileMsg); + } + } + + @Override + public void writeToNBT(final NBTTagCompound data) { + super.writeToNBT(data); + if (this.tile == null) return; + + NBTTagCompound tileMsg = new NBTTagCompound(); + tile.writeToNBT(tileMsg); + data.setTag("clickedInterface", tileMsg); + } + + @Override + public FCPartsTexture getFrontBright() { + return FRONT_BRIGHT_ICON; + } + + @Override + public FCPartsTexture getFrontColored() { + return FRONT_COLORED_ICON; + } + + @Override + public FCPartsTexture getFrontDark() { + return FRONT_DARK_ICON; + } + + @Override + public boolean isLightSource() { + return false; + } + + @Override + public boolean isBooting() { + return super.isBooting(); + } + + @Override + public GuiType getGui() { + return GuiType.LEVEL_TERMINAL; + } + + @Override + public void setClickedInterface(Util.DimensionalCoordSide tile) { + this.tile = tile; + this.getHost().markForSave(); + } + + @Override + public Util.DimensionalCoordSide getClickedInterface() { + return this.tile; + } +} diff --git a/src/main/java/com/glodblock/github/inventory/gui/GuiType.java b/src/main/java/com/glodblock/github/inventory/gui/GuiType.java index a89d0f73..45bd8593 100644 --- a/src/main/java/com/glodblock/github/inventory/gui/GuiType.java +++ b/src/main/java/com/glodblock/github/inventory/gui/GuiType.java @@ -6,6 +6,7 @@ import javax.annotation.Nullable; import net.minecraft.entity.player.EntityPlayer; +import com.glodblock.github.client.gui.GuiCraftingStatus; import com.glodblock.github.client.gui.GuiDualInterface; import com.glodblock.github.client.gui.GuiEssentiaTerminal; import com.glodblock.github.client.gui.GuiFCPriority; @@ -20,16 +21,17 @@ import com.glodblock.github.client.gui.GuiFluidPacketDecoder; import com.glodblock.github.client.gui.GuiFluidPatternEncoder; import com.glodblock.github.client.gui.GuiFluidPatternExWireless; import com.glodblock.github.client.gui.GuiFluidPatternTerminal; -import com.glodblock.github.client.gui.GuiFluidPatternTerminalCraftingStatus; import com.glodblock.github.client.gui.GuiFluidPatternTerminalEx; import com.glodblock.github.client.gui.GuiFluidPatternWireless; import com.glodblock.github.client.gui.GuiFluidPortableCell; import com.glodblock.github.client.gui.GuiFluidStorageBus; import com.glodblock.github.client.gui.GuiFluidTerminal; import com.glodblock.github.client.gui.GuiIngredientBuffer; -import com.glodblock.github.client.gui.GuiInterfaceTerminalWireless; +import com.glodblock.github.client.gui.GuiInterfaceWireless; import com.glodblock.github.client.gui.GuiLargeIngredientBuffer; import com.glodblock.github.client.gui.GuiLevelMaintainer; +import com.glodblock.github.client.gui.GuiLevelTerminal; +import com.glodblock.github.client.gui.GuiLevelWireless; import com.glodblock.github.client.gui.GuiOCPatternEditor; import com.glodblock.github.client.gui.GuiPatternValueAmount; import com.glodblock.github.client.gui.GuiRenamer; @@ -54,6 +56,8 @@ import com.glodblock.github.client.gui.container.ContainerIngredientBuffer; import com.glodblock.github.client.gui.container.ContainerInterfaceWireless; import com.glodblock.github.client.gui.container.ContainerLargeIngredientBuffer; import com.glodblock.github.client.gui.container.ContainerLevelMaintainer; +import com.glodblock.github.client.gui.container.ContainerLevelTerminal; +import com.glodblock.github.client.gui.container.ContainerLevelWireless; import com.glodblock.github.client.gui.container.ContainerOCPatternEditor; import com.glodblock.github.client.gui.container.ContainerPatternValueAmount; import com.glodblock.github.client.gui.container.ContainerRenamer; @@ -246,7 +250,7 @@ public enum GuiType { @Override protected Object createClientGui(EntityPlayer player, ITerminalHost inv) { - return new GuiFluidPatternTerminalCraftingStatus(player.inventory, inv); + return new GuiCraftingStatus(player.inventory, inv); } }), @@ -263,6 +267,19 @@ public enum GuiType { } }), + LEVEL_TERMINAL(new TileOrPartGuiFactory<>(ITerminalHost.class) { + + @Override + protected Object createServerGui(EntityPlayer player, ITerminalHost inv) { + return new ContainerLevelTerminal(player.inventory, inv); + } + + @Override + protected Object createClientGui(EntityPlayer player, ITerminalHost inv) { + return new GuiLevelTerminal(player.inventory, inv); + } + }), + PORTABLE_FLUID_CELL(new ItemGuiFactory<>(IFluidPortableCell.class) { @Override @@ -335,7 +352,20 @@ public enum GuiType { @Override protected Object createClientGui(EntityPlayer player, IWirelessTerminal inv) { - return new GuiInterfaceTerminalWireless(player.inventory, inv); + return new GuiInterfaceWireless(player.inventory, inv); + } + }), + + WIRELESS_LEVEL_TERMINAL(new ItemGuiFactory<>(IWirelessTerminal.class) { + + @Override + protected Object createServerGui(EntityPlayer player, IWirelessTerminal inv) { + return new ContainerLevelWireless(player.inventory, inv); + } + + @Override + protected Object createClientGui(EntityPlayer player, IWirelessTerminal inv) { + return new GuiLevelWireless(player.inventory, inv); } }), diff --git a/src/main/java/com/glodblock/github/inventory/item/WirelessInterfaceTerminalInventory.java b/src/main/java/com/glodblock/github/inventory/item/WirelessInterfaceTerminalInventory.java index 4ee0a9ba..d3bee52b 100644 --- a/src/main/java/com/glodblock/github/inventory/item/WirelessInterfaceTerminalInventory.java +++ b/src/main/java/com/glodblock/github/inventory/item/WirelessInterfaceTerminalInventory.java @@ -27,7 +27,7 @@ import appeng.tile.inventory.InvOperation; import appeng.util.ConfigManager; import appeng.util.Platform; -public class WirelessInterfaceTerminalInventory implements IWirelessInterfaceTerminal { +public class WirelessInterfaceTerminalInventory implements IClickableInTerminal, IWirelessTerminal { private final ItemStack target; private final IAEItemPowerStorage ips; @@ -125,9 +125,7 @@ public class WirelessInterfaceTerminalInventory implements IWirelessInterfaceTer } @Override - public void saveChanges() { - - } + public void saveChanges() {} @Override public void onChangeInventory(IInventory inv, int slot, InvOperation mc, ItemStack removedStack, diff --git a/src/main/java/com/glodblock/github/inventory/item/WirelessLevelTerminalInventory.java b/src/main/java/com/glodblock/github/inventory/item/WirelessLevelTerminalInventory.java new file mode 100644 index 00000000..8150d6a1 --- /dev/null +++ b/src/main/java/com/glodblock/github/inventory/item/WirelessLevelTerminalInventory.java @@ -0,0 +1,146 @@ +package com.glodblock.github.inventory.item; + +import java.util.Objects; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.IInventory; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraftforge.common.util.ForgeDirection; + +import com.glodblock.github.util.Util; + +import appeng.api.config.Actionable; +import appeng.api.config.PowerMultiplier; +import appeng.api.config.Settings; +import appeng.api.config.TerminalStyle; +import appeng.api.implementations.items.IAEItemPowerStorage; +import appeng.api.networking.IGridNode; +import appeng.api.storage.IMEMonitor; +import appeng.api.storage.StorageChannel; +import appeng.api.storage.data.IAEFluidStack; +import appeng.api.storage.data.IAEItemStack; +import appeng.api.util.AECableType; +import appeng.api.util.IConfigManager; +import appeng.items.tools.powered.ToolWirelessTerminal; +import appeng.tile.inventory.InvOperation; +import appeng.util.ConfigManager; +import appeng.util.Platform; + +public class WirelessLevelTerminalInventory implements IClickableInTerminal, IWirelessTerminal { + + private final ItemStack target; + private final IAEItemPowerStorage ips; + private final int inventorySlot; + private final IGridNode grid; + private Util.DimensionalCoordSide tile; + + public WirelessLevelTerminalInventory(ItemStack is, int slot, IGridNode gridNode, EntityPlayer player) { + Objects.requireNonNull(Util.getWirelessInv(is, player, StorageChannel.ITEMS)); + this.ips = (ToolWirelessTerminal) is.getItem(); + this.grid = gridNode; + this.target = is; + this.inventorySlot = slot; + readFromNBT(); + } + + public void readFromNBT() { + NBTTagCompound data = Platform.openNbtData(this.target); + if (data.hasKey("clickedInterface")) { + NBTTagCompound tileMsg = (NBTTagCompound) data.getTag("clickedInterface"); + this.tile = Util.DimensionalCoordSide.readFromNBT(tileMsg); + } + } + + public void writeToNBT() { + NBTTagCompound data = Platform.openNbtData(this.target); + NBTTagCompound tileMsg = new NBTTagCompound(); + tile.writeToNBT(tileMsg); + data.setTag("clickedInterface", tileMsg); + } + + @Override + public IGridNode getActionableNode() { + return this.grid; + } + + @Override + public IGridNode getGridNode(ForgeDirection dir) { + return this.grid; + } + + @Override + public AECableType getCableConnectionType(ForgeDirection dir) { + return AECableType.NONE; + } + + @Override + public void securityBreak() { + this.getGridNode(ForgeDirection.UNKNOWN).getMachine().securityBreak(); + } + + @Override + public ItemStack getItemStack() { + return this.target; + } + + @Override + public IInventory getViewCellStorage() { + return null; + } + + @Override + public double extractAEPower(double amt, Actionable mode, PowerMultiplier usePowerMultiplier) { + amt = usePowerMultiplier.multiply(amt); + if (mode == Actionable.SIMULATE) { + return usePowerMultiplier.divide(Math.min(amt, this.ips.getAECurrentPower(this.target))); + } + return usePowerMultiplier.divide(this.ips.extractAEPower(this.target, amt)); + } + + @Override + public IMEMonitor getItemInventory() { + return null; + } + + @Override + public IMEMonitor getFluidInventory() { + return null; + } + + @Override + public IConfigManager getConfigManager() { + final ConfigManager out = new ConfigManager((manager, settingName, newValue) -> { + final NBTTagCompound data = Platform.openNbtData(this.target); + manager.writeToNBT(data); + }); + out.registerSetting(Settings.TERMINAL_STYLE, TerminalStyle.SMALL); + out.readFromNBT((NBTTagCompound) Platform.openNbtData(this.target).copy()); + return out; + } + + @Override + public int getInventorySlot() { + return this.inventorySlot; + } + + @Override + public void saveChanges() {} + + @Override + public void onChangeInventory(IInventory inv, int slot, InvOperation mc, ItemStack removedStack, + ItemStack newStack) { + + } + + @Override + public void setClickedInterface(Util.DimensionalCoordSide tile) { + this.tile = tile; + this.writeToNBT(); + } + + @Override + public Util.DimensionalCoordSide getClickedInterface() { + return this.tile; + } +} diff --git a/src/main/java/com/glodblock/github/loader/ChannelLoader.java b/src/main/java/com/glodblock/github/loader/ChannelLoader.java index e1d78dbf..f7d1560d 100644 --- a/src/main/java/com/glodblock/github/loader/ChannelLoader.java +++ b/src/main/java/com/glodblock/github/loader/ChannelLoader.java @@ -12,17 +12,20 @@ import com.glodblock.github.network.CPacketFluidPatternTermBtns; import com.glodblock.github.network.CPacketFluidUpdate; import com.glodblock.github.network.CPacketInventoryAction; import com.glodblock.github.network.CPacketLevelMaintainer; +import com.glodblock.github.network.CPacketLevelTerminalCommands; import com.glodblock.github.network.CPacketPatternValueSet; import com.glodblock.github.network.CPacketRenamer; import com.glodblock.github.network.CPacketSwitchGuis; import com.glodblock.github.network.CPacketTransferRecipe; import com.glodblock.github.network.CPacketValueConfig; import com.glodblock.github.network.SPacketFluidUpdate; +import com.glodblock.github.network.SPacketLevelTerminalUpdate; import com.glodblock.github.network.SPacketMEFluidInvUpdate; import com.glodblock.github.network.SPacketMEItemInvUpdate; import com.glodblock.github.network.SPacketSetItemAmount; import com.glodblock.github.network.SPacketStringUpdate; +import cpw.mods.fml.common.network.simpleimpl.SimpleNetworkWrapper; import cpw.mods.fml.relauncher.Side; public class ChannelLoader implements Runnable { @@ -33,48 +36,47 @@ public class ChannelLoader implements Runnable { @SuppressWarnings("all") public void run() { int id = 0; - FluidCraft.proxy.netHandler - .registerMessage(new CPacketSwitchGuis.Handler(), CPacketSwitchGuis.class, id++, Side.SERVER); - FluidCraft.proxy.netHandler.registerMessage( + SimpleNetworkWrapper netHandler = FluidCraft.proxy.netHandler; + netHandler.registerMessage(new CPacketSwitchGuis.Handler(), CPacketSwitchGuis.class, id++, Side.SERVER); + netHandler.registerMessage( new CPacketFluidPatternTermBtns.Handler(), CPacketFluidPatternTermBtns.class, id++, Side.SERVER); - FluidCraft.proxy.netHandler - .registerMessage(new CPacketEncodePattern.Handler(), CPacketEncodePattern.class, id++, Side.SERVER); - FluidCraft.proxy.netHandler + netHandler.registerMessage(new CPacketEncodePattern.Handler(), CPacketEncodePattern.class, id++, Side.SERVER); + netHandler .registerMessage(new SPacketMEItemInvUpdate.Handler(), SPacketMEItemInvUpdate.class, id++, Side.CLIENT); - FluidCraft.proxy.netHandler.registerMessage( + netHandler.registerMessage( new SPacketMEFluidInvUpdate.Handler(), SPacketMEFluidInvUpdate.class, id++, Side.CLIENT); - FluidCraft.proxy.netHandler - .registerMessage(new CPacketCraftRequest.Handler(), CPacketCraftRequest.class, id++, Side.SERVER); - FluidCraft.proxy.netHandler + netHandler.registerMessage(new CPacketCraftRequest.Handler(), CPacketCraftRequest.class, id++, Side.SERVER); + netHandler .registerMessage(new CPacketInventoryAction.Handler(), CPacketInventoryAction.class, id++, Side.SERVER); - FluidCraft.proxy.netHandler - .registerMessage(new CPacketSwitchGuis.Handler(), CPacketSwitchGuis.class, id++, Side.CLIENT); - FluidCraft.proxy.netHandler - .registerMessage(new CPacketTransferRecipe.Handler(), CPacketTransferRecipe.class, id++, Side.SERVER); - FluidCraft.proxy.netHandler - .registerMessage(new CPacketDumpTank.Handler(), CPacketDumpTank.class, id++, Side.SERVER); - FluidCraft.proxy.netHandler - .registerMessage(new SPacketFluidUpdate.Handler(), SPacketFluidUpdate.class, id++, Side.CLIENT); - FluidCraft.proxy.netHandler + netHandler.registerMessage(new CPacketSwitchGuis.Handler(), CPacketSwitchGuis.class, id++, Side.CLIENT); + netHandler.registerMessage(new CPacketTransferRecipe.Handler(), CPacketTransferRecipe.class, id++, Side.SERVER); + netHandler.registerMessage(new CPacketDumpTank.Handler(), CPacketDumpTank.class, id++, Side.SERVER); + netHandler.registerMessage(new SPacketFluidUpdate.Handler(), SPacketFluidUpdate.class, id++, Side.CLIENT); + netHandler .registerMessage(new CPacketPatternValueSet.Handler(), CPacketPatternValueSet.class, id++, Side.SERVER); - FluidCraft.proxy.netHandler - .registerMessage(new CPacketValueConfig.Handler(), CPacketValueConfig.class, id++, Side.SERVER); - FluidCraft.proxy.netHandler - .registerMessage(new CPacketFluidUpdate.Handler(), CPacketFluidUpdate.class, id++, Side.SERVER); - FluidCraft.proxy.netHandler + netHandler.registerMessage(new CPacketValueConfig.Handler(), CPacketValueConfig.class, id++, Side.SERVER); + netHandler.registerMessage(new CPacketFluidUpdate.Handler(), CPacketFluidUpdate.class, id++, Side.SERVER); + netHandler .registerMessage(new CPacketLevelMaintainer.Handler(), CPacketLevelMaintainer.class, id++, Side.SERVER); - FluidCraft.proxy.netHandler - .registerMessage(new SPacketSetItemAmount.Handler(), SPacketSetItemAmount.class, id++, Side.CLIENT); - FluidCraft.proxy.netHandler - .registerMessage(new CPacketRenamer.Handler(), CPacketRenamer.class, id++, Side.SERVER); - FluidCraft.proxy.netHandler - .registerMessage(new SPacketStringUpdate.Handler(), SPacketStringUpdate.class, id++, Side.CLIENT); + netHandler.registerMessage(new SPacketSetItemAmount.Handler(), SPacketSetItemAmount.class, id++, Side.CLIENT); + netHandler.registerMessage(new CPacketRenamer.Handler(), CPacketRenamer.class, id++, Side.SERVER); + netHandler.registerMessage(new SPacketStringUpdate.Handler(), SPacketStringUpdate.class, id++, Side.CLIENT); + netHandler.registerMessage( + new SPacketLevelTerminalUpdate.Handler(), + SPacketLevelTerminalUpdate.class, + id++, + Side.CLIENT); + netHandler.registerMessage( + new CPacketLevelTerminalCommands.Handler(), + CPacketLevelTerminalCommands.class, + id++, + Side.CLIENT); } public static void sendPacketToAllPlayers(Packet packet, World world) { diff --git a/src/main/java/com/glodblock/github/loader/ItemAndBlockHolder.java b/src/main/java/com/glodblock/github/loader/ItemAndBlockHolder.java index 5b6e2b99..5aa5ab89 100644 --- a/src/main/java/com/glodblock/github/loader/ItemAndBlockHolder.java +++ b/src/main/java/com/glodblock/github/loader/ItemAndBlockHolder.java @@ -35,9 +35,11 @@ import com.glodblock.github.common.item.ItemPartFluidPatternTerminal; import com.glodblock.github.common.item.ItemPartFluidPatternTerminalEx; import com.glodblock.github.common.item.ItemPartFluidStorageBus; import com.glodblock.github.common.item.ItemPartFluidTerminal; +import com.glodblock.github.common.item.ItemPartLevelTerminal; import com.glodblock.github.common.item.ItemPortableFluidCell; import com.glodblock.github.common.item.ItemWirelessFluidTerminal; import com.glodblock.github.common.item.ItemWirelessInterfaceTerminal; +import com.glodblock.github.common.item.ItemWirelessLevelTerminal; import com.glodblock.github.common.item.ItemWirelessPatternTerminal; import com.glodblock.github.common.item.ItemWirelessUltraTerminal; import com.glodblock.github.common.storage.CellType; @@ -64,6 +66,7 @@ public class ItemAndBlockHolder { public static ItemPartFluidPatternTerminalEx FLUID_TERMINAL_EX = new ItemPartFluidPatternTerminalEx().register(); public static ItemPartFluidInterface FLUID_INTERFACE = new ItemPartFluidInterface().register(); public static ItemPartFluidP2PInterface FLUID_INTERFACE_P2P = new ItemPartFluidP2PInterface().register(); + public static ItemPartLevelTerminal LEVEL_TERMINAL = new ItemPartLevelTerminal().register(); public static ItemFluidImportBus FLUID_IMPORT_BUS = new ItemFluidImportBus().register(); public static ItemFluidExportBus FLUID_EXPORT_BUS = new ItemFluidExportBus().register(); public static ItemPartFluidStorageBus FLUID_STORAGE_BUS = new ItemPartFluidStorageBus().register(); @@ -72,8 +75,9 @@ public class ItemAndBlockHolder { public static ItemFluidConversionMonitor FLUID_CONVERSION_MONITOR = new ItemFluidConversionMonitor().register(); public static ItemPortableFluidCell PORTABLE_FLUID_CELL = new ItemPortableFluidCell().register(); public static ItemWirelessFluidTerminal WIRELESS_FLUID_TERM = new ItemWirelessFluidTerminal().register(); - public static ItemWirelessUltraTerminal WIRELESS_ULTRA_TERM = new ItemWirelessUltraTerminal().register(); + public static ItemWirelessLevelTerminal WIRELESS_LEVEL_TERM = new ItemWirelessLevelTerminal().register(); public static ItemWirelessPatternTerminal WIRELESS_PATTERN_TERM = new ItemWirelessPatternTerminal().register(); + public static ItemWirelessUltraTerminal WIRELESS_ULTRA_TERM = new ItemWirelessUltraTerminal().register(); public static ItemWirelessInterfaceTerminal WIRELESS_INTERFACE_TERM = new ItemWirelessInterfaceTerminal() .register(); public static ItemFluidPacket PACKET = new ItemFluidPacket().register(); diff --git a/src/main/java/com/glodblock/github/loader/RecipeLoader.java b/src/main/java/com/glodblock/github/loader/RecipeLoader.java index b8f3774d..878a3bf8 100644 --- a/src/main/java/com/glodblock/github/loader/RecipeLoader.java +++ b/src/main/java/com/glodblock/github/loader/RecipeLoader.java @@ -1,7 +1,56 @@ package com.glodblock.github.loader; import static com.glodblock.github.common.storage.CellType.Cell16384kPart; -import static com.glodblock.github.loader.ItemAndBlockHolder.*; +import static com.glodblock.github.loader.ItemAndBlockHolder.BUFFER; +import static com.glodblock.github.loader.ItemAndBlockHolder.CELL1024K; +import static com.glodblock.github.loader.ItemAndBlockHolder.CELL1024KM; +import static com.glodblock.github.loader.ItemAndBlockHolder.CELL16384K; +import static com.glodblock.github.loader.ItemAndBlockHolder.CELL16384KM; +import static com.glodblock.github.loader.ItemAndBlockHolder.CELL16K; +import static com.glodblock.github.loader.ItemAndBlockHolder.CELL16KM; +import static com.glodblock.github.loader.ItemAndBlockHolder.CELL1K; +import static com.glodblock.github.loader.ItemAndBlockHolder.CELL1KM; +import static com.glodblock.github.loader.ItemAndBlockHolder.CELL256K; +import static com.glodblock.github.loader.ItemAndBlockHolder.CELL256KM; +import static com.glodblock.github.loader.ItemAndBlockHolder.CELL4096K; +import static com.glodblock.github.loader.ItemAndBlockHolder.CELL4096KM; +import static com.glodblock.github.loader.ItemAndBlockHolder.CELL4K; +import static com.glodblock.github.loader.ItemAndBlockHolder.CELL4KM; +import static com.glodblock.github.loader.ItemAndBlockHolder.CELL64K; +import static com.glodblock.github.loader.ItemAndBlockHolder.CELL64KM; +import static com.glodblock.github.loader.ItemAndBlockHolder.CELL_HOUSING; +import static com.glodblock.github.loader.ItemAndBlockHolder.CERTUS_QUARTZ_TANK; +import static com.glodblock.github.loader.ItemAndBlockHolder.DECODER; +import static com.glodblock.github.loader.ItemAndBlockHolder.DISCRETIZER; +import static com.glodblock.github.loader.ItemAndBlockHolder.ENCODER; +import static com.glodblock.github.loader.ItemAndBlockHolder.FLUID_AUTO_FILLER; +import static com.glodblock.github.loader.ItemAndBlockHolder.FLUID_BUFFER; +import static com.glodblock.github.loader.ItemAndBlockHolder.FLUID_CONVERSION_MONITOR; +import static com.glodblock.github.loader.ItemAndBlockHolder.FLUID_EXPORT_BUS; +import static com.glodblock.github.loader.ItemAndBlockHolder.FLUID_IMPORT_BUS; +import static com.glodblock.github.loader.ItemAndBlockHolder.FLUID_INTERFACE; +import static com.glodblock.github.loader.ItemAndBlockHolder.FLUID_INTERFACE_P2P; +import static com.glodblock.github.loader.ItemAndBlockHolder.FLUID_LEVEL_EMITTER; +import static com.glodblock.github.loader.ItemAndBlockHolder.FLUID_STORAGE_BUS; +import static com.glodblock.github.loader.ItemAndBlockHolder.FLUID_STORAGE_MONITOR; +import static com.glodblock.github.loader.ItemAndBlockHolder.FLUID_TERM; +import static com.glodblock.github.loader.ItemAndBlockHolder.FLUID_TERMINAL; +import static com.glodblock.github.loader.ItemAndBlockHolder.FLUID_TERMINAL_EX; +import static com.glodblock.github.loader.ItemAndBlockHolder.INTERFACE; +import static com.glodblock.github.loader.ItemAndBlockHolder.LARGE_BUFFER; +import static com.glodblock.github.loader.ItemAndBlockHolder.LEVEL_MAINTAINER; +import static com.glodblock.github.loader.ItemAndBlockHolder.LEVEL_TERMINAL; +import static com.glodblock.github.loader.ItemAndBlockHolder.OC_EDITOR; +import static com.glodblock.github.loader.ItemAndBlockHolder.PATTERN; +import static com.glodblock.github.loader.ItemAndBlockHolder.PORTABLE_FLUID_CELL; +import static com.glodblock.github.loader.ItemAndBlockHolder.QUANTUM_CELL; +import static com.glodblock.github.loader.ItemAndBlockHolder.SINGULARITY_CELL; +import static com.glodblock.github.loader.ItemAndBlockHolder.WALRUS; +import static com.glodblock.github.loader.ItemAndBlockHolder.WIRELESS_FLUID_TERM; +import static com.glodblock.github.loader.ItemAndBlockHolder.WIRELESS_INTERFACE_TERM; +import static com.glodblock.github.loader.ItemAndBlockHolder.WIRELESS_LEVEL_TERM; +import static com.glodblock.github.loader.ItemAndBlockHolder.WIRELESS_PATTERN_TERM; +import static com.glodblock.github.loader.ItemAndBlockHolder.WIRELESS_ULTRA_TERM; import static net.minecraft.init.Blocks.redstone_torch; import static net.minecraft.init.Items.fish; @@ -156,6 +205,7 @@ public class RecipeLoader implements Runnable { GameRegistry.addShapelessRecipe(WIRELESS_FLUID_TERM.stack(), AE2_WIRELESS_TERMINAL, BUCKET); GameRegistry.addShapelessRecipe(WIRELESS_PATTERN_TERM.stack(), WIRELESS_FLUID_TERM, FLUID_TERMINAL); GameRegistry.addShapelessRecipe(WIRELESS_INTERFACE_TERM.stack(), AE2_WIRELESS_TERMINAL, AE2_INTERFACE_TERMINAL); + GameRegistry.addShapelessRecipe(WIRELESS_LEVEL_TERM.stack(), AE2_WIRELESS_TERMINAL, LEVEL_MAINTAINER); GameRegistry .addRecipe(new ShapedOreRecipe(CERTUS_QUARTZ_TANK.stack(), "GGG", "G G", "GGG", 'G', AE2_QUARTZ_GLASS)); GameRegistry.addShapelessRecipe(FLUID_AUTO_FILLER.stack(), FLUID_TERMINAL, ENCODER); @@ -276,6 +326,7 @@ public class RecipeLoader implements Runnable { AE2_PROCESS_ENG)); GameRegistry.addShapelessRecipe(AE2_BLANK_PATTERN, PATTERN.stack()); GameRegistry.addShapelessRecipe(FLUID_TERM.stack(), AE2_TERMINAL, BUFFER); + GameRegistry.addShapelessRecipe(LEVEL_TERMINAL.stack(), AE2_TERMINAL, LEVEL_MAINTAINER); GameRegistry.addShapelessRecipe( FLUID_BUFFER.stack(), LARGE_BUFFER.stack(), @@ -594,10 +645,11 @@ public class RecipeLoader implements Runnable { GameRegistry.addRecipe(new WirelessTerminalRecipe(WIRELESS_FLUID_TERM.stack())); GameRegistry.addRecipe(new WirelessTerminalRecipe(WIRELESS_ULTRA_TERM.stack())); GameRegistry.addRecipe(new WirelessTerminalRecipe(WIRELESS_INTERFACE_TERM.stack())); + GameRegistry.addRecipe(new WirelessTerminalRecipe(WIRELESS_LEVEL_TERM.stack())); } ItemStack[] term = { AE2_WIRELESS_TERMINAL, WIRELESS_FLUID_TERM.stack(), WIRELESS_PATTERN_TERM.stack(), - WIRELESS_INTERFACE_TERM.stack(), THE_WIRELESS_TERM, WCT_WIRELESS_TERM }; + WIRELESS_INTERFACE_TERM.stack(), WIRELESS_LEVEL_TERM.stack(), THE_WIRELESS_TERM, WCT_WIRELESS_TERM }; GameRegistry.addShapelessRecipe( WIRELESS_ULTRA_TERM.stack(), Arrays.stream(term).filter(Objects::nonNull).toArray()); diff --git a/src/main/java/com/glodblock/github/network/CPacketLevelTerminalCommands.java b/src/main/java/com/glodblock/github/network/CPacketLevelTerminalCommands.java new file mode 100644 index 00000000..b6e61b9b --- /dev/null +++ b/src/main/java/com/glodblock/github/network/CPacketLevelTerminalCommands.java @@ -0,0 +1,134 @@ +package com.glodblock.github.network; + +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.tileentity.TileEntity; +import net.minecraftforge.common.DimensionManager; +import net.minecraftforge.common.util.ForgeDirection; + +import com.glodblock.github.inventory.InventoryHandler; +import com.glodblock.github.inventory.gui.GuiType; +import com.glodblock.github.inventory.item.IClickableInTerminal; +import com.glodblock.github.util.BlockPos; +import com.glodblock.github.util.Util; + +import appeng.container.AEBaseContainer; +import cpw.mods.fml.common.network.simpleimpl.IMessage; +import cpw.mods.fml.common.network.simpleimpl.IMessageHandler; +import cpw.mods.fml.common.network.simpleimpl.MessageContext; +import io.netty.buffer.ByteBuf; + +public class CPacketLevelTerminalCommands implements IMessage { + + private Action action; + private int x; + private int y; + private int z; + private int dim; + private ForgeDirection side; + + public enum Action { + EDIT, + ENABLE, + DISABLE, + ENABLE_ALL, + DISABLE_ALL, + } + + public CPacketLevelTerminalCommands() {} + + public CPacketLevelTerminalCommands(Action action, int x, int y, int z, int dim, ForgeDirection side) { + this.action = action; + this.x = x; + this.y = y; + this.z = z; + this.dim = dim; + this.side = side; + } + + @Override + public void fromBytes(ByteBuf buf) { + action = Action.values()[buf.readInt()]; + switch (action) { + case EDIT -> { + x = buf.readInt(); + y = buf.readInt(); + z = buf.readInt(); + dim = buf.readInt(); + side = ForgeDirection.getOrientation(buf.readInt()); + } + case ENABLE -> {} + case DISABLE -> {} + case ENABLE_ALL -> {} + case DISABLE_ALL -> {} + } + } + + @Override + public void toBytes(ByteBuf buf) { + buf.writeInt(action.ordinal()); + switch (action) { + case EDIT -> { + buf.writeInt(this.x); + buf.writeInt(this.y); + buf.writeInt(this.z); + buf.writeInt(this.dim); + buf.writeInt(side.ordinal()); + } + case ENABLE -> {} + case DISABLE -> {} + case ENABLE_ALL -> {} + case DISABLE_ALL -> {} + } + } + + public static class Handler implements IMessageHandler { + + @Override + public IMessage onMessage(CPacketLevelTerminalCommands message, MessageContext ctx) { + EntityPlayerMP player = ctx.getServerHandler().playerEntity; + AEBaseContainer con = (AEBaseContainer) player.openContainer; + switch (message.action) { + case EDIT -> { + TileEntity tile = DimensionManager.getWorld(message.dim) + .getTileEntity(message.x, message.y, message.z); + InventoryHandler.openGui( + player, + player.worldObj, + new BlockPos(tile), + message.side, + GuiType.LEVEL_MAINTAINER); + } + case ENABLE -> { + if (con.getTarget() instanceof IClickableInTerminal clickableInterface) { + Util.DimensionalCoordSide intMsg = clickableInterface.getClickedInterface(); + TileEntity tile = DimensionManager.getWorld(intMsg.getDimension()) + .getTileEntity(intMsg.x, intMsg.y, intMsg.z); + } + } + case DISABLE -> { + if (con.getTarget() instanceof IClickableInTerminal clickableInterface) { + Util.DimensionalCoordSide intMsg = clickableInterface.getClickedInterface(); + TileEntity tile = DimensionManager.getWorld(intMsg.getDimension()) + .getTileEntity(intMsg.x, intMsg.y, intMsg.z); + } + } + case ENABLE_ALL -> { + if (con.getTarget() instanceof IClickableInTerminal clickableInterface) { + Util.DimensionalCoordSide intMsg = clickableInterface.getClickedInterface(); + TileEntity tile = DimensionManager.getWorld(intMsg.getDimension()) + .getTileEntity(intMsg.x, intMsg.y, intMsg.z); + } + } + case DISABLE_ALL -> { + if (con.getTarget() instanceof IClickableInTerminal clickableInterface) { + Util.DimensionalCoordSide intMsg = clickableInterface.getClickedInterface(); + TileEntity tile = DimensionManager.getWorld(intMsg.getDimension()) + .getTileEntity(intMsg.x, intMsg.y, intMsg.z); + } + } + } + return null; + } + + } +} diff --git a/src/main/java/com/glodblock/github/network/SPacketLevelTerminalUpdate.java b/src/main/java/com/glodblock/github/network/SPacketLevelTerminalUpdate.java new file mode 100644 index 00000000..7e49efa4 --- /dev/null +++ b/src/main/java/com/glodblock/github/network/SPacketLevelTerminalUpdate.java @@ -0,0 +1,450 @@ +package com.glodblock.github.network; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import javax.annotation.Nullable; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.CompressedStreamTools; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraftforge.common.util.Constants.NBT; + +import com.glodblock.github.client.gui.GuiLevelTerminal; + +import appeng.core.AELog; +import cpw.mods.fml.common.network.ByteBufUtils; +import cpw.mods.fml.common.network.simpleimpl.IMessage; +import cpw.mods.fml.common.network.simpleimpl.IMessageHandler; +import cpw.mods.fml.common.network.simpleimpl.MessageContext; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufInputStream; +import io.netty.buffer.ByteBufOutputStream; +import io.netty.buffer.Unpooled; + +/** + * Packet used for level terminal updates. Packet allows the server to send an array of command packets, which are then + * processed in order. This allows for chaining commands to produce the desired update. + */ +public class SPacketLevelTerminalUpdate implements IMessage { + + public static final int CLEAR_ALL_BIT = 1; + public static final int DISCONNECT_BIT = 2; + + private final List commands = new ArrayList<>(); + private int statusFlags; + + public SPacketLevelTerminalUpdate() {} + + public void fromBytes(ByteBuf buf) { + this.statusFlags = buf.readByte(); + int numEntries = buf.readInt(); + + for (int i = 0; i < numEntries; ++i) { + PacketType type = PacketType.values()[buf.readByte()]; + + try { + switch (type) { + case ADD -> this.commands.add(new PacketAdd(buf)); + case REMOVE -> this.commands.add(new PacketRemove(buf)); + case OVERWRITE -> this.commands.add(new PacketOverwrite(buf)); + case RENAME -> this.commands.add(new PacketRename(buf)); + default -> throw new IOException("Unknown packet type received of index " + type); + } + } catch (Exception e) { + AELog.debug(e); + return; + } + } + } + + public void toBytes(ByteBuf buf) { + try { + buf.writeByte(statusFlags); + buf.writeInt(commands.size()); + for (PacketEntry entry : commands) { + entry.write(buf); + } + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + /** + * Remove all entries on the terminal. This is done BEFORE any entries are processed, so you can set this to clear + * old entries, and add new ones after in one packet. + */ + public void setClear() { + statusFlags |= CLEAR_ALL_BIT; + } + + /** + * The terminal disconnected. Entries are still processed, but indicates for the GUI to darken/turn off the + * terminal. No further updates will arrive until the terminal reconnects. + */ + public void setDisconnect() { + this.statusFlags |= DISCONNECT_BIT; + } + + /** + * Adds a new entry. Fill out the rest of the command using the {@link PacketAdd#setItems(int, int, NBTTagList)} and + * {@link PacketAdd#setLocation(int, int, int, int, int)}. + * + * @return the packet, which needs to have information filled out. + */ + public PacketAdd addNewEntry(long id, String name, boolean online) { + PacketAdd packet = new PacketAdd(id, name, online); + + commands.add(packet); + return packet; + } + + /** + * Remove the entry with the id from the terminal. + */ + public void addRemovalEntry(long id) { + commands.add(new PacketRemove(id)); + } + + /** + * Rename the entry + */ + public void addRenamedEntry(long id, String newName) { + commands.add(new PacketRename(id, newName)); + } + + /** + * Overwrite the entry with new items or new status + */ + public PacketOverwrite addOverwriteEntry(long id) { + PacketOverwrite packet = new PacketOverwrite(id); + + commands.add(packet); + return packet; + } + + public static class Handler implements IMessageHandler { + + @Nullable + @Override + public IMessage onMessage(SPacketLevelTerminalUpdate message, MessageContext ctx) { + final GuiScreen gs = Minecraft.getMinecraft().currentScreen; + + if (gs instanceof GuiLevelTerminal levelTerminal) { + levelTerminal.postUpdate(message.commands, message.statusFlags); + } + + return null; + } + } + + enum PacketType { + ADD, + REMOVE, + OVERWRITE, + RENAME, + } + + /** + * A packet for updating an entry. + */ + public abstract static class PacketEntry { + + public final long entryId; + + protected PacketEntry(long entryId) { + this.entryId = entryId; + } + + protected PacketEntry(ByteBuf buf) throws IOException { + this.entryId = buf.readLong(); + read(buf); + } + + /** + * Needs to write the packet id. + */ + protected abstract void write(ByteBuf buf) throws IOException; + + /** + * Reading the entry id is not needed. + */ + protected abstract void read(ByteBuf buf) throws IOException; + } + + /** + * A command for sending a new entry. + */ + public static class PacketAdd extends PacketEntry { + + public String name; + public int x, y, z, dim, side; + public int rows, rowSize; + public boolean online; + public ItemStack selfItemStack; + public ItemStack displayItemStack; + public NBTTagList items; + + PacketAdd(long id, String name, boolean online) { + super(id); + this.name = name; + this.online = online; + } + + PacketAdd(ByteBuf buf) throws IOException { + super(buf); + } + + public PacketAdd setLocation(int x, int y, int z, int dim, int side) { + this.x = x; + this.y = y; + this.z = z; + this.dim = dim; + this.side = side; + return this; + } + + public PacketAdd setItems(int rows, int rowSize, NBTTagList items) { + this.rows = rows; + this.rowSize = rowSize; + this.items = items; + + return this; + } + + public PacketAdd setViewItemStack(ItemStack selfItemStack, ItemStack displayItemStack) { + this.selfItemStack = selfItemStack; + this.displayItemStack = displayItemStack; + + return this; + } + + @Override + protected void write(ByteBuf buf) throws IOException { + buf.writeByte(PacketType.ADD.ordinal()); + buf.writeLong(entryId); + ByteBufUtils.writeUTF8String(buf, this.name); + buf.writeInt(x); + buf.writeInt(y); + buf.writeInt(z); + buf.writeInt(dim); + buf.writeInt(side); + buf.writeInt(rows); + buf.writeInt(rowSize); + + ByteBuf tempBuf = Unpooled.directBuffer(256); + try { + try (ByteBufOutputStream stream = new ByteBufOutputStream(tempBuf)) { + + NBTTagCompound wrapper = new NBTTagCompound(); + + if (selfItemStack != null) { + wrapper.setTag("self", selfItemStack.writeToNBT(new NBTTagCompound())); + } + if (displayItemStack != null) { + wrapper.setTag("display", displayItemStack.writeToNBT(new NBTTagCompound())); + } + wrapper.setTag("data", items); + CompressedStreamTools.writeCompressed(wrapper, stream); + } + buf.writeInt(tempBuf.readableBytes()); + buf.writeBytes(tempBuf); + } finally { + tempBuf.release(); + } + } + + @Override + protected void read(ByteBuf buf) throws IOException { + this.name = ByteBufUtils.readUTF8String(buf); + this.x = buf.readInt(); + this.y = buf.readInt(); + this.z = buf.readInt(); + this.dim = buf.readInt(); + this.side = buf.readInt(); + this.rows = buf.readInt(); + this.rowSize = buf.readInt(); + int payloadSize = buf.readInt(); + try (ByteBufInputStream stream = new ByteBufInputStream(buf, payloadSize)) { + NBTTagCompound payload = CompressedStreamTools.readCompressed(stream); + if (payload.hasKey("self", NBT.TAG_COMPOUND)) { + this.selfItemStack = ItemStack.loadItemStackFromNBT(payload.getCompoundTag("self")); + } + + if (payload.hasKey("display", NBT.TAG_COMPOUND)) { + this.displayItemStack = ItemStack.loadItemStackFromNBT(payload.getCompoundTag("display")); + } + + this.items = payload.getTagList("data", NBT.TAG_COMPOUND); + } + } + } + + public static class PacketRemove extends PacketEntry { + + PacketRemove(long id) { + super(id); + } + + PacketRemove(ByteBuf buf) throws IOException { + super(buf); + } + + @Override + protected void write(ByteBuf buf) { + buf.writeByte(PacketType.REMOVE.ordinal()); + buf.writeLong(entryId); + } + + @Override + protected void read(ByteBuf buf) {} + } + + /** + * Overwrite online status or inventory of the entry. + */ + public static class PacketOverwrite extends PacketEntry { + + public static final int ONLINE_BIT = 1; + public static final int ONLINE_VALID = 1 << 1; + public static final int ITEMS_VALID = 1 << 2; + public static final int ALL_ITEM_UPDATE_BIT = 1 << 3; + public boolean onlineValid; + public boolean online; + public boolean itemsValid; + public boolean allItemUpdate; + public int[] validIndices; + public NBTTagList items; + + protected PacketOverwrite(long id) { + super(id); + } + + protected PacketOverwrite(ByteBuf buf) throws IOException { + super(buf); + } + + public PacketOverwrite setOnline(boolean online) { + this.onlineValid = true; + this.online = online; + return this; + } + + public PacketOverwrite setItems(int[] validIndices, NBTTagList items) { + this.itemsValid = true; + this.allItemUpdate = validIndices == null || validIndices.length == 0; + this.validIndices = validIndices; + this.items = items; + + return this; + } + + public PacketOverwrite setItems(int newSize, NBTTagList items) { + this.itemsValid = true; + this.allItemUpdate = true; + this.validIndices = new int[newSize]; + this.items = items; + + return this; + } + + @Override + protected void write(ByteBuf buf) throws IOException { + buf.writeByte(PacketType.OVERWRITE.ordinal()); + buf.writeLong(entryId); + + int flags = 0; + + if (onlineValid) { + flags |= ONLINE_VALID; + flags |= online ? ONLINE_BIT : 0; + } + if (itemsValid) { + flags |= ITEMS_VALID; + if (allItemUpdate) { + flags |= ALL_ITEM_UPDATE_BIT; + buf.writeByte(flags); + buf.writeInt(validIndices.length); + } else { + buf.writeByte(flags); + buf.writeInt(validIndices.length); + for (int validIndex : validIndices) { + buf.writeInt(validIndex); + } + } + try (ByteBufOutputStream stream = new ByteBufOutputStream(buf)) { + + NBTTagCompound wrapper = new NBTTagCompound(); + + wrapper.setTag("data", items); + CompressedStreamTools.writeCompressed(wrapper, stream); + } + } else { + buf.writeByte(flags); + } + } + + @Override + protected void read(ByteBuf buf) throws IOException { + int flags = buf.readByte(); + + /* Decide whether to use online flag or not */ + if ((flags & ONLINE_VALID) == ONLINE_VALID) { + this.onlineValid = true; + this.online = (flags & ONLINE_BIT) == ONLINE_BIT; + } + /* Decide whether to read item list or not */ + if ((flags & ITEMS_VALID) == ITEMS_VALID) { + this.itemsValid = true; + if ((flags & ALL_ITEM_UPDATE_BIT) == ALL_ITEM_UPDATE_BIT) { + this.allItemUpdate = true; + int numItems = buf.readInt(); + this.validIndices = new int[numItems]; + } else { + int numItems = buf.readInt(); + this.validIndices = new int[numItems]; + for (int i = 0; i < numItems; ++i) { + this.validIndices[i] = buf.readInt(); + } + } + + try (ByteBufInputStream stream = new ByteBufInputStream(buf)) { + this.items = CompressedStreamTools.readCompressed(stream).getTagList("data", NBT.TAG_COMPOUND); + } + } + } + } + + /** + * Rename the entry. + */ + public static class PacketRename extends PacketEntry { + + public String newName; + + protected PacketRename(long id, String newName) { + super(id); + this.newName = newName; + } + + protected PacketRename(ByteBuf buf) throws IOException { + super(buf); + } + + @Override + protected void write(ByteBuf buf) { + buf.writeByte(PacketType.RENAME.ordinal()); + buf.writeLong(entryId); + ByteBufUtils.writeUTF8String(buf, newName); + } + + @Override + protected void read(ByteBuf buf) { + newName = ByteBufUtils.readUTF8String(buf); + } + } +} diff --git a/src/main/java/com/glodblock/github/util/FCGuiColors.java b/src/main/java/com/glodblock/github/util/FCGuiColors.java new file mode 100644 index 00000000..140aa097 --- /dev/null +++ b/src/main/java/com/glodblock/github/util/FCGuiColors.java @@ -0,0 +1,56 @@ +/* + * This file is part of Applied Energistics 2. Copyright (c) 2013 - 2014, 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 . + */ + +package com.glodblock.github.util; + +import net.minecraft.util.StatCollector; + +import appeng.core.AELog; + +public enum FCGuiColors { + + // ARGB Colors: Name and default value + StateNone(0x00000000), + StateIdle(0xFF55FF55), + StateCraft(0xFFFFFF55), + StateExport(0xFFAA00AA), + StateError(0xFFFF5555); + + private final String root; + private final int color; + + FCGuiColors() { + this.root = "ae2fc.gui.color"; + this.color = 0x000000; + } + + FCGuiColors(final int hex) { + this.root = "ae2fc.gui.color"; + this.color = hex; + } + + public int getColor() { + String hex = StatCollector.translateToLocal(this.getUnlocalized()); + int color = this.color; + + if (hex.length() <= 8) { + try { + color = Integer.parseUnsignedInt(hex, 16); + } catch (final NumberFormatException e) { + AELog.warn("Couldn't format color correctly for: " + this.root + " -> " + hex); + } + } + return color; + } + + public String getUnlocalized() { + return this.root + '.' + this.toString(); + } +} diff --git a/src/main/java/com/glodblock/github/util/NameConst.java b/src/main/java/com/glodblock/github/util/NameConst.java index 350f651d..f5644bfa 100644 --- a/src/main/java/com/glodblock/github/util/NameConst.java +++ b/src/main/java/com/glodblock/github/util/NameConst.java @@ -26,6 +26,7 @@ public class NameConst { public static final String ITEM_PART_FLUID_STORAGE_MONITOR = "part_fluid_storage_monitor"; public static final String ITEM_PART_FLUID_CONVERSION_MONITOR = "part_fluid_conversion_monitor"; public static final String ITEM_PART_FLUID_TERMINAL = "part_fluid_terminal"; + public static final String ITEM_PART_LEVEL_TERMINAL = "part_level_terminal"; public static final String ITEM_PART_FLUID_PATTERN_TERMINAL = "part_fluid_pattern_terminal"; public static final String ITEM_PART_FLUID_PATTERN_TERMINAL_EX = "part_fluid_pattern_terminal_ex"; public static final String ITEM_PART_FLUID_INTERFACE = "part_fluid_interface"; @@ -43,6 +44,7 @@ public class NameConst { public static final String ITEM_FLUID_PORTABLE_CELL = "portable_fluid_cell"; public static final String ITEM_WIRELESS_FLUID_TERMINAL = "wireless_fluid_terminal"; public static final String ITEM_WIRELESS_INTERFACE_TERMINAL = "wireless_interface_terminal"; + public static final String ITEM_WIRELESS_LEVEL_TERMINAL = "wireless_level_terminal"; public static final String ITEM_WIRELESS_ULTRA_TERMINAL = "wireless_ultra_terminal"; public static final String ITEM_WIRELESS_FLUID_PATTERN_TERMINAL = "wireless_fluid_pattern_terminal"; public static final String ITEM_FLUID_PART = "fluid_part"; @@ -50,6 +52,7 @@ public class NameConst { public static final String TT_KEY = FluidCraft.MODID + ".tooltip."; public static final String TT_FLUID_TERMINAL = TT_KEY + "fluid_terminal"; + public static final String TT_LEVEL_TERMINAL = TT_KEY + "level_terminal"; public static final String TT_INVALID_FLUID = TT_KEY + "invalid_fluid"; public static final String TT_FLUID_PACKET = TT_KEY + "fluid_packet"; public static final String TT_ENCODE_PATTERN = TT_KEY + "encode_pattern"; @@ -79,6 +82,8 @@ public class NameConst { public static final String TT_LEVEL_MAINTAINER_LINK_DESC = TT_LEVEL_MAINTAINER + "link_desc"; public static final String TT_LEVEL_MAINTAINER_EXPORT = TT_LEVEL_MAINTAINER + "export"; public static final String TT_LEVEL_MAINTAINER_EXPORT_DESC = TT_LEVEL_MAINTAINER + "export_desc"; + public static final String TT_LEVEL_MAINTAINER_ERROR = TT_LEVEL_MAINTAINER + "error"; + public static final String TT_LEVEL_MAINTAINER_ERROR_DESC = TT_LEVEL_MAINTAINER + "error_desc"; public static final String TT_CELL_CONTENTS = TT_KEY + "cell_contents"; public static final String TT_CELL_EMPTY = TT_KEY + "cell_empty"; public static final String TT_CELL_PORTABLE = TT_KEY + "cell_portable"; @@ -101,6 +106,7 @@ public class NameConst { public static final String GUI_FLUID_PATTERN_TERMINAL = GUI_KEY + ITEM_PART_FLUID_PATTERN_TERMINAL; public static final String GUI_FLUID_PATTERN_TERMINAL_EX = GUI_KEY + ITEM_PART_FLUID_PATTERN_TERMINAL_EX; public static final String GUI_LEVEL_MAINTAINER = GUI_KEY + BLOCK_LEVEL_MAINTAINER; + public static final String GUI_LEVEL_TERMINAL = GUI_KEY + ITEM_PART_LEVEL_TERMINAL; public static final String GUI_FLUID_PATTERN_ENCODER = GUI_KEY + BLOCK_FLUID_PATTERN_ENCODER; public static final String GUI_FLUID_PACKET_DECODER = GUI_KEY + BLOCK_FLUID_PACKET_DECODER; public static final String GUI_FLUID_AUTO_FILLER = GUI_KEY + BLOCK_FLUID_AUTO_FILLER; diff --git a/src/main/resources/assets/ae2fc/lang/en_US.lang b/src/main/resources/assets/ae2fc/lang/en_US.lang index 58ebb341..08a052e9 100644 --- a/src/main/resources/assets/ae2fc/lang/en_US.lang +++ b/src/main/resources/assets/ae2fc/lang/en_US.lang @@ -11,13 +11,16 @@ item.part_fluid_storage_bus.name=ME Fluid Storage Bus item.part_fluid_level_emitter.name=ME Fluid Level Emitter item.part_fluid_storage_monitor.name=ME Fluid Storage Monitor item.part_fluid_conversion_monitor.name=ME Fluid Conversion Monitor +item.part_level_terminal.name=ME Level Terminal item.portable_fluid_cell.name=Portable Fluid Cell item.wireless_fluid_terminal.name=Wireless Fluid Terminal +item.wireless_level_terminal.name=Wireless Level Maintainer Terminal item.wireless_fluid_pattern_terminal.name=Wireless Fluid Pattern Terminal item.wireless_interface_terminal.name=Wireless Interface Terminal item.wireless_ultra_terminal.WIRELESS_CRAFTING_TERMINAL.name=Universal Wireless Terminal - Crafting item.wireless_ultra_terminal.WIRELESS_FLUID_PATTERN_TERMINAL.name=Universal Wireless Terminal - Pattern item.wireless_ultra_terminal.WIRELESS_FLUID_TERMINAL.name=Universal Wireless Terminal - Fluid +item.wireless_ultra_terminal.WIRELESS_LEVEL_TERMINAL.name=Universal Wireless Terminal - Level Maintainer item.wireless_ultra_terminal.WIRELESS_ESSENTIA_TERMINAL.name=Universal Wireless Terminal - Essentia item.wireless_ultra_terminal.WIRELESS_INTERFACE_TERMINAL.name=Universal Wireless Terminal - Interface item.wireless_ultra_terminal.WIRELESS_FLUID_PATTERN_TERMINAL_EX.name=Universal Wireless Terminal - Processing Pattern @@ -90,6 +93,8 @@ ae2fc.tooltip.level_maintainer.link=§eCrafting§r ae2fc.tooltip.level_maintainer.link_desc=The system is currently crafting the requested item or fluid. ae2fc.tooltip.level_maintainer.export=§5Exporting§r ae2fc.tooltip.level_maintainer.export_desc=The Requester is trying to export the crafting results to the system. If the Requester has this status for a long time, there is no available space for the export. +ae2fc.tooltip.level_maintainer.error=§4Errored§r +ae2fc.tooltip.level_maintainer.error_desc=The Requester is trying to schedule crafting job but it fails. Possible reasons no receipt found or insufficient resources to craft. ae2fc.tooltip.level_maintainer.batch_size=§6Crafting Batch Size§r ae2fc.tooltip.level_maintainer.batch_size.hint=When craft requests are emitted, the Requester will use the batch size for the request. Using larger batches will increase the speed of stocking up items but will require more crafting storage. ae2fc.tooltip.level_maintainer.request_size=§6Amount to Maintain§r @@ -97,6 +102,7 @@ ae2fc.tooltip.level_maintainer.request_size.hint=The count defines how many item ae2fc.tooltip.ultra_terminal.WIRELESS_CRAFTING_TERMINAL=Mode: Crafting Terminal ae2fc.tooltip.ultra_terminal.WIRELESS_FLUID_PATTERN_TERMINAL=Mode: Pattern Terminal ae2fc.tooltip.ultra_terminal.WIRELESS_FLUID_TERMINAL=Mode: Fluid Terminal +ae2fc.tooltip.ultra_terminal.WIRELESS_LEVEL_TERMINAL=Mode: Level Maintainer Terminal ae2fc.tooltip.ultra_terminal.WIRELESS_ESSENTIA_TERMINAL=Mode: Essentia Terminal ae2fc.tooltip.ultra_terminal.WIRELESS_INTERFACE_TERMINAL=Mode: Interface Terminal ae2fc.tooltip.ultra_terminal.WIRELESS_FLUID_PATTERN_TERMINAL_EX=Mode: Processing Pattern Terminal @@ -108,6 +114,7 @@ ae2fc.tooltip.cell_contents=Contains Fluid: ae2fc.tooltip.cell_empty=Cell is empty ae2fc.tooltip.cell_portable=Right click to open its GUI. It works like a backpack, but you need to charge it before using. ae2fc.tooltip.fluid_terminal=uh So we don't need EC2 anymore? +ae2fc.tooltip.level_terminal=uh... ae2fc.tooltip.invalid_fluid=Invalid fluid! ae2fc.tooltip.encode_pattern=Encode Pattern ae2fc.tooltip.fluid_packet=Use a Fluid Packet Decoder to\nconvert this back into fluid. @@ -123,6 +130,7 @@ ae2fc.tooltip.craft_terminal_w=Crafting Terminal ae2fc.tooltip.pattern_terminal_w=Pattern Terminal ae2fc.tooltip.essentia_terminal_w=Essentia Terminal ae2fc.tooltip.interface_terminal_w=Interface Terminal +ae2fc.tooltip.level_terminal_w=Level Terminal ae2fc.tooltip.pattern_terminal_ex_w=Processing Pattern Terminal ae2fc.tooltip.wireless.installed=Installed Card: @@ -134,13 +142,17 @@ ae2fc.tooltip.submit=Submit ae2fc.tooltip.enable=Enabled ae2fc.tooltip.disable=Disabled ae2fc.tooltip.edit=Edit +ae2fc.tooltip.view=View ae2fc.tooltip.fill_pattern=Allow auto fill blank pattern ae2fc.tooltip.not_fill_pattern=Allow auto fill blank pattern ae2fc.tooltip.fill_pattern.hint=Enabled ae2fc.tooltip.not_fill_pattern.hint=Disabled +ae2fc.tooltip.open_configuration=Open configuration +ae2fc.tooltip.block_highlight=Highlight Block ae2fc.gui.part_fluid_pattern_terminal=Fluid Pattern Terminal ae2fc.gui.part_fluid_pattern_terminal_ex=Fluid Processing Pattern Terminal +ae2fc.gui.part_level_terminal=Level Terminal ae2fc.gui.fluid_pattern_encoder=Fluid Pattern Encoder ae2fc.gui.fluid_packet_decoder=ME Fluid Packet Decoder ae2fc.gui.ingredient_buffer=Ingredient Buffer @@ -162,6 +174,12 @@ ae2fc.gui.part_fluid_terminal=ME Fluid Terminal ae2fc.gui.part_fluid_storage_bus=ME Fluid Storage Bus ae2fc.gui.fluid_auto_filler=ME Fluid Auto Filler +ae2fc.gui.color.StateNone=00000000 +ae2fc.gui.color.StateIdle=FF55FF55 +ae2fc.gui.color.StateCraft=FFFFFF55 +ae2fc.gui.color.StateExport=FFAA00AA +ae2fc.gui.color.StateError=FFFF5555 + error.unknown=Unknown key.OpenTerminal=Open Wireless Universal Terminal diff --git a/src/main/resources/assets/ae2fc/textures/blocks/level_terminal_bright.png b/src/main/resources/assets/ae2fc/textures/blocks/level_terminal_bright.png new file mode 100644 index 00000000..fa8156bd Binary files /dev/null and b/src/main/resources/assets/ae2fc/textures/blocks/level_terminal_bright.png differ diff --git a/src/main/resources/assets/ae2fc/textures/blocks/level_terminal_dark.png b/src/main/resources/assets/ae2fc/textures/blocks/level_terminal_dark.png new file mode 100644 index 00000000..44e4c64f Binary files /dev/null and b/src/main/resources/assets/ae2fc/textures/blocks/level_terminal_dark.png differ diff --git a/src/main/resources/assets/ae2fc/textures/blocks/level_terminal_medium.png b/src/main/resources/assets/ae2fc/textures/blocks/level_terminal_medium.png new file mode 100644 index 00000000..a24af7d7 Binary files /dev/null and b/src/main/resources/assets/ae2fc/textures/blocks/level_terminal_medium.png differ diff --git a/src/main/resources/assets/ae2fc/textures/gui/level_terminal.png b/src/main/resources/assets/ae2fc/textures/gui/level_terminal.png new file mode 100644 index 00000000..a1cd2545 Binary files /dev/null and b/src/main/resources/assets/ae2fc/textures/gui/level_terminal.png differ diff --git a/src/main/resources/assets/ae2fc/textures/gui/states0.png b/src/main/resources/assets/ae2fc/textures/gui/states0.png index 39e33fad..bca40fd7 100644 Binary files a/src/main/resources/assets/ae2fc/textures/gui/states0.png and b/src/main/resources/assets/ae2fc/textures/gui/states0.png differ diff --git a/src/main/resources/assets/ae2fc/textures/gui/states2.png b/src/main/resources/assets/ae2fc/textures/gui/states2.png new file mode 100644 index 00000000..2018500d Binary files /dev/null and b/src/main/resources/assets/ae2fc/textures/gui/states2.png differ diff --git a/src/main/resources/assets/ae2fc/textures/items/wireless_interface_terminal.png b/src/main/resources/assets/ae2fc/textures/items/wireless_interface_terminal.png index 93716a83..fbf9a036 100644 Binary files a/src/main/resources/assets/ae2fc/textures/items/wireless_interface_terminal.png and b/src/main/resources/assets/ae2fc/textures/items/wireless_interface_terminal.png differ diff --git a/src/main/resources/assets/ae2fc/textures/items/wireless_level_terminal.png b/src/main/resources/assets/ae2fc/textures/items/wireless_level_terminal.png new file mode 100644 index 00000000..9749c6dc Binary files /dev/null and b/src/main/resources/assets/ae2fc/textures/items/wireless_level_terminal.png differ