diff --git a/gradle/scripts/dependencies.gradle b/gradle/scripts/dependencies.gradle index fb4ca39..f087009 100644 --- a/gradle/scripts/dependencies.gradle +++ b/gradle/scripts/dependencies.gradle @@ -41,9 +41,10 @@ dependencies { compileOnly "com.cleanroommc:lwjglx:1.0.0" } - modImplementation "com.github.FormlessDragon:Applied-Energistics-2-Supergiant:v1.0.14" - modImplementation "curse.maven:had-enough-items-557549:7938914" + modImplementation "com.github.FormlessDragon:Applied-Energistics-2-Supergiant:v1.1.0" + modImplementation "curse.maven:had-enough-items-557549:9093136" modCompileOnly 'curse.maven:mouse-tweaks-unofficial-461660:5876158' + modCompileOnly "curse.maven:baubles-227083:2518667" compileOnly 'CraftTweaker2:CraftTweaker2-MC1120-Main:1.12-4.1.20.711' //modImplementation 'software.bernie.geckolib:geckolib-forge-1.12.2:3.0.31' diff --git a/src/main/java/com/formlesslab/ae2additions/AppliedAdditions.java b/src/main/java/com/formlesslab/ae2additions/AppliedAdditions.java index 552472e..acde19b 100644 --- a/src/main/java/com/formlesslab/ae2additions/AppliedAdditions.java +++ b/src/main/java/com/formlesslab/ae2additions/AppliedAdditions.java @@ -9,6 +9,8 @@ import com.formlesslab.ae2additions.init.ModContent; import com.formlesslab.ae2additions.init.ModGuiHandler; import com.formlesslab.ae2additions.init.ModNetworks; +import com.formlesslab.ae2additions.util.ImportExportCardHandler; +import com.formlesslab.ae2additions.client.ImportExportGuiHooks; import com.formlesslab.ae2additions.me.cluster.QuantumCpuRowIcons; import com.formlesslab.ae2additions.me.service.QuantumCpuMaintenanceService; import net.minecraftforge.common.MinecraftForge; @@ -39,6 +41,7 @@ public void preInit(FMLPreInitializationEvent event) { ModContent.registerTileEntities(); InfinityCellInventory.registerHandler(); ModNetworks.init(); + MinecraftForge.EVENT_BUS.register(ImportExportCardHandler.class); NetworkRegistry.INSTANCE.registerGuiHandler(this, new ModGuiHandler()); GridServices.register(QuantumCpuMaintenanceService.class, QuantumCpuMaintenanceService.class); QuantumCpuRowIcons.bootstrap(); @@ -47,6 +50,7 @@ public void preInit(FMLPreInitializationEvent event) { MinecraftForge.EVENT_BUS.register(QuantumComputerModelOverride.INSTANCE); MinecraftForge.EVENT_BUS.register(WirelessHighlightHandler.INSTANCE); MinecraftForge.EVENT_BUS.register(CraftingJobNotificationHandler.INSTANCE); + MinecraftForge.EVENT_BUS.register(ImportExportGuiHooks.class); } LOGGER.info("{} initialized", Tags.MOD_NAME); } diff --git a/src/main/java/com/formlesslab/ae2additions/client/ImportExportGuiHooks.java b/src/main/java/com/formlesslab/ae2additions/client/ImportExportGuiHooks.java new file mode 100644 index 0000000..0897ccb --- /dev/null +++ b/src/main/java/com/formlesslab/ae2additions/client/ImportExportGuiHooks.java @@ -0,0 +1,184 @@ +package com.formlesslab.ae2additions.client; + +import ae2.api.implementations.guiobjects.ItemGuiHost; +import ae2.api.upgrades.IUpgradeInventory; +import ae2.api.upgrades.IUpgradeableObject; +import ae2.client.gui.AEBaseGui; +import ae2.client.gui.me.common.GuiMEStorage; +import ae2.core.gui.locator.BaublesItemLocator; +import ae2.core.gui.locator.ItemGuiHostLocator; +import com.formlesslab.ae2additions.init.ModContent; +import com.formlesslab.ae2additions.init.ModNetworks; +import com.formlesslab.ae2additions.util.WirelessTerminalAccess; +import com.formlesslab.ae2additions.network.COpenImportExportCard; +import com.formlesslab.ae2additions.client.gui.widgets.ImportExportCardButton; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.util.text.TextComponentTranslation; +import net.minecraftforge.client.event.GuiScreenEvent; +import net.minecraftforge.client.event.GuiOpenEvent; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; + +import java.lang.reflect.Method; +import java.util.Map; +import java.util.WeakHashMap; +import java.lang.ref.WeakReference; + +public final class ImportExportGuiHooks { + private static final int NO_TERMINAL = Integer.MIN_VALUE; + private static final Map, ImportExportCardButton[]> BUTTONS = new WeakHashMap<>(); + private static Method ADD_TO_LEFT_TOOLBAR; + + static { + try { + ADD_TO_LEFT_TOOLBAR = AEBaseGui.class.getDeclaredMethod("addToLeftToolbar", GuiButton.class); + ADD_TO_LEFT_TOOLBAR.setAccessible(true); + } catch (ReflectiveOperationException ignored) { + } + } + + private ImportExportGuiHooks() { + } + + @SubscribeEvent + public static void onInit(GuiScreenEvent.InitGuiEvent.Pre event) { + if (!(event.getGui() instanceof GuiMEStorage screen)) { + return; + } + Minecraft mc = Minecraft.getMinecraft(); + if (mc.player == null) { + return; + } + if (BUTTONS.containsKey(screen)) { + updateVisibility(screen, mc.player.inventory); + return; + } + + WeakReference> screenReference = new WeakReference<>(screen); + ImportExportCardButton importButton = new ImportExportCardButton(false, + () -> openCard(screenReference.get(), false)); + importButton.setMessage(new TextComponentTranslation("item.ae2additions.import_card.name")); + ImportExportCardButton exportButton = new ImportExportCardButton(true, + () -> openCard(screenReference.get(), true)); + exportButton.setMessage(new TextComponentTranslation("item.ae2additions.export_card.name")); + + addToolbarButton(screen, event, importButton); + addToolbarButton(screen, event, exportButton); + BUTTONS.put(screen, new ImportExportCardButton[]{importButton, exportButton}); + updateVisibility(screen, mc.player.inventory); + } + + @SubscribeEvent + public static void onDraw(GuiScreenEvent.DrawScreenEvent.Pre event) { + if (!(event.getGui() instanceof GuiMEStorage screen)) { + return; + } + Minecraft mc = Minecraft.getMinecraft(); + if (mc.player != null) { + updateVisibility(screen, mc.player.inventory); + } + } + + @SubscribeEvent + public static void onGuiOpen(GuiOpenEvent event) { + if (event.getGui() == null) { + BUTTONS.clear(); + } else { + BUTTONS.keySet().removeIf(screen -> screen != event.getGui()); + } + } + + private static void addToolbarButton(GuiMEStorage screen, GuiScreenEvent.InitGuiEvent.Pre event, ImportExportCardButton button) { + boolean addedToToolbar = false; + if (ADD_TO_LEFT_TOOLBAR != null) { + try { + ADD_TO_LEFT_TOOLBAR.invoke(screen, button); + addedToToolbar = true; + } catch (ReflectiveOperationException ignored) { + } + } + if (!addedToToolbar) { + event.getButtonList().add(button); + } + } + + private static void updateVisibility(GuiMEStorage screen, InventoryPlayer inventory) { + ImportExportCardButton[] buttons = BUTTONS.get(screen); + if (buttons == null) { + return; + } + int[] slots = findCardSlots(screen, inventory); + buttons[0].setVisibility(slots[1] >= 0 && slots[0] != NO_TERMINAL); + buttons[1].setVisibility(slots[3] >= 0 && slots[2] != NO_TERMINAL); + } + + private static void openCard(GuiMEStorage screen, boolean export) { + if (screen == null) { + return; + } + Minecraft mc = Minecraft.getMinecraft(); + if (mc.player == null) { + return; + } + int[] slots = findCardSlots(screen, mc.player.inventory); + int terminalSlot = export ? slots[2] : slots[0]; + int cardSlot = export ? slots[3] : slots[1]; + if (terminalSlot != NO_TERMINAL && cardSlot >= 0) { + ModNetworks.sendToServer(new COpenImportExportCard(terminalSlot, cardSlot, export)); + } + } + + private static int[] findCardSlots(GuiMEStorage screen, InventoryPlayer inventory) { + int[] result = {NO_TERMINAL, -1, NO_TERMINAL, -1}; + Object host = screen.getContainer().getHost(); + if (host instanceof ItemGuiHost itemHost) { + int terminalSlot = encodeTerminalSlot(itemHost.getLocator()); + if (host instanceof IUpgradeableObject upgradeable && terminalSlot != NO_TERMINAL) { + recordCardSlots(result, terminalSlot, upgradeable.getUpgrades()); + } + return result; + } + + for (int candidate = 0; candidate < inventory.getSizeInventory(); candidate++) { + ItemStack terminal = inventory.getStackInSlot(candidate); + if (!WirelessTerminalAccess.isWirelessTerminal(terminal)) { + continue; + } + IUpgradeInventory upgrades = WirelessTerminalAccess.getUpgrades(terminal); + if (upgrades != null) { + recordCardSlots(result, candidate, upgrades); + } + } + return result; + } + + private static int encodeTerminalSlot(ItemGuiHostLocator locator) { + if (locator == null) { + return NO_TERMINAL; + } + Integer inventorySlot = locator.getPlayerInventorySlot(); + if (inventorySlot != null) { + return inventorySlot; + } + if (locator instanceof BaublesItemLocator baublesLocator) { + return -1 - baublesLocator.getBaubleSlot(); + } + return NO_TERMINAL; + } + + private static void recordCardSlots(int[] result, int terminalSlot, IUpgradeInventory upgrades) { + for (int slot = 0; slot < upgrades.size(); slot++) { + ItemStack card = upgrades.getStackInSlot(slot); + if (card.getItem() == ModContent.IMPORT_CARD && result[1] < 0) { + result[0] = terminalSlot; + result[1] = slot; + } + if (card.getItem() == ModContent.EXPORT_CARD && result[3] < 0) { + result[2] = terminalSlot; + result[3] = slot; + } + } + } +} diff --git a/src/main/java/com/formlesslab/ae2additions/client/gui/GuiAssemblerMatrix.java b/src/main/java/com/formlesslab/ae2additions/client/gui/GuiAssemblerMatrix.java index 6d2cca8..a71a580 100644 --- a/src/main/java/com/formlesslab/ae2additions/client/gui/GuiAssemblerMatrix.java +++ b/src/main/java/com/formlesslab/ae2additions/client/gui/GuiAssemblerMatrix.java @@ -120,7 +120,8 @@ public void drawScreen(int mouseX, int mouseY, float partialTicks) { this.refreshVisibleSlots(); super.drawScreen(mouseX, mouseY, partialTicks); if (isMouseOverQueueText(mouseX - this.guiLeft, mouseY - this.guiTop)) { - this.drawTooltipLines(mouseX, mouseY, Collections.singletonList(I18n.format("gui.ae2additions.assembler_matrix.queued_jobs", this.queuedJobs))); + this.drawTooltipLines(ItemStack.EMPTY, mouseX, mouseY, + Collections.singletonList(I18n.format("gui.ae2additions.assembler_matrix.queued_jobs", this.queuedJobs))); } } diff --git a/src/main/java/com/formlesslab/ae2additions/client/gui/GuiImportExportCard.java b/src/main/java/com/formlesslab/ae2additions/client/gui/GuiImportExportCard.java new file mode 100644 index 0000000..2f86461 --- /dev/null +++ b/src/main/java/com/formlesslab/ae2additions/client/gui/GuiImportExportCard.java @@ -0,0 +1,295 @@ +package com.formlesslab.ae2additions.client.gui; + +import ae2.client.gui.AEBaseGui; +import ae2.client.gui.Icon; +import ae2.client.gui.style.Blitter; +import ae2.client.gui.style.GuiStyle; +import ae2.client.gui.style.GuiStyleManager; +import ae2.client.gui.widgets.ServerSettingToggleButton; +import ae2.client.gui.widgets.TabButton; +import ae2.client.gui.widgets.UpgradesPanel; +import ae2.api.upgrades.Upgrades; +import ae2.api.config.FuzzyMode; +import ae2.api.config.Settings; +import ae2.container.SlotSemantics; +import ae2.core.definitions.AEItems; +import ae2.core.localization.GuiText; +import com.formlesslab.ae2additions.Tags; +import com.formlesslab.ae2additions.container.CardPlayerSlot; +import com.formlesslab.ae2additions.container.ContainerImportExportCard; +import com.formlesslab.ae2additions.init.ModNetworks; +import com.formlesslab.ae2additions.network.CCloseImportExportCard; +import com.formlesslab.ae2additions.network.CCardConfig; +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.resources.I18n; +import net.minecraft.inventory.ClickType; +import net.minecraft.inventory.Slot; +import net.minecraft.item.ItemStack; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.util.text.ITextComponent; +import org.lwjgl.input.Keyboard; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +public final class GuiImportExportCard extends AEBaseGui { + private static final int MASS_SELECT_X = 23 + 16 * 10; + private static final int HOTBAR_OFFSET_Y = 16 * 3 + 10; + private static final Blitter CHECKMARK = Blitter.texture(new ResourceLocation(Tags.MOD_ID, "textures/guis/checkmark.png"), 16, 16); + private static final Blitter XMARK = Blitter.texture(new ResourceLocation(Tags.MOD_ID, "textures/guis/xmark.png"), 16, 16); + private static final Blitter MASS_SELECT = Blitter.texture(new ResourceLocation(Tags.MOD_ID, "textures/guis/mass_select.png"), 16, 16); + + private final int[] selectedInventorySlots; + private final TabButton backButton; + private final ServerSettingToggleButton fuzzyMode; + private boolean selectingPlayerSlot; + private boolean draggingPlayerStack; + private int clickedPlayerSlot = -1; + + public GuiImportExportCard(ContainerImportExportCard container, InventoryPlayer playerInventory, GuiStyle style) { + super(container, playerInventory, style); + this.selectedInventorySlots = container.getSelectedSlots(); + this.backButton = new TabButton(Icon.BACK, new TextComponentTranslation(container.export ? "item.ae2additions.export_card.name" : "item.ae2additions.import_card.name"), this::closeScreen); + this.widgets.add("back", this.backButton); + this.widgets.add("upgrades", UpgradesPanel.create(this.widgets, container.getSlots(SlotSemantics.UPGRADE), this::getCompatibleUpgrades)); + this.fuzzyMode = this.addToLeftToolbar(new ServerSettingToggleButton<>(Settings.FUZZY_MODE, FuzzyMode.IGNORE_ALL)); + } + + private List getCompatibleUpgrades() { + var lines = Upgrades.getTooltipLinesForInventory(this.container.getUpgrades()); + var result = new ArrayList(lines.size() + 1); + result.add(GuiText.CompatibleUpgrades.text()); + result.addAll(lines); + return result; + } + + public static GuiImportExportCard create(ContainerImportExportCard container, InventoryPlayer inventory) { + String type = container.export ? "export" : "import"; + String path = "/screens/" + type + "_card_" + container.getCapacityCardCount() + ".json"; + return new GuiImportExportCard(container, inventory, GuiStyleManager.loadStyleDoc(path)); + } + + private void closeScreen() { + ModNetworks.sendToServer(new CCloseImportExportCard()); + } + + @Override + protected void keyTyped(char typedChar, int keyCode) throws IOException { + if (keyCode == Keyboard.KEY_ESCAPE) { + closeScreen(); + return; + } + super.keyTyped(typedChar, keyCode); + } + + @Override + protected void updateBeforeRender() { + super.updateBeforeRender(); + this.fuzzyMode.set(this.container.fuzzyMode); + this.fuzzyMode.setVisibility(!this.container.getCard().isEmpty() && this.hasFuzzyCard()); + } + + private boolean hasFuzzyCard() { + return this.container.getUpgrades() + .isInstalled(AEItems.FUZZY_CARD.item()); + } + + @Override + public void drawFG(int offsetX, int offsetY, int mouseX, int mouseY) { + super.drawFG(offsetX, offsetY, mouseX, mouseY); + GlStateManager.disableLighting(); + GlStateManager.disableDepth(); + try { + drawCardOverlays(); + } finally { + GlStateManager.enableDepth(); + } + } + + private void drawCardOverlays() { + for (Slot slot : this.container.inventorySlots) { + int x = slot.xPos; + int y = slot.yPos; + if (slot instanceof CardPlayerSlot) { + int index = slot.getSlotIndex(); + if (index < 0 || index >= selectedInventorySlots.length) { + continue; + } + int selected = selectedInventorySlots[index]; + if (selected > 0) { + if (this.container.export) { + drawFilterNumber(x, y, selected); + } else { + CHECKMARK.copy().dest(x, y, 16, 16).zOffset(4).blit(); + } + } else if (selected == 0) { + XMARK.copy().dest(x, y, 16, 16).zOffset(4).blit(); + } + } + } + + if (this.container.export) { + int filter = 1; + for (Slot slot : this.container.getSlots(ContainerImportExportCard.CARD_CONFIG)) { + drawFilterNumber(slot.xPos, slot.yPos, filter++); + } + } + + for (int group = 0; group < 2; group++) { + MASS_SELECT.copy().dest(MASS_SELECT_X, getMassSelectY(group), 16, 16).zOffset(4).blit(); + } + } + + @Override + public void drawScreen(int mouseX, int mouseY, float partialTicks) { + super.drawScreen(mouseX, mouseY, partialTicks); + int group = findMassSelectGroup(mouseX, mouseY); + if (group >= 0) { + drawTooltipLines(ItemStack.EMPTY, mouseX, mouseY, List.of( + I18n.format("gui.ae2additions.card.mass_select." + (group == 0 ? "inventory" : "hotbar")), + I18n.format("gui.ae2additions.card.mass_select." + (this.container.export ? "export" : "import")), + I18n.format("gui.ae2additions.card.mass_select.clear"))); + } + } + + private void drawFilterNumber(int x, int y, int value) { + String text = Integer.toString(value); + GlStateManager.pushMatrix(); + GlStateManager.translate(x + 16, y + 1, 5); + GlStateManager.scale(0.5F, 0.5F, 1.0F); + this.fontRenderer.drawString(text, -this.fontRenderer.getStringWidth(text), 0, 0xFF55FF55); + GlStateManager.popMatrix(); + } + + @Override + protected void mouseClicked(int mouseX, int mouseY, int mouseButton) throws IOException { + this.draggingPlayerStack = false; + Slot slot = findCardPlayerSlot(mouseX, mouseY); + if (slot != null && (mouseButton == 0 || mouseButton == 1) + && this.playerInventory.getItemStack().isEmpty()) { + if (Keyboard.isKeyDown(Keyboard.KEY_LSHIFT) || Keyboard.isKeyDown(Keyboard.KEY_RSHIFT)) { + super.mouseClicked(mouseX, mouseY, mouseButton); + return; + } + this.selectingPlayerSlot = true; + this.clickedPlayerSlot = slot.getSlotIndex(); + return; + } + + if (handleMassSelect(mouseX, mouseY, mouseButton)) { + return; + } + super.mouseClicked(mouseX, mouseY, mouseButton); + } + + @Override + protected void mouseReleased(int mouseX, int mouseY, int state) { + if (this.draggingPlayerStack) { + resetPlayerSelection(); + super.mouseReleased(mouseX, mouseY, state); + return; + } + if (this.selectingPlayerSlot) { + if (state == 0 || state == 1) { + Slot slot = findCardPlayerSlot(mouseX, mouseY); + if (slot != null && slot.getSlotIndex() == this.clickedPlayerSlot) { + updateSelectedSlot(this.clickedPlayerSlot, state == 1); + } + } + resetPlayerSelection(); + return; + } + super.mouseReleased(mouseX, mouseY, state); + } + + private void resetPlayerSelection() { + this.draggingPlayerStack = false; + this.selectingPlayerSlot = false; + this.clickedPlayerSlot = -1; + } + + @Override + protected void mouseClickMove(int mouseX, int mouseY, int clickedMouseButton, long timeSinceLastClick) { + if (this.selectingPlayerSlot && !this.draggingPlayerStack && (clickedMouseButton == 0 || clickedMouseButton == 1) && this.playerInventory.getItemStack().isEmpty()) { + Slot origin = findCardPlayerSlotByIndex(this.clickedPlayerSlot); + if (origin != null && origin.getHasStack()) { + handleMouseClick(origin, origin.slotNumber, clickedMouseButton, ClickType.PICKUP); + this.draggingPlayerStack = true; + super.mouseClickMove(mouseX, mouseY, clickedMouseButton, timeSinceLastClick); + return; + } + return; + } + super.mouseClickMove(mouseX, mouseY, clickedMouseButton, timeSinceLastClick); + } + + private Slot findCardPlayerSlot(int mouseX, int mouseY) { + for (Slot slot : this.container.inventorySlots) { + if (!(slot instanceof CardPlayerSlot)) { + continue; + } + if (this.isCardPointInRegion(slot.xPos, slot.yPos, 16, 16, mouseX, mouseY)) { + return slot; + } + } + return null; + } + + private Slot findCardPlayerSlotByIndex(int index) { + for (Slot slot : this.container.inventorySlots) { + if (slot instanceof CardPlayerSlot && slot.getSlotIndex() == index) { + return slot; + } + } + return null; + } + + private boolean isCardPointInRegion(int x, int y, int width, int height, int mouseX, int mouseY) { + return mouseX >= this.guiLeft + x && mouseX < this.guiLeft + x + width && mouseY >= this.guiTop + y && mouseY < this.guiTop + y + height; + } + + private void updateSelectedSlot(int slot, boolean rightClick) { + int value; + if (rightClick) { + value = 0; + } else if (this.container.export) { + value = selectedInventorySlots[slot] >= this.container.getFilterSize() ? 0 : selectedInventorySlots[slot] + 1; + } else { + value = selectedInventorySlots[slot] == 0 ? 1 : 0; + } + selectedInventorySlots[slot] = value; + ModNetworks.sendToServer(CCardConfig.assignSlot(this.container.terminalSlot, this.container.cardSlot, slot, value - 1)); + } + + private int findMassSelectGroup(int mouseX, int mouseY) { + for (int group = 0; group < 2; group++) { + if (isCardPointInRegion(MASS_SELECT_X, getMassSelectY(group), 16, 16, mouseX, mouseY)) { + return group; + } + } + return -1; + } + + private int getMassSelectY(int group) { + return 76 + this.container.getCapacityCardCount() * 18 + group * HOTBAR_OFFSET_Y; + } + + private boolean handleMassSelect(int mouseX, int mouseY, int mouseButton) { + if (mouseButton != 0 && mouseButton != 1) { + return false; + } + int group = findMassSelectGroup(mouseX, mouseY); + if (group < 0) { + return false; + } + int start = group == 1 ? 0 : 9; + int end = group == 1 ? 9 : 36; + for (int i = start; i < end; i++) { + updateSelectedSlot(i, mouseButton == 1); + } + return true; + } +} diff --git a/src/main/java/com/formlesslab/ae2additions/client/gui/GuiReactionChamber.java b/src/main/java/com/formlesslab/ae2additions/client/gui/GuiReactionChamber.java index 246d399..b551f1f 100644 --- a/src/main/java/com/formlesslab/ae2additions/client/gui/GuiReactionChamber.java +++ b/src/main/java/com/formlesslab/ae2additions/client/gui/GuiReactionChamber.java @@ -16,6 +16,7 @@ import com.formlesslab.ae2additions.util.FluidStackRenderer; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.item.ItemStack; import net.minecraft.util.text.ITextComponent; import net.minecraft.util.text.TextComponentString; import net.minecraft.util.text.TextComponentTranslation; @@ -99,7 +100,7 @@ private boolean drawTankTooltip(int mouseX, int mouseY, Rectangle tank, FluidSta List tooltip = new ObjectArrayList<>(); tooltip.add(fluid.getFluid().getLocalizedName(new FluidStack(fluid.getFluid(), 1))); tooltip.add(TextFormatting.GRAY + "" + amount + " / " + TileReactionChamber.TANK_CAPACITY + " mB"); - this.drawTooltipLines(mouseX, mouseY, tooltip); + this.drawTooltipLines(ItemStack.EMPTY, mouseX, mouseY, tooltip); return true; } } diff --git a/src/main/java/com/formlesslab/ae2additions/client/gui/GuiWirelessHub.java b/src/main/java/com/formlesslab/ae2additions/client/gui/GuiWirelessHub.java index 9b67a04..ff375b3 100644 --- a/src/main/java/com/formlesslab/ae2additions/client/gui/GuiWirelessHub.java +++ b/src/main/java/com/formlesslab/ae2additions/client/gui/GuiWirelessHub.java @@ -17,6 +17,7 @@ import it.unimi.dsi.fastutil.objects.ObjectArrayList; import net.minecraft.client.Minecraft; import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.item.ItemStack; import net.minecraft.util.math.BlockPos; import net.minecraft.util.text.ITextComponent; import net.minecraft.util.text.TextComponentTranslation; @@ -92,7 +93,7 @@ public void drawScreen(int mouseX, int mouseY, float partialTicks) { super.drawScreen(mouseX, mouseY, partialTicks); int port = hoveredPort(mouseX - this.guiLeft, mouseY - this.guiTop); if (port >= 0) { - drawTooltipLines(mouseX, mouseY, tooltipForPort(port)); + drawTooltipLines(ItemStack.EMPTY, mouseX, mouseY, tooltipForPort(port)); } } diff --git a/src/main/java/com/formlesslab/ae2additions/client/gui/widgets/ImportExportCardButton.java b/src/main/java/com/formlesslab/ae2additions/client/gui/widgets/ImportExportCardButton.java new file mode 100644 index 0000000..5af3f8e --- /dev/null +++ b/src/main/java/com/formlesslab/ae2additions/client/gui/widgets/ImportExportCardButton.java @@ -0,0 +1,38 @@ +package com.formlesslab.ae2additions.client.gui.widgets; + +import ae2.client.gui.Icon; +import ae2.client.gui.style.Blitter; +import ae2.client.gui.widgets.IconButton; +import com.formlesslab.ae2additions.Tags; +import net.minecraft.client.Minecraft; +import net.minecraft.util.ResourceLocation; + +public final class ImportExportCardButton extends IconButton { + private final Blitter icon; + + public ImportExportCardButton(boolean export, Runnable action) { + super(action); + this.icon = Blitter.texture(new ResourceLocation(Tags.MOD_ID, + "textures/guis/" + (export ? "export_card" : "import_card") + ".png"), 16, 16); + } + + @Override + protected Icon getIcon() { + return null; + } + + @Override + public void drawButton(Minecraft minecraft, int mouseX, int mouseY, float partialTicks) { + if (!this.visible) { + return; + } + + this.hovered = mouseX >= this.x && mouseY >= this.y + && mouseX < this.x + this.width && mouseY < this.y + this.height; + int yOffset = this.hovered ? 1 : 0; + Icon background = this.hovered || this.isFocused() + ? Icon.TOOLBAR_BUTTON_BACKGROUND_HOVER : Icon.TOOLBAR_BUTTON_BACKGROUND; + background.getBlitter().dest(this.x - 1, this.y + yOffset, 18, 20).zOffset(2).blit(); + this.icon.copy().dest(this.x, this.y + 1 + yOffset, 16, 16).zOffset(3).blit(); + } +} diff --git a/src/main/java/com/formlesslab/ae2additions/container/CardPlayerSlot.java b/src/main/java/com/formlesslab/ae2additions/container/CardPlayerSlot.java new file mode 100644 index 0000000..fee890b --- /dev/null +++ b/src/main/java/com/formlesslab/ae2additions/container/CardPlayerSlot.java @@ -0,0 +1,11 @@ +package com.formlesslab.ae2additions.container; + +import net.minecraft.inventory.IInventory; +import net.minecraft.inventory.Slot; + +public final class CardPlayerSlot extends Slot { + public CardPlayerSlot(IInventory inventory, int index, int x, int y) { + super(inventory, index, x, y); + } + +} diff --git a/src/main/java/com/formlesslab/ae2additions/container/ContainerImportExportCard.java b/src/main/java/com/formlesslab/ae2additions/container/ContainerImportExportCard.java new file mode 100644 index 0000000..de3b0c9 --- /dev/null +++ b/src/main/java/com/formlesslab/ae2additions/container/ContainerImportExportCard.java @@ -0,0 +1,208 @@ +package com.formlesslab.ae2additions.container; + +import ae2.api.upgrades.IUpgradeInventory; +import ae2.api.upgrades.UpgradeInventories; +import ae2.api.config.FuzzyMode; +import ae2.api.config.Settings; +import ae2.api.util.IConfigManager; +import ae2.api.util.IConfigurableObject; +import ae2.container.AEBaseContainer; +import ae2.container.SlotSemantic; +import ae2.container.guisync.GuiSync; +import ae2.container.SlotSemantics; +import ae2.container.slot.FakeSlot; +import ae2.util.ConfigGuiInventory; +import ae2.util.ConfigInventory; +import com.formlesslab.ae2additions.init.ModContent; +import com.formlesslab.ae2additions.util.ImportExportCardConfig; +import com.formlesslab.ae2additions.util.WirelessTerminalAccess; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.inventory.Slot; +import net.minecraft.item.ItemStack; + +import java.util.List; + +public final class ContainerImportExportCard extends AEBaseContainer implements IConfigurableObject { + + public static final SlotSemantic CARD_CONFIG = SlotSemantics.register("AE2ADDITIONS_CARD_CONFIG", false); + + public final int terminalSlot; + public final int cardSlot; + public final boolean export; + private final InventoryPlayer inventory; + private final ItemStack terminal; + private final ConfigInventory filters; + private final int filterSize; + private final ItemStack card; + private final IUpgradeInventory upgrades; + private final IConfigManager configManager; + + @GuiSync(0) + public FuzzyMode fuzzyMode = FuzzyMode.IGNORE_ALL; + + public ContainerImportExportCard(InventoryPlayer inventory, int terminalSlot, int cardSlot, boolean export) { + super(inventory, null); + this.inventory = inventory; + this.terminalSlot = terminalSlot; + this.cardSlot = cardSlot; + this.export = export; + this.terminal = WirelessTerminalAccess.getTerminal(inventory.player, terminalSlot); + this.card = findCard(this.terminal, cardSlot, export); + this.upgrades = UpgradeInventories.forItem(this.card, ImportExportCardConfig.getUpgradeSlotCount(export), null); + this.configManager = IConfigManager.builder(this.card).registerSetting(Settings.FUZZY_MODE, FuzzyMode.IGNORE_ALL).build(); + this.fuzzyMode = this.configManager.getSetting(Settings.FUZZY_MODE); + this.filterSize = ImportExportCardConfig.getFilterSize(this.upgrades); + this.filters = createFilterInventory(this.card, this.filterSize); + this.setupUpgrades(this.upgrades); + + if (!this.terminal.isEmpty() && terminalSlot >= 0) { + this.lockPlayerInventorySlot(terminalSlot); + } + + ConfigGuiInventory filterView = this.filters.createGuiWrapper(); + + for (int i = 0; i < this.filterSize; i++) { + int x = 23 + (i % 9) * 18; + int y = 29 + (i / 9) * 18; + this.addSlot(new FakeSlot(filterView, i, x, y), CARD_CONFIG); + } + + for (int i = 0; i < 36; i++) { + Slot slot = new CardPlayerSlot(inventory, i, 0, 0); + this.addSlot(slot, i < 9 ? SlotSemantics.PLAYER_HOTBAR : SlotSemantics.PLAYER_INVENTORY); + } + + int armorY = 70 + getCapacityCardCount() * 18; + + for (int armorIndex = 0; armorIndex < 4 && 39 - armorIndex < inventory.getSizeInventory(); armorIndex++) { + int i = 39 - armorIndex; + this.addSlotToContainer(new CardPlayerSlot(inventory, i, 2, + armorY + armorIndex * 18)); + } + } + + private ConfigInventory createFilterInventory(ItemStack card, int size) { + ConfigInventory loaded = card.isEmpty() ? ConfigInventory.configTypes(size).build() : ImportExportCardConfig.readFilters(card, size); + ConfigInventory[] holder = new ConfigInventory[1]; + ConfigInventory result = ConfigInventory.configTypes(size).changeListener(() -> { + if (!this.card.isEmpty() && holder[0] != null) { + ImportExportCardConfig.writeFilters(this.card, holder[0]); + } + }).build(); + holder[0] = result; + result.readFromList(loaded.toList()); + return result; + } + + public ItemStack getCard() { + return this.card; + } + + public ConfigInventory getFilters() { + return this.filters; + } + + public IUpgradeInventory getUpgrades() { + return this.upgrades; + } + + public int getFilterSize() { + return this.filterSize; + } + + public int getCapacityCardCount() { + return ImportExportCardConfig.getCapacityCardCount(this.upgrades); + } + + @Override + public IConfigManager getConfigManager() { + return this.configManager; + } + + @Override + public Object getTarget() { + return this; + } + + @Override + public void broadcastChanges() { + if (this.isServerSide()) { + this.fuzzyMode = this.configManager.getSetting(Settings.FUZZY_MODE); + this.saveCard(); + } + super.broadcastChanges(); + } + + public int[] getSelectedSlots() { + return this.card.isEmpty() ? new int[ImportExportCardConfig.SLOT_COUNT] : ImportExportCardConfig.getSelectedSlots(this.card); + } + + public void setSelectedSlot(int slot, int value) { + if (this.card.isEmpty() || slot < 0 || slot >= ImportExportCardConfig.SLOT_COUNT) { + return; + } + int[] selected = ImportExportCardConfig.getSelectedSlots(this.card); + selected[slot] = Math.max(0, Math.min(this.filterSize, value)); + ImportExportCardConfig.setSelectedSlots(this.card, selected); + this.saveCard(); + } + + private void saveCard() { + if (!this.isServerSide()) { + return; + } + IUpgradeInventory terminalUpgrades = getValidTerminalUpgrades(this.inventory.player); + if (terminalUpgrades == null) { + return; + } + ItemStack stored = terminalUpgrades.getStackInSlot(this.cardSlot); + if (!ItemStack.areItemStacksEqual(stored, this.card)) { + terminalUpgrades.setItemDirect(this.cardSlot, this.card); + this.inventory.markDirty(); + } + } + + public List getCardPlayerSlots() { + int start = this.filterSize + this.upgrades.size(); + return this.inventorySlots.subList(start, Math.min(this.inventorySlots.size(), start + ImportExportCardConfig.SLOT_COUNT)); + } + + @Override + public boolean canInteractWith(EntityPlayer player) { + return getValidTerminalUpgrades(player) != null; + } + + private IUpgradeInventory getValidTerminalUpgrades(EntityPlayer player) { + if (player != this.inventory.player || this.card.isEmpty() + || !WirelessTerminalAccess.isWirelessTerminal(this.terminal) + || WirelessTerminalAccess.getTerminal(player, this.terminalSlot) != this.terminal) { + return null; + } + IUpgradeInventory terminalUpgrades = WirelessTerminalAccess.getUpgrades(this.terminal); + if (terminalUpgrades == null || this.cardSlot < 0 || this.cardSlot >= terminalUpgrades.size() + || terminalUpgrades.getStackInSlot(this.cardSlot).getItem() != this.card.getItem()) { + return null; + } + return terminalUpgrades; + } + + @Override + public ItemStack transferStackInSlot(EntityPlayer player, int index) { + return ItemStack.EMPTY; + } + + private static ItemStack findCard(ItemStack terminal, int cardSlot, boolean export) { + if (terminal.isEmpty()) { + return ItemStack.EMPTY; + } + IUpgradeInventory upgrades = WirelessTerminalAccess.getUpgrades(terminal); + if (upgrades == null || cardSlot < 0 || cardSlot >= upgrades.size()) { + return ItemStack.EMPTY; + } + ItemStack card = upgrades.getStackInSlot(cardSlot); + return card.getItem() == (export ? ModContent.EXPORT_CARD : ModContent.IMPORT_CARD) + ? card : ItemStack.EMPTY; + } + +} diff --git a/src/main/java/com/formlesslab/ae2additions/init/ModContent.java b/src/main/java/com/formlesslab/ae2additions/init/ModContent.java index f3901d4..d7452c3 100644 --- a/src/main/java/com/formlesslab/ae2additions/init/ModContent.java +++ b/src/main/java/com/formlesslab/ae2additions/init/ModContent.java @@ -84,6 +84,8 @@ public final class ModContent { public static final BlockWirelessHub WIRELESS_HUB; public static final ItemWirelessTool WIRELESS_TOOL; public static final ItemWirelessConnectorUpgrade WIRELESS_CONNECTOR_UPGRADE; + public static final Item IMPORT_CARD; + public static final Item EXPORT_CARD; public static final Item SHATTERED_SINGULARITY; public static final Item QUANTUM_PROCESSOR_PRESS; public static final Item PRINTED_QUANTUM_PROCESSOR; @@ -127,6 +129,8 @@ public ItemStack createIcon() { WIRELESS_HUB = new BlockWirelessHub(); WIRELESS_TOOL = new ItemWirelessTool(); WIRELESS_CONNECTOR_UPGRADE = new ItemWirelessConnectorUpgrade(); + IMPORT_CARD = Upgrades.createUpgradeCardItem(); + EXPORT_CARD = Upgrades.createUpgradeCardItem(); SHATTERED_SINGULARITY = new Item(); QUANTUM_PROCESSOR_PRESS = new Item(); PRINTED_QUANTUM_PROCESSOR = new Item(); @@ -157,6 +161,8 @@ public ItemStack createIcon() { registerItem(WIRELESS_TOOL, "wireless_tool"); registerItem(WIRELESS_CONNECTOR_UPGRADE, "wireless_connector_upgrade"); + registerItem(IMPORT_CARD, "import_card"); + registerItem(EXPORT_CARD, "export_card"); registerItem(QUANTUM_PROCESSOR_PRESS, "quantum_processor_press"); registerItem(PRINTED_QUANTUM_PROCESSOR, "printed_quantum_processor"); registerItem(QUANTUM_PROCESSOR, "quantum_processor"); @@ -212,6 +218,17 @@ public static void registerUpgrades() { Upgrades.add(AEItems.ENERGY_CARD.item(), Item.getItemFromBlock(ModContent.WIRELESS_HUB), 4); Upgrades.add(AEItems.SPEED_CARD.item(), Item.getItemFromBlock(ModContent.REACTION_CHAMBER), 4); Upgrades.add(AEItems.PARALLEL_CARD.item(), Item.getItemFromBlock(ModContent.REACTION_CHAMBER), 3); + Upgrades.add(IMPORT_CARD, AEItems.WIRELESS_TERMINAL.item(), 1); + Upgrades.add(EXPORT_CARD, AEItems.WIRELESS_TERMINAL.item(), 1); + Upgrades.add(IMPORT_CARD, AEItems.WIRELESS_CRAFTING_TERMINAL.item(), 1); + Upgrades.add(EXPORT_CARD, AEItems.WIRELESS_CRAFTING_TERMINAL.item(), 1); + Upgrades.add(AEItems.CAPACITY_CARD.item(), IMPORT_CARD, 3); + Upgrades.add(AEItems.CAPACITY_CARD.item(), EXPORT_CARD, 3); + Upgrades.add(AEItems.FUZZY_CARD.item(), IMPORT_CARD, 1); + Upgrades.add(AEItems.FUZZY_CARD.item(), EXPORT_CARD, 1); + Upgrades.add(AEItems.INVERTER_CARD.item(), IMPORT_CARD, 1); + Upgrades.add(AEItems.CRAFTING_CARD.item(), EXPORT_CARD, 1); + Upgrades.add(AEItems.SPEED_CARD.item(), EXPORT_CARD, 1); } public static void registerOreDictionary() { diff --git a/src/main/java/com/formlesslab/ae2additions/init/ModGuiHandler.java b/src/main/java/com/formlesslab/ae2additions/init/ModGuiHandler.java index f008174..718b421 100644 --- a/src/main/java/com/formlesslab/ae2additions/init/ModGuiHandler.java +++ b/src/main/java/com/formlesslab/ae2additions/init/ModGuiHandler.java @@ -12,6 +12,8 @@ import com.formlesslab.ae2additions.container.ContainerReactionChamber; import com.formlesslab.ae2additions.container.ContainerWirelessConnector; import com.formlesslab.ae2additions.container.ContainerWirelessHub; +import com.formlesslab.ae2additions.container.ContainerImportExportCard; +import com.formlesslab.ae2additions.client.gui.GuiImportExportCard; import com.formlesslab.ae2additions.tile.TileAdvCraftingBlock; import com.formlesslab.ae2additions.tile.TileAssemblerMatrixBase; import com.formlesslab.ae2additions.tile.TileReactionChamber; @@ -33,6 +35,7 @@ public class ModGuiHandler implements IGuiHandler { public static final int QUANTUM_COMPUTER = 2; public static final int ASSEMBLER_MATRIX = 3; public static final int REACTION_CHAMBER = 4; + public static final int IMPORT_EXPORT_CARD = 5; private static final Int2ObjectMap> REGISTRATIONS = new Int2ObjectLinkedOpenHashMap<>(); private static boolean clientRegistrationsInitialized; @@ -94,6 +97,9 @@ private static ContainerReactionChamber createReactionChamberContainer(EntityPla @Override public Object getServerGuiElement(int id, EntityPlayer player, World world, int x, int y, int z) { + if (id == IMPORT_EXPORT_CARD) { + return new ContainerImportExportCard(player.inventory, x, y, z != 0); + } BlockPos pos = new BlockPos(x, y, z); TileEntity tile = world.getTileEntity(pos); GuiRegistration registration = REGISTRATIONS.get(id); @@ -103,6 +109,10 @@ public Object getServerGuiElement(int id, EntityPlayer player, World world, int @Override @SideOnly(Side.CLIENT) public Object getClientGuiElement(int id, EntityPlayer player, World world, int x, int y, int z) { + if (id == IMPORT_EXPORT_CARD) { + ContainerImportExportCard container = new ContainerImportExportCard(player.inventory, x, y, z != 0); + return GuiImportExportCard.create(container, player.inventory); + } ensureClientRegistrations(); BlockPos pos = new BlockPos(x, y, z); TileEntity tile = world.getTileEntity(pos); diff --git a/src/main/java/com/formlesslab/ae2additions/init/ModNetworks.java b/src/main/java/com/formlesslab/ae2additions/init/ModNetworks.java index 8757c52..30dfac8 100644 --- a/src/main/java/com/formlesslab/ae2additions/init/ModNetworks.java +++ b/src/main/java/com/formlesslab/ae2additions/init/ModNetworks.java @@ -4,6 +4,9 @@ import com.formlesslab.ae2additions.network.CAssemblerMatrixCancel; import com.formlesslab.ae2additions.network.CAssemblerMatrixPatternMode; import com.formlesslab.ae2additions.network.CReactionChamberOutputSides; +import com.formlesslab.ae2additions.network.CCardConfig; +import com.formlesslab.ae2additions.network.COpenImportExportCard; +import com.formlesslab.ae2additions.network.CCloseImportExportCard; import com.formlesslab.ae2additions.network.SAssemblerMatrixUpdate; import com.formlesslab.ae2additions.network.base.ModClientboundPacket; import com.formlesslab.ae2additions.network.base.ModServerboundPacket; @@ -21,6 +24,9 @@ public final class ModNetworks { public static final int ASSEMBLER_MATRIX_CANCEL = 3; public static final int ASSEMBLER_MATRIX_PATTERN_MODE = 4; public static final int REACTION_CHAMBER_OUTPUT_SIDES = 5; + public static final int OPEN_IMPORT_EXPORT_CARD = 6; + public static final int IMPORT_EXPORT_CARD_CONFIG = 7; + public static final int CLOSE_IMPORT_EXPORT_CARD = 8; private static boolean initialized; @@ -39,6 +45,9 @@ public static synchronized void init() { registerServerbound(ASSEMBLER_MATRIX_CANCEL, CAssemblerMatrixCancel.class); registerServerbound(ASSEMBLER_MATRIX_PATTERN_MODE, CAssemblerMatrixPatternMode.class); registerServerbound(REACTION_CHAMBER_OUTPUT_SIDES, CReactionChamberOutputSides.class); + registerServerbound(OPEN_IMPORT_EXPORT_CARD, COpenImportExportCard.class); + registerServerbound(IMPORT_EXPORT_CARD_CONFIG, CCardConfig.class); + registerServerbound(CLOSE_IMPORT_EXPORT_CARD, CCloseImportExportCard.class); } @SuppressWarnings({"rawtypes", "unchecked"}) diff --git a/src/main/java/com/formlesslab/ae2additions/network/CCardConfig.java b/src/main/java/com/formlesslab/ae2additions/network/CCardConfig.java new file mode 100644 index 0000000..03f768d --- /dev/null +++ b/src/main/java/com/formlesslab/ae2additions/network/CCardConfig.java @@ -0,0 +1,93 @@ +package com.formlesslab.ae2additions.network; + +import ae2.api.stacks.GenericStack; +import com.formlesslab.ae2additions.container.ContainerImportExportCard; +import com.formlesslab.ae2additions.network.base.ModServerboundPacket; +import io.netty.buffer.ByteBuf; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.network.PacketBuffer; + +public final class CCardConfig extends ModServerboundPacket { + private static final int SET_FILTER = 0; + private static final int ASSIGN_SLOT = 1; + private static final int CLEAR_FILTER = 2; + private int action; + private int terminalSlot; + private int cardSlot; + private int filterSlot; + private int inventorySlot; + private GenericStack filter; + + public static CCardConfig setFilter(int terminal, int card, int filterSlot, GenericStack stack) { + CCardConfig packet = base(SET_FILTER, terminal, card, filterSlot); + packet.filter = stack; + return packet; + } + + public static CCardConfig assignSlot(int terminal, int card, int inventorySlot, int filterSlot) { + CCardConfig packet = base(ASSIGN_SLOT, terminal, card, filterSlot); + packet.inventorySlot = inventorySlot; + return packet; + } + + public static CCardConfig clearFilter(int terminal, int card, int filterSlot) { + return base(CLEAR_FILTER, terminal, card, filterSlot); + } + + private static CCardConfig base(int action, int terminal, int card, int filter) { + CCardConfig packet = new CCardConfig(); + packet.action = action; + packet.terminalSlot = terminal; + packet.cardSlot = card; + packet.filterSlot = filter; + return packet; + } + + @Override + protected void read(ByteBuf buf) { + this.action = buf.readByte(); + this.terminalSlot = buf.readByte(); + this.cardSlot = buf.readByte(); + this.filterSlot = buf.readByte(); + this.inventorySlot = buf.readByte(); + this.filter = readStack(buf); + } + + @Override + protected void write(ByteBuf buf) { + buf.writeByte(action); + buf.writeByte(terminalSlot); + buf.writeByte(cardSlot); + buf.writeByte(filterSlot); + buf.writeByte(inventorySlot); + GenericStack.writeBuffer(filter, new PacketBuffer(buf)); + } + + private static GenericStack readStack(ByteBuf buf) { + return GenericStack.readBuffer(new PacketBuffer(buf)); + } + + @Override + public void handleServer(EntityPlayerMP player) { + if (!(player.openContainer instanceof ContainerImportExportCard container) + || container.terminalSlot != this.terminalSlot + || container.cardSlot != this.cardSlot + || !container.canInteractWith(player)) { + return; + } + int max = container.getFilterSize(); + if (action == ASSIGN_SLOT) { + if (filterSlot < -1 || filterSlot >= max) { + return; + } + container.setSelectedSlot(inventorySlot, filterSlot + 1); + } else if (filterSlot >= 0 && filterSlot < max) { + if (action == SET_FILTER) { + container.getFilters().setStack(filterSlot, filter); + } else if (action == CLEAR_FILTER) { + container.getFilters().setStack(filterSlot, null); + } + } + container.detectAndSendChanges(); + } +} diff --git a/src/main/java/com/formlesslab/ae2additions/network/CCloseImportExportCard.java b/src/main/java/com/formlesslab/ae2additions/network/CCloseImportExportCard.java new file mode 100644 index 0000000..d931c4c --- /dev/null +++ b/src/main/java/com/formlesslab/ae2additions/network/CCloseImportExportCard.java @@ -0,0 +1,27 @@ +package com.formlesslab.ae2additions.network; + +import ae2.core.gui.locator.ItemGuiHostLocator; +import com.formlesslab.ae2additions.container.ContainerImportExportCard; +import com.formlesslab.ae2additions.util.WirelessTerminalAccess; +import com.formlesslab.ae2additions.network.base.ModServerboundPacket; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; + +public final class CCloseImportExportCard extends ModServerboundPacket { + @Override + public void handleServer(EntityPlayerMP player) { + if (!(player.openContainer instanceof ContainerImportExportCard cardContainer)) { + return; + } + int slot = cardContainer.terminalSlot; + ItemStack terminal = WirelessTerminalAccess.getTerminal(player, slot); + if (!WirelessTerminalAccess.isWirelessTerminal(terminal)) { + return; + } + try { + terminal.getItem().getClass().getMethod("openFromInventory", EntityPlayer.class, ItemGuiHostLocator.class).invoke(terminal.getItem(), player, WirelessTerminalAccess.locatorForSlot(slot)); + } catch (ReflectiveOperationException ignored) { + } + } +} diff --git a/src/main/java/com/formlesslab/ae2additions/network/COpenImportExportCard.java b/src/main/java/com/formlesslab/ae2additions/network/COpenImportExportCard.java new file mode 100644 index 0000000..3eb4832 --- /dev/null +++ b/src/main/java/com/formlesslab/ae2additions/network/COpenImportExportCard.java @@ -0,0 +1,60 @@ +package com.formlesslab.ae2additions.network; + +import ae2.api.upgrades.IUpgradeInventory; +import com.formlesslab.ae2additions.AppliedAdditions; +import com.formlesslab.ae2additions.init.ModGuiHandler; +import com.formlesslab.ae2additions.init.ModContent; +import com.formlesslab.ae2additions.util.WirelessTerminalAccess; +import com.formlesslab.ae2additions.network.base.ModServerboundPacket; +import io.netty.buffer.ByteBuf; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.item.ItemStack; + +public final class COpenImportExportCard extends ModServerboundPacket { + private int terminalSlot; + private int cardSlot; + private boolean export; + + public COpenImportExportCard() { + } + + public COpenImportExportCard(int terminalSlot, int cardSlot, boolean export) { + this.terminalSlot = terminalSlot; + this.cardSlot = cardSlot; + this.export = export; + } + + @Override + protected void read(ByteBuf buf) { + terminalSlot = buf.readByte(); + cardSlot = buf.readByte(); + export = buf.readBoolean(); + } + + @Override + protected void write(ByteBuf buf) { + buf.writeByte(terminalSlot); + buf.writeByte(cardSlot); + buf.writeBoolean(export); + } + + @Override + public void handleServer(EntityPlayerMP player) { + ItemStack terminal = WirelessTerminalAccess.getTerminal(player, terminalSlot); + if (!WirelessTerminalAccess.isWirelessTerminal(terminal)) { + return; + } + IUpgradeInventory upgrades = WirelessTerminalAccess.getUpgrades(terminal); + if (upgrades == null) { + return; + } + if (cardSlot < 0 || cardSlot >= upgrades.size()) { + return; + } + ItemStack card = upgrades.getStackInSlot(cardSlot); + if (card.getItem() != (export ? ModContent.EXPORT_CARD : ModContent.IMPORT_CARD)) { + return; + } + player.openGui(AppliedAdditions.INSTANCE, ModGuiHandler.IMPORT_EXPORT_CARD, player.world, terminalSlot, cardSlot, export ? 1 : 0); + } +} diff --git a/src/main/java/com/formlesslab/ae2additions/util/ImportExportCardConfig.java b/src/main/java/com/formlesslab/ae2additions/util/ImportExportCardConfig.java new file mode 100644 index 0000000..6b0bca5 --- /dev/null +++ b/src/main/java/com/formlesslab/ae2additions/util/ImportExportCardConfig.java @@ -0,0 +1,83 @@ +package com.formlesslab.ae2additions.util; + +import ae2.api.stacks.GenericStack; +import ae2.api.upgrades.IUpgradeInventory; +import ae2.core.definitions.AEItems; +import ae2.util.ConfigInventory; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; + +public final class ImportExportCardConfig { + public static final String FILTER_CONFIG = "filter_config"; + public static final String SELECTED_SLOTS = "selected_inventory_slots"; + public static final int IMPORT_UPGRADE_SLOTS = 4; + public static final int EXPORT_UPGRADE_SLOTS = 5; + public static final int BASE_FILTER_SLOTS = 18; + public static final int FILTER_SLOTS_PER_CAPACITY = 9; + public static final int MAX_CAPACITY_CARDS = 3; + public static final int SLOT_COUNT = 40; + + private ImportExportCardConfig() { + } + + public static int getUpgradeSlotCount(boolean export) { + return export ? EXPORT_UPGRADE_SLOTS : IMPORT_UPGRADE_SLOTS; + } + + public static int getCapacityCardCount(IUpgradeInventory upgrades) { + return Math.min(MAX_CAPACITY_CARDS, upgrades.getInstalledUpgrades(AEItems.CAPACITY_CARD.item())); + } + + public static int getFilterSize(IUpgradeInventory upgrades) { + return BASE_FILTER_SLOTS + getCapacityCardCount(upgrades) * FILTER_SLOTS_PER_CAPACITY; + } + + public static int[] getSelectedSlots(ItemStack card) { + int[] result = new int[SLOT_COUNT]; + NBTTagCompound tag = card.getTagCompound(); + + if (tag != null && tag.hasKey(SELECTED_SLOTS)) { + int[] stored = tag.getIntArray(SELECTED_SLOTS); + System.arraycopy(stored, 0, result, 0, Math.min(stored.length, result.length)); + } + return result; + } + + public static void setSelectedSlots(ItemStack card, int[] slots) { + NBTTagCompound tag = card.getTagCompound(); + if (tag == null) { + tag = new NBTTagCompound(); + } + int[] normalized = new int[SLOT_COUNT]; + System.arraycopy(slots, 0, normalized, 0, Math.min(slots.length, normalized.length)); + tag.setIntArray(SELECTED_SLOTS, normalized); + card.setTagCompound(tag); + } + + public static ConfigInventory readFilters(ItemStack card, int size) { + ConfigInventory inventory = ConfigInventory.configTypes(size).build(); + NBTTagCompound tag = card.getTagCompound(); + if (tag != null && tag.hasKey(FILTER_CONFIG)) { + inventory.readFromChildTag(tag, FILTER_CONFIG); + } + return inventory; + } + + public static void writeFilters(ItemStack card, ConfigInventory inventory) { + NBTTagCompound tag = card.getTagCompound(); + if (tag == null) { + tag = new NBTTagCompound(); + } + inventory.writeToChildTag(tag, FILTER_CONFIG); + card.setTagCompound(tag); + } + + public static void setFilter(ItemStack card, int slot, GenericStack filter, int size) { + ConfigInventory inventory = readFilters(card, size); + if (slot < 0 || slot >= inventory.size()) { + throw new IndexOutOfBoundsException("filter slot " + slot); + } + inventory.setStack(slot, filter); + writeFilters(card, inventory); + } +} diff --git a/src/main/java/com/formlesslab/ae2additions/util/ImportExportCardHandler.java b/src/main/java/com/formlesslab/ae2additions/util/ImportExportCardHandler.java new file mode 100644 index 0000000..9d27bfc --- /dev/null +++ b/src/main/java/com/formlesslab/ae2additions/util/ImportExportCardHandler.java @@ -0,0 +1,372 @@ +package com.formlesslab.ae2additions.util; + +import baubles.api.BaublesApi; +import baubles.api.cap.IBaublesItemHandler; +import ae2.api.config.Actionable; +import ae2.api.config.FuzzyMode; +import ae2.api.config.Settings; +import ae2.api.networking.IGrid; +import ae2.api.networking.crafting.CalculationStrategy; +import ae2.api.networking.crafting.ICraftingPlan; +import ae2.api.networking.crafting.ICraftingService; +import ae2.api.networking.security.IActionSource; +import ae2.api.networking.energy.IEnergySource; +import ae2.api.storage.StorageHelper; +import ae2.api.config.PowerMultiplier; +import ae2.api.stacks.AEFluidKey; +import ae2.api.stacks.AEItemKey; +import ae2.api.stacks.AEKey; +import ae2.api.stacks.GenericStack; +import ae2.api.storage.StorageCells; +import ae2.api.storage.cells.StorageCell; +import ae2.api.storage.cells.ICellWorkbenchItem; +import ae2.api.upgrades.IUpgradeInventory; +import ae2.api.upgrades.UpgradeInventories; +import ae2.api.util.IConfigManager; +import ae2.core.definitions.AEItems; +import ae2.me.helpers.PlayerSource; +import ae2.util.ConfigInventory; +import com.formlesslab.ae2additions.init.ModContent; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.EntityEquipmentSlot; +import net.minecraft.item.ItemArmor; +import net.minecraft.item.ItemStack; +import net.minecraftforge.fluids.capability.CapabilityFluidHandler; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.capability.IFluidHandler; +import net.minecraftforge.fluids.capability.IFluidTankProperties; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.common.gameevent.TickEvent; +import net.minecraftforge.fml.common.Loader; + +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +import it.unimi.dsi.fastutil.objects.Object2LongMap; + +public final class ImportExportCardHandler { + private static final Map> CRAFTING_JOBS = new ConcurrentHashMap<>(); + + private ImportExportCardHandler() { + } + + @SubscribeEvent + public static void onPlayerTick(TickEvent.PlayerTickEvent event) { + + if (event.phase != TickEvent.Phase.END || event.player.world.isRemote || !(event.player instanceof EntityPlayerMP player) || event.player.ticksExisted % 10 != 0) { + return; + } + + for (int i = 0; i < player.inventory.getSizeInventory(); i++) { + tickTerminal(player, player.inventory.getStackInSlot(i)); + } + + tickBaubles(player); + player.inventory.markDirty(); + player.openContainer.detectAndSendChanges(); + } + + private static void tickBaubles(EntityPlayerMP player) { + if (!Loader.isModLoaded("baubles")) { + return; + } + IBaublesItemHandler baubles = BaublesApi.getBaublesHandler(player); + if (baubles == null) { + return; + } + for (int i = 0; i < baubles.getSlots(); i++) { + tickTerminal(player, baubles.getStackInSlot(i)); + } + } + + private static void tickTerminal(EntityPlayerMP player, ItemStack terminal) { + if (!WirelessTerminalAccess.isWirelessTerminal(terminal)) { + return; + } + + IGrid grid = WirelessTerminalAccess.getLinkedGrid(terminal, player); + IUpgradeInventory terminalUpgrades = WirelessTerminalAccess.getUpgrades(terminal); + + if (grid == null || grid.getStorageService() == null || terminalUpgrades == null) { + return; + } + for (int slot = 0; slot < terminalUpgrades.size(); slot++) { + ItemStack card = terminalUpgrades.getStackInSlot(slot); + if (card.getItem() == ModContent.IMPORT_CARD) { + tickCard(player, terminal, card, grid, true); + } else if (card.getItem() == ModContent.EXPORT_CARD) { + tickCard(player, terminal, card, grid, false); + } + } + } + + private static void tickCard(EntityPlayerMP player, ItemStack terminal, ItemStack card, IGrid grid, boolean importing) { + int upgradeSlots = ImportExportCardConfig.getUpgradeSlotCount(!importing); + IUpgradeInventory upgrades = UpgradeInventories.forItem(card, upgradeSlots, null); + int filterSlots = ImportExportCardConfig.getFilterSize(upgrades); + ConfigInventory filters = ImportExportCardConfig.readFilters(card, filterSlots); + int[] selected = ImportExportCardConfig.getSelectedSlots(card); + FuzzyMode fuzzyMode = FuzzyMode.IGNORE_ALL; + boolean fuzzy = upgrades.isInstalled(AEItems.FUZZY_CARD.item()); + + if (fuzzy) { + IConfigManager manager = IConfigManager.builder(card).registerSetting(Settings.FUZZY_MODE, FuzzyMode.IGNORE_ALL).build(); + fuzzyMode = manager.getSetting(Settings.FUZZY_MODE); + } + + boolean invert = upgrades.isInstalled(AEItems.INVERTER_CARD.item()); + IActionSource source = new PlayerSource(player); + IEnergySource energy = new TerminalEnergySource(terminal.getItem(), player, terminal); + + for (int slot = 0; slot < selected.length && slot < player.inventory.getSizeInventory(); slot++) { + int selectedFilter = selected[slot]; + if (selectedFilter < 1 || selectedFilter > filters.size()) { + continue; + } + ItemStack stack = player.inventory.getStackInSlot(slot); + if (stack == terminal || (importing && stack.isEmpty())) { + continue; + } + GenericStack configured = filters.getStack(selectedFilter - 1); + if (importing) { + importFromSlot(player, grid, energy, source, slot, stack, filters, fuzzyMode, fuzzy, invert); + } else if (configured != null) { + exportToSlot(player, grid, energy, source, slot, stack, configured.what(), upgrades, fuzzy, fuzzyMode); + } + } + } + + private static boolean passes(AEKey key, ConfigInventory filters, FuzzyMode mode, boolean fuzzy, boolean invert) { + boolean match = false; + for (int i = 0; i < filters.size(); i++) { + GenericStack configured = filters.getStack(i); + if (configured != null && (fuzzy ? key.fuzzyEquals(configured.what(), mode) : key.equals(configured.what()))) { + match = true; + break; + } + } + return invert != match; + } + + private static void importFromSlot(EntityPlayerMP player, IGrid grid, IEnergySource energy, IActionSource source, int slot, ItemStack stack, ConfigInventory filters, FuzzyMode mode, boolean fuzzy, boolean invert) { + AEItemKey key = AEItemKey.of(stack); + if (key != null && passes(key, filters, mode, fuzzy, invert)) { + long inserted = StorageHelper.poweredInsert(energy, grid.getStorageService().getInventory(), key, stack.getCount(), source, Actionable.MODULATE); + if (inserted > 0) { + stack.shrink((int) Math.min(inserted, stack.getCount())); + if (stack.isEmpty()) { + player.inventory.setInventorySlotContents(slot, ItemStack.EMPTY); + } + } + } + ItemStack current = player.inventory.getStackInSlot(slot); + if (current.isEmpty()) { + return; + } + IFluidHandler handler = current.getCapability(CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY, null); + if (handler == null) { + return; + } + for (IFluidTankProperties tank : handler.getTankProperties()) { + FluidStack fluid = tank.getContents(); + if (fluid == null || fluid.amount <= 0) { + continue; + } + AEFluidKey fluidKey = AEFluidKey.of(fluid); + if (fluidKey == null || !passes(fluidKey, filters, mode, fuzzy, invert)) { + continue; + } + FluidStack drained = handler.drain(fluid.amount, true); + if (drained == null || drained.amount <= 0) { + continue; + } + long inserted = StorageHelper.poweredInsert(energy, grid.getStorageService().getInventory(), fluidKey, drained.amount, source, Actionable.MODULATE); + if (inserted < drained.amount) { + handler.fill(new FluidStack(drained, drained.amount - (int) inserted), true); + } + } + } + + private static void exportToSlot(EntityPlayerMP player, IGrid grid, IEnergySource energy, IActionSource source, int inventorySlot, ItemStack stack, AEKey filter, IUpgradeInventory upgrades, boolean fuzzy, FuzzyMode fuzzyMode) { + AEKey exportKey = filter; + if (fuzzy) { + for (Object2LongMap.Entry entry : grid.getStorageService().getCachedInventory().findFuzzy(filter, fuzzyMode)) { + exportKey = entry.getKey(); + break; + } + } + if (isArmorSlot(inventorySlot) + && (!(exportKey instanceof AEItemKey itemKey) || !isValidArmorForSlot(itemKey.toStack(1), inventorySlot))) { + return; + } + if (exportKey instanceof AEFluidKey) { + exportFluid(player, grid, energy, source, stack, (AEFluidKey) exportKey, upgrades); + return; + } + if (stack.getCount() == 1 && stack.getItem() instanceof ICellWorkbenchItem + && exportToCell(player, grid, energy, source, stack, exportKey, upgrades)) { + return; + } + if (!(exportKey instanceof AEItemKey itemKey)) { + return; + } + if (stack.isEmpty()) { + int amount = upgrades.isInstalled(AEItems.SPEED_CARD.item()) ? itemKey.getMaxStackSize() : 1; + long extracted = extractOrRequestCrafting(player, grid, energy, source, itemKey, amount, upgrades); + if (extracted > 0) { + player.inventory.setInventorySlotContents(inventorySlot, itemKey.toStack((int) extracted)); + } + return; + } + if (!itemKey.matches(stack)) { + return; + } + int missing = stack.getMaxStackSize() - stack.getCount(); + if (missing <= 0) { + return; + } + int amount = upgrades.isInstalled(AEItems.SPEED_CARD.item()) ? missing : 1; + long extracted = extractOrRequestCrafting(player, grid, energy, source, exportKey, amount, upgrades); + if (extracted > 0) { + stack.grow((int) extracted); + } + } + + private static boolean isArmorSlot(int inventorySlot) { + return inventorySlot >= 36 && inventorySlot <= 39; + } + + private static boolean isValidArmorForSlot(ItemStack stack, int inventorySlot) { + if (!(stack.getItem() instanceof ItemArmor armor)) { + return false; + } + EntityEquipmentSlot expectedSlot = switch (inventorySlot) { + case 36 -> EntityEquipmentSlot.FEET; + case 37 -> EntityEquipmentSlot.LEGS; + case 38 -> EntityEquipmentSlot.CHEST; + case 39 -> EntityEquipmentSlot.HEAD; + default -> null; + }; + return armor.armorType == expectedSlot; + } + + private static long extractOrRequestCrafting(EntityPlayerMP player, IGrid grid, IEnergySource energy, IActionSource source, AEKey key, long amount, IUpgradeInventory upgrades) { + long available = StorageHelper.poweredExtraction(energy, grid.getStorageService().getInventory(), key, amount, source, Actionable.SIMULATE); + if (available <= 0) { + requestCrafting(player, grid, source, key, amount, upgrades); + return 0; + } + return StorageHelper.poweredExtraction(energy, grid.getStorageService().getInventory(), key, Math.min(amount, available), source, Actionable.MODULATE); + } + + private static boolean exportToCell(EntityPlayerMP player, IGrid grid, IEnergySource energy, IActionSource source, ItemStack stack, AEKey key, IUpgradeInventory upgrades) { + StorageCell cell = StorageCells.getCellInventory(stack, null); + if (cell == null) { + return false; + } + long limit = (long) key.getAmountPerOperation() * (upgrades.isInstalled(AEItems.SPEED_CARD.item()) ? 64 : 1); + long requested = cell.insert(key, limit, Actionable.SIMULATE, source); + if (requested <= 0) { + return false; + } + long available = StorageHelper.poweredExtraction(energy, grid.getStorageService().getInventory(), key, requested, source, Actionable.SIMULATE); + if (available <= 0) { + requestCrafting(player, grid, source, key, requested, upgrades); + return true; + } + long accepted = cell.insert(key, available, Actionable.MODULATE, source); + if (accepted <= 0) { + return true; + } + cell.persist(); + long extracted = StorageHelper.poweredExtraction(energy, grid.getStorageService().getInventory(), key, accepted, source, Actionable.MODULATE); + if (extracted < accepted) { + StorageHelper.poweredInsert(energy, grid.getStorageService().getInventory(), key, accepted - extracted, source, Actionable.MODULATE); + } + cell.persist(); + player.inventory.markDirty(); + return true; + } + + private static void exportFluid(EntityPlayerMP player, IGrid grid, IEnergySource energy, IActionSource source, ItemStack stack, AEFluidKey key, IUpgradeInventory upgrades) { + IFluidHandler handler = stack.getCapability(CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY, null); + if (handler == null) { + return; + } + int amount = (upgrades.isInstalled(AEItems.SPEED_CARD.item()) ? 64 : 1) * AEFluidKey.AMOUNT_BUCKET; + FluidStack requested = key.toStack(amount); + int accepted = handler.fill(requested, false); + if (accepted <= 0) { + return; + } + long extracted = extractOrRequestCrafting(player, grid, energy, source, key, accepted, upgrades); + if (extracted > 0) { + int filled = handler.fill(key.toStack((int) extracted), true); + if (filled < extracted) { + StorageHelper.poweredInsert(energy, grid.getStorageService().getInventory(), key, extracted - filled, source, Actionable.MODULATE); + } + } + } + + private static void requestCrafting(EntityPlayerMP player, IGrid grid, IActionSource source, AEKey key, long amount, IUpgradeInventory upgrades) { + if (!upgrades.isInstalled(AEItems.CRAFTING_CARD.item())) { + return; + } + ICraftingService crafting = grid.getCraftingService(); + if (crafting == null || !crafting.isCraftable(key) || crafting.isRequesting(key)) { + return; + } + Future current = CRAFTING_JOBS.get(player.getUniqueID()); + if (current != null) { + if (!current.isDone()) { + return; + } + try { + ICraftingPlan plan = current.get(); + if (plan != null) { + crafting.submitJob(plan, null, null, false, source); + } + } catch (InterruptedException | ExecutionException ignored) { + Thread.currentThread().interrupt(); + } finally { + CRAFTING_JOBS.remove(player.getUniqueID()); + } + return; + } + CRAFTING_JOBS.put(player.getUniqueID(), crafting.beginCraftingCalculation(player.world, () -> source, key, amount, CalculationStrategy.CRAFT_LESS)); + } + + private static final class TerminalEnergySource implements IEnergySource { + private final Object item; + private final EntityPlayerMP player; + private final ItemStack terminal; + + private TerminalEnergySource(Object item, EntityPlayerMP player, ItemStack terminal) { + this.item = item; + this.player = player; + this.terminal = terminal; + } + + @Override + public double extractAEPower(double amount, Actionable mode, PowerMultiplier multiplier) { + double requested = multiplier.multiply(amount); + try { + boolean hasPower = (boolean) item.getClass().getMethod("hasPower", EntityPlayer.class, double.class, ItemStack.class).invoke(item, player, requested, terminal); + if (!hasPower) { + return 0; + } + if (mode == Actionable.SIMULATE) { + return requested; + } + boolean used = (boolean) item.getClass().getMethod("usePower", EntityPlayer.class, double.class, ItemStack.class).invoke(item, player, requested, terminal); + return used ? requested : 0; + } catch (ReflectiveOperationException ignored) { + return 0; + } + } + } +} diff --git a/src/main/java/com/formlesslab/ae2additions/util/WirelessTerminalAccess.java b/src/main/java/com/formlesslab/ae2additions/util/WirelessTerminalAccess.java new file mode 100644 index 0000000..bb054cd --- /dev/null +++ b/src/main/java/com/formlesslab/ae2additions/util/WirelessTerminalAccess.java @@ -0,0 +1,48 @@ +package com.formlesslab.ae2additions.util; + +import ae2.api.networking.IGrid; +import ae2.api.upgrades.IUpgradeInventory; +import ae2.core.gui.locator.GuiHostLocators; +import ae2.core.gui.locator.ItemGuiHostLocator; +import ae2.items.tools.powered.WirelessTerminalItem; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.item.ItemStack; + +public final class WirelessTerminalAccess { + + public static ItemGuiHostLocator locatorForSlot(int encodedSlot) { + return encodedSlot >= 0 + ? GuiHostLocators.forInventorySlot(encodedSlot) + : GuiHostLocators.forBaubleSlot(-encodedSlot - 1); + } + + public static ItemStack getTerminal(EntityPlayer player, int encodedSlot) { + return locatorForSlot(encodedSlot).locateItem(player); + } + + public static boolean isWirelessTerminal(ItemStack stack) { + return !stack.isEmpty() && stack.getItem() instanceof WirelessTerminalItem; + } + + public static IUpgradeInventory getUpgrades(ItemStack stack) { + if (!isWirelessTerminal(stack)) { + return null; + } + var item = stack.getItem(); + if (item instanceof WirelessTerminalItem terminal) { + return terminal.getUpgrades(stack); + } + return null; + } + + public static IGrid getLinkedGrid(ItemStack stack, EntityPlayerMP player) { + if (!isWirelessTerminal(stack)) { + return null; + } + if (stack.getItem() instanceof WirelessTerminalItem terminal) { + return terminal.getLinkedGrid(stack, player.world, null); + } + return null; + } +} diff --git a/src/main/resources/assets/ae2/screens/export_card_0.json b/src/main/resources/assets/ae2/screens/export_card_0.json new file mode 100644 index 0000000..19cd354 --- /dev/null +++ b/src/main/resources/assets/ae2/screens/export_card_0.json @@ -0,0 +1,41 @@ +{ + "$schema": "schema.json", + "includes": ["common/common.json", "common/player_inventory.json"], + "background": { + "texture": "ae2additions:textures/guis/upgrade_0.png", + "srcRect": [0, 0, 191, 163] + }, + "slots": { + "AE2ADDITIONS_CARD_CONFIG": { + "left": 23, + "top": 29, + "grid": "BREAK_AFTER_9COLS" + }, + "PLAYER_INVENTORY": { + "left": 23, + "bottom": 84, + "grid": "BREAK_AFTER_9COLS" + }, + "PLAYER_HOTBAR": { + "left": 23, + "bottom": 26, + "grid": "HORIZONTAL" + } + }, + "widgets": { + "upgrades": { "left": 191, "top": 0, "height": 90 }, + "verticalToolbar": { "left": 18, "top": 1 }, + "back": { "left": 167, "top": -5, "width": 20, "height": 20 } + }, + "text": { + "dialog_title": { + "text": { "translate": "item.ae2additions.export_card.name" }, + "position": { "left": 23, "top": 6 } + }, + "player_inventory_title": { + "text": { "translate": "container.inventory" }, + "position": { "left": 23, "bottom": 95 } + } + } +} + diff --git a/src/main/resources/assets/ae2/screens/export_card_1.json b/src/main/resources/assets/ae2/screens/export_card_1.json new file mode 100644 index 0000000..89f9227 --- /dev/null +++ b/src/main/resources/assets/ae2/screens/export_card_1.json @@ -0,0 +1,41 @@ +{ + "$schema": "schema.json", + "includes": ["common/common.json", "common/player_inventory.json"], + "background": { + "texture": "ae2additions:textures/guis/upgrade_1.png", + "srcRect": [0, 0, 191, 181] + }, + "slots": { + "AE2ADDITIONS_CARD_CONFIG": { + "left": 23, + "top": 29, + "grid": "BREAK_AFTER_9COLS" + }, + "PLAYER_INVENTORY": { + "left": 23, + "bottom": 84, + "grid": "BREAK_AFTER_9COLS" + }, + "PLAYER_HOTBAR": { + "left": 23, + "bottom": 26, + "grid": "HORIZONTAL" + } + }, + "widgets": { + "upgrades": { "left": 191, "top": 0, "height": 90 }, + "verticalToolbar": { "left": 18, "top": 1 }, + "back": { "left": 167, "top": -5, "width": 20, "height": 20 } + }, + "text": { + "dialog_title": { + "text": { "translate": "item.ae2additions.export_card.name" }, + "position": { "left": 23, "top": 6 } + }, + "player_inventory_title": { + "text": { "translate": "container.inventory" }, + "position": { "left": 23, "bottom": 95 } + } + } +} + diff --git a/src/main/resources/assets/ae2/screens/export_card_2.json b/src/main/resources/assets/ae2/screens/export_card_2.json new file mode 100644 index 0000000..5b0ce11 --- /dev/null +++ b/src/main/resources/assets/ae2/screens/export_card_2.json @@ -0,0 +1,41 @@ +{ + "$schema": "schema.json", + "includes": ["common/common.json", "common/player_inventory.json"], + "background": { + "texture": "ae2additions:textures/guis/upgrade_2.png", + "srcRect": [0, 0, 191, 199] + }, + "slots": { + "AE2ADDITIONS_CARD_CONFIG": { + "left": 23, + "top": 29, + "grid": "BREAK_AFTER_9COLS" + }, + "PLAYER_INVENTORY": { + "left": 23, + "bottom": 84, + "grid": "BREAK_AFTER_9COLS" + }, + "PLAYER_HOTBAR": { + "left": 23, + "bottom": 26, + "grid": "HORIZONTAL" + } + }, + "widgets": { + "upgrades": { "left": 191, "top": 0, "height": 90 }, + "verticalToolbar": { "left": 18, "top": 1 }, + "back": { "left": 167, "top": -5, "width": 20, "height": 20 } + }, + "text": { + "dialog_title": { + "text": { "translate": "item.ae2additions.export_card.name" }, + "position": { "left": 23, "top": 6 } + }, + "player_inventory_title": { + "text": { "translate": "container.inventory" }, + "position": { "left": 23, "bottom": 95 } + } + } +} + diff --git a/src/main/resources/assets/ae2/screens/export_card_3.json b/src/main/resources/assets/ae2/screens/export_card_3.json new file mode 100644 index 0000000..d125147 --- /dev/null +++ b/src/main/resources/assets/ae2/screens/export_card_3.json @@ -0,0 +1,41 @@ +{ + "$schema": "schema.json", + "includes": ["common/common.json", "common/player_inventory.json"], + "background": { + "texture": "ae2additions:textures/guis/upgrade_3.png", + "srcRect": [0, 0, 191, 217] + }, + "slots": { + "AE2ADDITIONS_CARD_CONFIG": { + "left": 23, + "top": 29, + "grid": "BREAK_AFTER_9COLS" + }, + "PLAYER_INVENTORY": { + "left": 23, + "bottom": 84, + "grid": "BREAK_AFTER_9COLS" + }, + "PLAYER_HOTBAR": { + "left": 23, + "bottom": 26, + "grid": "HORIZONTAL" + } + }, + "widgets": { + "upgrades": { "left": 191, "top": 0, "height": 90 }, + "verticalToolbar": { "left": 18, "top": 1 }, + "back": { "left": 167, "top": -5, "width": 20, "height": 20 } + }, + "text": { + "dialog_title": { + "text": { "translate": "item.ae2additions.export_card.name" }, + "position": { "left": 23, "top": 6 } + }, + "player_inventory_title": { + "text": { "translate": "container.inventory" }, + "position": { "left": 23, "bottom": 95 } + } + } +} + diff --git a/src/main/resources/assets/ae2/screens/import_card_0.json b/src/main/resources/assets/ae2/screens/import_card_0.json new file mode 100644 index 0000000..febb282 --- /dev/null +++ b/src/main/resources/assets/ae2/screens/import_card_0.json @@ -0,0 +1,41 @@ +{ + "$schema": "schema.json", + "includes": ["common/common.json", "common/player_inventory.json"], + "background": { + "texture": "ae2additions:textures/guis/upgrade_0.png", + "srcRect": [0, 0, 191, 163] + }, + "slots": { + "AE2ADDITIONS_CARD_CONFIG": { + "left": 23, + "top": 29, + "grid": "BREAK_AFTER_9COLS" + }, + "PLAYER_INVENTORY": { + "left": 23, + "bottom": 84, + "grid": "BREAK_AFTER_9COLS" + }, + "PLAYER_HOTBAR": { + "left": 23, + "bottom": 26, + "grid": "HORIZONTAL" + } + }, + "widgets": { + "upgrades": { "left": 191, "top": 0, "height": 90 }, + "verticalToolbar": { "left": 18, "top": 1 }, + "back": { "left": 167, "top": -5, "width": 20, "height": 20 } + }, + "text": { + "dialog_title": { + "text": { "translate": "item.ae2additions.import_card.name" }, + "position": { "left": 23, "top": 6 } + }, + "player_inventory_title": { + "text": { "translate": "container.inventory" }, + "position": { "left": 23, "bottom": 95 } + } + } +} + diff --git a/src/main/resources/assets/ae2/screens/import_card_1.json b/src/main/resources/assets/ae2/screens/import_card_1.json new file mode 100644 index 0000000..d9a55c3 --- /dev/null +++ b/src/main/resources/assets/ae2/screens/import_card_1.json @@ -0,0 +1,41 @@ +{ + "$schema": "schema.json", + "includes": ["common/common.json", "common/player_inventory.json"], + "background": { + "texture": "ae2additions:textures/guis/upgrade_1.png", + "srcRect": [0, 0, 191, 181] + }, + "slots": { + "AE2ADDITIONS_CARD_CONFIG": { + "left": 23, + "top": 29, + "grid": "BREAK_AFTER_9COLS" + }, + "PLAYER_INVENTORY": { + "left": 23, + "bottom": 84, + "grid": "BREAK_AFTER_9COLS" + }, + "PLAYER_HOTBAR": { + "left": 23, + "bottom": 26, + "grid": "HORIZONTAL" + } + }, + "widgets": { + "upgrades": { "left": 191, "top": 0, "height": 90 }, + "verticalToolbar": { "left": 18, "top": 1 }, + "back": { "left": 167, "top": -5, "width": 20, "height": 20 } + }, + "text": { + "dialog_title": { + "text": { "translate": "item.ae2additions.import_card.name" }, + "position": { "left": 23, "top": 6 } + }, + "player_inventory_title": { + "text": { "translate": "container.inventory" }, + "position": { "left": 23, "bottom": 95 } + } + } +} + diff --git a/src/main/resources/assets/ae2/screens/import_card_2.json b/src/main/resources/assets/ae2/screens/import_card_2.json new file mode 100644 index 0000000..157d02d --- /dev/null +++ b/src/main/resources/assets/ae2/screens/import_card_2.json @@ -0,0 +1,41 @@ +{ + "$schema": "schema.json", + "includes": ["common/common.json", "common/player_inventory.json"], + "background": { + "texture": "ae2additions:textures/guis/upgrade_2.png", + "srcRect": [0, 0, 191, 199] + }, + "slots": { + "AE2ADDITIONS_CARD_CONFIG": { + "left": 23, + "top": 29, + "grid": "BREAK_AFTER_9COLS" + }, + "PLAYER_INVENTORY": { + "left": 23, + "bottom": 84, + "grid": "BREAK_AFTER_9COLS" + }, + "PLAYER_HOTBAR": { + "left": 23, + "bottom": 26, + "grid": "HORIZONTAL" + } + }, + "widgets": { + "upgrades": { "left": 191, "top": 0, "height": 90 }, + "verticalToolbar": { "left": 18, "top": 1 }, + "back": { "left": 167, "top": -5, "width": 20, "height": 20 } + }, + "text": { + "dialog_title": { + "text": { "translate": "item.ae2additions.import_card.name" }, + "position": { "left": 23, "top": 6 } + }, + "player_inventory_title": { + "text": { "translate": "container.inventory" }, + "position": { "left": 23, "bottom": 95 } + } + } +} + diff --git a/src/main/resources/assets/ae2/screens/import_card_3.json b/src/main/resources/assets/ae2/screens/import_card_3.json new file mode 100644 index 0000000..fd2d99c --- /dev/null +++ b/src/main/resources/assets/ae2/screens/import_card_3.json @@ -0,0 +1,41 @@ +{ + "$schema": "schema.json", + "includes": ["common/common.json", "common/player_inventory.json"], + "background": { + "texture": "ae2additions:textures/guis/upgrade_3.png", + "srcRect": [0, 0, 191, 217] + }, + "slots": { + "AE2ADDITIONS_CARD_CONFIG": { + "left": 23, + "top": 29, + "grid": "BREAK_AFTER_9COLS" + }, + "PLAYER_INVENTORY": { + "left": 23, + "bottom": 84, + "grid": "BREAK_AFTER_9COLS" + }, + "PLAYER_HOTBAR": { + "left": 23, + "bottom": 26, + "grid": "HORIZONTAL" + } + }, + "widgets": { + "upgrades": { "left": 191, "top": 0, "height": 90 }, + "verticalToolbar": { "left": 18, "top": 1 }, + "back": { "left": 167, "top": -5, "width": 20, "height": 20 } + }, + "text": { + "dialog_title": { + "text": { "translate": "item.ae2additions.import_card.name" }, + "position": { "left": 23, "top": 6 } + }, + "player_inventory_title": { + "text": { "translate": "container.inventory" }, + "position": { "left": 23, "bottom": 95 } + } + } +} + diff --git a/src/main/resources/assets/ae2additions/lang/en_us.lang b/src/main/resources/assets/ae2additions/lang/en_us.lang index 5fd1233..b729d47 100644 --- a/src/main/resources/assets/ae2additions/lang/en_us.lang +++ b/src/main/resources/assets/ae2additions/lang/en_us.lang @@ -32,6 +32,8 @@ item.ae2additions.assembler_matrix_glass.name=Assembler Matrix Glass item.ae2additions.assembler_matrix_pattern.name=Assembler Matrix Pattern Core item.ae2additions.assembler_matrix_crafter.name=Assembler Matrix Craft Core item.ae2additions.assembler_matrix_speed.name=Assembler Matrix Speed Core +item.ae2additions.import_card.name=Import Card +item.ae2additions.export_card.name=Export Card tooltip.ae2additions.quantum_unit.01=Interior filler block for Quantum Computers. tooltip.ae2additions.quantum_unit.02=Provides no extra storage or parallelism. @@ -99,6 +101,11 @@ gui.ae2additions.status.remote_error=Remote Error gui.ae2additions.status.remote_error.desc=The bound connector is missing or not working. gui.ae2additions.status.no_power=No Power gui.ae2additions.status.no_power.desc=This Wireless Connector needs more power. +gui.ae2additions.card.mass_select.inventory=Configure all main inventory slots +gui.ae2additions.card.mass_select.hotbar=Configure all hotbar slots +gui.ae2additions.card.mass_select.export=Left-click: cycle each slot's filter number +gui.ae2additions.card.mass_select.import=Left-click: toggle each slot +gui.ae2additions.card.mass_select.clear=Right-click: clear all slots in this group chat.ae2additions.highlight=Remote block: %s, %s, %s wireless.tooltip=Bound to %s, %s, %s diff --git a/src/main/resources/assets/ae2additions/lang/zh_cn.lang b/src/main/resources/assets/ae2additions/lang/zh_cn.lang index ec8a616..3e52878 100644 --- a/src/main/resources/assets/ae2additions/lang/zh_cn.lang +++ b/src/main/resources/assets/ae2additions/lang/zh_cn.lang @@ -99,6 +99,11 @@ gui.ae2additions.status.remote_error=远端错误 gui.ae2additions.status.remote_error.desc=绑定的连接器丢失或无法工作。 gui.ae2additions.status.no_power=无能源 gui.ae2additions.status.no_power.desc=此无线连接器需要更多能源。 +gui.ae2additions.card.mass_select.inventory=批量设置主物品栏槽位 +gui.ae2additions.card.mass_select.hotbar=批量设置快捷栏槽位 +gui.ae2additions.card.mass_select.export=左键:逐槽切换过滤器编号 +gui.ae2additions.card.mass_select.import=左键:切换各槽位的启用状态 +gui.ae2additions.card.mass_select.clear=右键:清空这组槽位的设置 chat.ae2additions.highlight=远端方块:%s, %s, %s wireless.tooltip=已绑定到 %s, %s, %s @@ -135,6 +140,9 @@ item.ae2additions.quantum_alloy_block.name=量子合金块 item.ae2additions.quantum_alloy_slab.name=量子合金台阶 item.ae2additions.quantum_alloy_stairs.name=量子合金楼梯 item.ae2additions.quantum_alloy_wall.name=量子合金墙 +item.ae2additions.import_card.name=输入卡 +item.ae2additions.export_card.name=输出卡 + fluid.quantum_infusion=量子灌注液 fluid.ae2additions.quantum_infusion=量子灌注液 gui.ae2additions.reaction_chamber=反应仓 diff --git a/src/main/resources/assets/ae2additions/models/item/export_card.json b/src/main/resources/assets/ae2additions/models/item/export_card.json new file mode 100644 index 0000000..c4eed84 --- /dev/null +++ b/src/main/resources/assets/ae2additions/models/item/export_card.json @@ -0,0 +1,4 @@ +{ + "parent": "minecraft:item/generated", + "textures": { "layer0": "ae2additions:item/export_card" } +} diff --git a/src/main/resources/assets/ae2additions/models/item/import_card.json b/src/main/resources/assets/ae2additions/models/item/import_card.json new file mode 100644 index 0000000..e32a7b4 --- /dev/null +++ b/src/main/resources/assets/ae2additions/models/item/import_card.json @@ -0,0 +1,4 @@ +{ + "parent": "minecraft:item/generated", + "textures": { "layer0": "ae2additions:item/import_card" } +} diff --git a/src/main/resources/assets/ae2additions/recipes/shaped/export_card.json b/src/main/resources/assets/ae2additions/recipes/shaped/export_card.json new file mode 100644 index 0000000..077cad8 --- /dev/null +++ b/src/main/resources/assets/ae2additions/recipes/shaped/export_card.json @@ -0,0 +1,11 @@ +{ + "type": "forge:ore_shaped", + "pattern": [" A ", "BCB", " D "], + "key": { + "A": {"item": "ae2:engineering_processor"}, + "B": {"item": "ae2:quartz_glass"}, + "C": {"item": "ae2:logic_processor"}, + "D": {"item": "minecraft:iron_ingot"} + }, + "result": {"item": "ae2additions:export_card"} +} diff --git a/src/main/resources/assets/ae2additions/recipes/shaped/import_card.json b/src/main/resources/assets/ae2additions/recipes/shaped/import_card.json new file mode 100644 index 0000000..452cd23 --- /dev/null +++ b/src/main/resources/assets/ae2additions/recipes/shaped/import_card.json @@ -0,0 +1,11 @@ +{ + "type": "forge:ore_shaped", + "pattern": [" A ", "BCB", " D "], + "key": { + "A": {"item": "ae2:calculation_processor"}, + "B": {"item": "ae2:quartz_glass"}, + "C": {"item": "ae2:logic_processor"}, + "D": {"item": "minecraft:iron_ingot"} + }, + "result": {"item": "ae2additions:import_card"} +} diff --git a/src/main/resources/assets/ae2additions/textures/guis/checkmark.png b/src/main/resources/assets/ae2additions/textures/guis/checkmark.png new file mode 100644 index 0000000..18b059d Binary files /dev/null and b/src/main/resources/assets/ae2additions/textures/guis/checkmark.png differ diff --git a/src/main/resources/assets/ae2additions/textures/guis/export_card.png b/src/main/resources/assets/ae2additions/textures/guis/export_card.png new file mode 100644 index 0000000..85b78c2 Binary files /dev/null and b/src/main/resources/assets/ae2additions/textures/guis/export_card.png differ diff --git a/src/main/resources/assets/ae2additions/textures/guis/import_card.png b/src/main/resources/assets/ae2additions/textures/guis/import_card.png new file mode 100644 index 0000000..0fe601f Binary files /dev/null and b/src/main/resources/assets/ae2additions/textures/guis/import_card.png differ diff --git a/src/main/resources/assets/ae2additions/textures/guis/mass_select.png b/src/main/resources/assets/ae2additions/textures/guis/mass_select.png new file mode 100644 index 0000000..2735b6a Binary files /dev/null and b/src/main/resources/assets/ae2additions/textures/guis/mass_select.png differ diff --git a/src/main/resources/assets/ae2additions/textures/guis/upgrade_0.png b/src/main/resources/assets/ae2additions/textures/guis/upgrade_0.png new file mode 100644 index 0000000..7c6b272 Binary files /dev/null and b/src/main/resources/assets/ae2additions/textures/guis/upgrade_0.png differ diff --git a/src/main/resources/assets/ae2additions/textures/guis/upgrade_1.png b/src/main/resources/assets/ae2additions/textures/guis/upgrade_1.png new file mode 100644 index 0000000..ebfd7d1 Binary files /dev/null and b/src/main/resources/assets/ae2additions/textures/guis/upgrade_1.png differ diff --git a/src/main/resources/assets/ae2additions/textures/guis/upgrade_2.png b/src/main/resources/assets/ae2additions/textures/guis/upgrade_2.png new file mode 100644 index 0000000..96a17f6 Binary files /dev/null and b/src/main/resources/assets/ae2additions/textures/guis/upgrade_2.png differ diff --git a/src/main/resources/assets/ae2additions/textures/guis/upgrade_3.png b/src/main/resources/assets/ae2additions/textures/guis/upgrade_3.png new file mode 100644 index 0000000..3f3be5c Binary files /dev/null and b/src/main/resources/assets/ae2additions/textures/guis/upgrade_3.png differ diff --git a/src/main/resources/assets/ae2additions/textures/guis/xmark.png b/src/main/resources/assets/ae2additions/textures/guis/xmark.png new file mode 100644 index 0000000..8959a5f Binary files /dev/null and b/src/main/resources/assets/ae2additions/textures/guis/xmark.png differ diff --git a/src/main/resources/assets/ae2additions/textures/item/export_card.png b/src/main/resources/assets/ae2additions/textures/item/export_card.png new file mode 100644 index 0000000..d001aab Binary files /dev/null and b/src/main/resources/assets/ae2additions/textures/item/export_card.png differ diff --git a/src/main/resources/assets/ae2additions/textures/item/import_card.png b/src/main/resources/assets/ae2additions/textures/item/import_card.png new file mode 100644 index 0000000..c6efdd4 Binary files /dev/null and b/src/main/resources/assets/ae2additions/textures/item/import_card.png differ