From 1f9786733bfac9995f778b29f67aacd247a70749 Mon Sep 17 00:00:00 2001 From: li_waiwai <2157995671a@gmail.com> Date: Wed, 28 Oct 2026 22:38:31 +0800 Subject: [PATCH 1/5] =?UTF-8?q?=E8=BE=93=E5=85=A5=E8=BE=93=E5=87=BA?= =?UTF-8?q?=E5=8D=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gradle/scripts/dependencies.gradle | 1 + .../ae2additions/AppliedAdditions.java | 4 + .../client/ImportExportGuiHooks.java | 184 +++++++++ .../client/gui/GuiImportExportCard.java | 294 +++++++++++++++ .../gui/widgets/ImportExportCardButton.java | 38 ++ .../container/CardPlayerSlot.java | 11 + .../container/ContainerImportExportCard.java | 208 +++++++++++ .../ae2additions/init/ModContent.java | 17 + .../ae2additions/init/ModGuiHandler.java | 10 + .../ae2additions/init/ModNetworks.java | 9 + .../ae2additions/network/CCardConfig.java | 93 +++++ .../network/CCloseImportExportCard.java | 27 ++ .../network/COpenImportExportCard.java | 60 +++ .../util/ImportExportCardConfig.java | 83 +++++ .../util/ImportExportCardHandler.java | 348 ++++++++++++++++++ .../util/WirelessTerminalAccess.java | 58 +++ .../assets/ae2/screens/export_card_0.json | 41 +++ .../assets/ae2/screens/export_card_1.json | 41 +++ .../assets/ae2/screens/export_card_2.json | 41 +++ .../assets/ae2/screens/export_card_3.json | 41 +++ .../assets/ae2/screens/import_card_0.json | 41 +++ .../assets/ae2/screens/import_card_1.json | 41 +++ .../assets/ae2/screens/import_card_2.json | 41 +++ .../assets/ae2/screens/import_card_3.json | 41 +++ .../assets/ae2additions/lang/en_us.lang | 7 + .../assets/ae2additions/lang/zh_cn.lang | 8 + .../ae2additions/models/item/export_card.json | 4 + .../ae2additions/models/item/import_card.json | 4 + .../recipes/shaped/export_card.json | 11 + .../recipes/shaped/import_card.json | 11 + .../ae2additions/textures/guis/checkmark.png | Bin 0 -> 178 bytes .../textures/guis/export_card.png | Bin 0 -> 243 bytes .../textures/guis/import_card.png | Bin 0 -> 240 bytes .../textures/guis/mass_select.png | Bin 0 -> 202 bytes .../ae2additions/textures/guis/upgrade_0.png | Bin 0 -> 1164 bytes .../ae2additions/textures/guis/upgrade_1.png | Bin 0 -> 1182 bytes .../ae2additions/textures/guis/upgrade_2.png | Bin 0 -> 1197 bytes .../ae2additions/textures/guis/upgrade_3.png | Bin 0 -> 1212 bytes .../ae2additions/textures/guis/xmark.png | Bin 0 -> 169 bytes .../textures/item/export_card.png | Bin 0 -> 512 bytes .../textures/item/import_card.png | Bin 0 -> 513 bytes 41 files changed, 1818 insertions(+) create mode 100644 src/main/java/com/formlesslab/ae2additions/client/ImportExportGuiHooks.java create mode 100644 src/main/java/com/formlesslab/ae2additions/client/gui/GuiImportExportCard.java create mode 100644 src/main/java/com/formlesslab/ae2additions/client/gui/widgets/ImportExportCardButton.java create mode 100644 src/main/java/com/formlesslab/ae2additions/container/CardPlayerSlot.java create mode 100644 src/main/java/com/formlesslab/ae2additions/container/ContainerImportExportCard.java create mode 100644 src/main/java/com/formlesslab/ae2additions/network/CCardConfig.java create mode 100644 src/main/java/com/formlesslab/ae2additions/network/CCloseImportExportCard.java create mode 100644 src/main/java/com/formlesslab/ae2additions/network/COpenImportExportCard.java create mode 100644 src/main/java/com/formlesslab/ae2additions/util/ImportExportCardConfig.java create mode 100644 src/main/java/com/formlesslab/ae2additions/util/ImportExportCardHandler.java create mode 100644 src/main/java/com/formlesslab/ae2additions/util/WirelessTerminalAccess.java create mode 100644 src/main/resources/assets/ae2/screens/export_card_0.json create mode 100644 src/main/resources/assets/ae2/screens/export_card_1.json create mode 100644 src/main/resources/assets/ae2/screens/export_card_2.json create mode 100644 src/main/resources/assets/ae2/screens/export_card_3.json create mode 100644 src/main/resources/assets/ae2/screens/import_card_0.json create mode 100644 src/main/resources/assets/ae2/screens/import_card_1.json create mode 100644 src/main/resources/assets/ae2/screens/import_card_2.json create mode 100644 src/main/resources/assets/ae2/screens/import_card_3.json create mode 100644 src/main/resources/assets/ae2additions/models/item/export_card.json create mode 100644 src/main/resources/assets/ae2additions/models/item/import_card.json create mode 100644 src/main/resources/assets/ae2additions/recipes/shaped/export_card.json create mode 100644 src/main/resources/assets/ae2additions/recipes/shaped/import_card.json create mode 100644 src/main/resources/assets/ae2additions/textures/guis/checkmark.png create mode 100644 src/main/resources/assets/ae2additions/textures/guis/export_card.png create mode 100644 src/main/resources/assets/ae2additions/textures/guis/import_card.png create mode 100644 src/main/resources/assets/ae2additions/textures/guis/mass_select.png create mode 100644 src/main/resources/assets/ae2additions/textures/guis/upgrade_0.png create mode 100644 src/main/resources/assets/ae2additions/textures/guis/upgrade_1.png create mode 100644 src/main/resources/assets/ae2additions/textures/guis/upgrade_2.png create mode 100644 src/main/resources/assets/ae2additions/textures/guis/upgrade_3.png create mode 100644 src/main/resources/assets/ae2additions/textures/guis/xmark.png create mode 100644 src/main/resources/assets/ae2additions/textures/item/export_card.png create mode 100644 src/main/resources/assets/ae2additions/textures/item/import_card.png diff --git a/gradle/scripts/dependencies.gradle b/gradle/scripts/dependencies.gradle index fb4ca39..5c93caa 100644 --- a/gradle/scripts/dependencies.gradle +++ b/gradle/scripts/dependencies.gradle @@ -44,6 +44,7 @@ dependencies { modImplementation "com.github.FormlessDragon:Applied-Energistics-2-Supergiant:v1.0.14" modImplementation "curse.maven:had-enough-items-557549:7938914" 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/GuiImportExportCard.java b/src/main/java/com/formlesslab/ae2additions/client/gui/GuiImportExportCard.java new file mode 100644 index 0000000..c8090d5 --- /dev/null +++ b/src/main/java/com/formlesslab/ae2additions/client/gui/GuiImportExportCard.java @@ -0,0 +1,294 @@ +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.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(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/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..3025117 --- /dev/null +++ b/src/main/java/com/formlesslab/ae2additions/util/ImportExportCardHandler.java @@ -0,0 +1,348 @@ +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.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 (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 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..68dbd9f --- /dev/null +++ b/src/main/java/com/formlesslab/ae2additions/util/WirelessTerminalAccess.java @@ -0,0 +1,58 @@ +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; +import net.minecraft.world.World; + +import java.util.function.Consumer; + +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; + } + try { + Object result = stack.getItem().getClass() + .getMethod("getUpgrades", ItemStack.class) + .invoke(stack.getItem(), stack); + return result instanceof IUpgradeInventory ? (IUpgradeInventory) result : null; + } catch (ReflectiveOperationException ignored) { + return null; + } + } + + public static IGrid getLinkedGrid(ItemStack stack, EntityPlayerMP player) { + if (!isWirelessTerminal(stack)) { + return null; + } + try { + Object result = stack.getItem().getClass().getMethod("getLinkedGrid", ItemStack.class, + World.class, Consumer.class) + .invoke(stack.getItem(), stack, player.world, null); + return result instanceof IGrid ? (IGrid) result : null; + } catch (ReflectiveOperationException ignored) { + 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 0000000000000000000000000000000000000000..18b059d359bfe41e7a83940b31123c7d03e8b8bf GIT binary patch literal 178 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9GG!XV7ZFl&wkP%y>Q z#WBR9H~G*1|Mtv=3w{+ag|+&)ygbIF#iKSkkHIIn^zopr0Br;}cmMzZ literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..85b78c278a8b32486f3ce5dedf502a745c111261 GIT binary patch literal 243 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9GG!XV7ZFl&wkP;jZI zi(`mI@6q6cyoVKdY~25DlHyKb6nh~OyMQ~Tv8Ro_z>YC?<4UciK@)U-^5xW+b*Ka#+%c1?iuIBMFP6iJ4Ir}d2vjj^_GSN&lU^sAQ*VU-r!j#97;Rn|= oUN)-avYbA@>F3-NZ})#>FpVr$6L!@020EC*)78&qol`;+0M6fAf&c&j literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..0fe601fd93a3176369c3a7ba78f4885d892f9d38 GIT binary patch literal 240 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9GG!XV7ZFl&wkP;il_ zi(`mI@7mzoyoU`qT2}3s(~VPLHsLPm(wo4#slmOOab0|wp(_7XTTadid=@>D480G` zj{K`;40YW)N4%>h^sUL}yN#=o=FN9;*^;|{-Q=^&r~L3>s@|k|!mhr*Q!W0k!xzmTAdNS%G|>0G|-o_}Pa(efs2<)(;dYoa5pPQdbh>7ySSKe+CDwm0y5-&H|6fVg?3o zVGw3ym^DWNC}{5K;uxY4e747ti@|{7@U36}?blmL3V&;0)%SE{P!V9Sj+38$lZxy-8q?;Kn_c~qpu?a z!^VE@KZ&eBzG#3?h^wQ0$fr-APM^6lYyOtC8;-R1F9pi5MhJxgDfW^ezu^C1z#!$# z>;@F#EbxddW?u8gU~wiO+mMloK@dns0dZzK z3s^h{$OZwvGDe0MK%c^BbXyr1fRYo~8CZa74UCKp7#Bdy1%(mo0*G1DfNT(80-DSO zRvBbz0c1gS85$UXWDgylwIM7w#1d#Ci>HfYNJZS++h_CcxQVbny!hk!U-_F1?QAP{ zeOZ)f_RGxgs*}UHMK^y59X-QQ`S;(w?9By#KUN%ioxQpOOuyb6$n@dY%lQj9FxV|ACPTPIxM6?OtLr%p@-P0r+v>yqqx0VTxNZg}4uu9Fao~0TabXsq z;fz3{;r#VuoF9Ik0*U=&`XD^x*UU4wQmq*3pS|*bc*_;6hJkS&oX(*wnZ$1A*< zfV#?iOd5)|0bLq!tg01cun(KXx%@c{^Vff7_)!*OKRI%@5Br0Ndamnwkg%~~DUd1k zY%po~)HjhW;G7uHyi*bDHvkQ8ILYvKHqQo6sO#S`{#ZY!?kwAbdc~{fQ?Kh?X0*6w z-w8Cvh9iKB`A4Xb0{4Su8VvuwRvO7e41k6tD7Kpzo}YvmhKO-<)&xjOILq!b_wm=` zk2#rg#Mas0`#*m}B~yWiHv?ZL=K)1sMvEn?4DDN39!!|VAahBm!8vNsNM4iQq&q1z T&R38$lZxy-8q?;Kn_c~qpu?a z!^VE@KZ&eBzG#3?h^wQ0$fr-APM^6lYyOtC8;-R1F9pi5MhJxgDfW^ezu^C1z#!$# z>;@F#EbxddW?u8gU~wiO+mMloK@dns0dZzK z3s^h{$OZwvGDe0MK%c^BbXyr1fRYo~8CZa74UCKp7#Bdy1%(mo0*G1DfNT(80-DSO zRvBbz0c1gS85$UXWDgylwIM7w#1d$tn5T|(D3(`1=E{wkXwJSJ&>O9 z%P%e0ShnGN%wF#6W>8}b_z#@_KT|{S!JqyO#*Et%kGnvPvZzmeGxH<^e<#pupLw3! z_aEXvDa#Q4&@#7}`@qld|9=D@D8BVfuHn-$OWp-G$s9nQ8w2By$~I-7%VQWn+*}c# z#{A*sihnJ2AWKt0&J_f^>w#KB?iEd@C5%(jdrTUPqJi$#NR3U*1!|nkxb0`J@`1X! z><5-FhB`MD;o3Va8+xr=8%!EL^-N?7NaF?u($Y2BYjRlx5{wR*?Js1xvHml|kCKr4 zC)eaIW8Sc){2|Cqz@TZE&9E(SImZOHfO&?O7*6E?#THB{ZQTkCX&<(X`EYN#H)J#X zn|fJ)Gvg20pj!Ripa9-fry6&gqd?igQ{upVA;wJ%dP1xR;^ik-CP3T_2}npPIR50@ zOr8%DU=eq~*(#muz~V!oXcX7)uxbaVkBW#5|0?S2A3hXpaE)R~m?*{=vBZ@@EOQ{a a`xxH8E!>)Dq;MFNTRdI;T-G@yGywn?7J};l literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..96a17f6c1812734ca52047cff9e69cbd50a71daf GIT binary patch literal 1197 zcmeAS@N?(olHy`uVBq!ia0y~yU<5K58911MRQ8&P5D>38$lZxy-8q?;Kn_c~qpu?a z!^VE@KZ&eBzG#3?h^wQ0$fr-APM^6lYyOtC8;-R1F9pi5MhJxgDfW^ezu^C1z#!$# z>;@F#EbxddW?u8gU~wiO+mMloK@dns0dZzK z3s^h{$OZwvGDe0MK%c^BbXyr1fRYo~8CZa74UCKp7#Bdy1%(mo0*G1DfNT(80-DSO zRvBbz0c1gS85$UXWDgylwIM7w#1d$ts;7%%NJZS+J9qQu1n{^%Jo)AMUwKO*1EI)e z;$a*6CSKp?@M@pIy!A^J)wAO3>z{qyknp$0;^Eh?D+(ZVT>#62Uq1_)7??h&%~fW& ze!g9pMWDg_r3~YaI=eXv4GeocpK~1e`uQOXBg+F{D|d!<^ZC^|6d3X^Su%l)aAaVt zn0!vK;q~*woa_wsELZifGVGu5eRp^;-v{Z+{ne&S96&>W1k`8;21XVEhlanmESTPe zgUqU7s9+L4pK{IYDMQV(N5MO`2||=Gz#ITE;LY`8f(iSJK#pcUAUvZ&ZSyuw_6Nq| z^ABz91sM;r1L{lxumK7C&H5OAPX)P4o?+gBBgQkg`dTu~-}ysiT`tfCAZI*~YncB( z{HsgDXZzFhIXCcHt8qcxdf=ya@_7+v8x^3L=O>@Dk872mWX-Vd(C;m0bs3)D|Nq#Z z@%?49?+h)%&odbw$~rULTMjg{VZOR7$W*R|_ilfKcn@q}{6qK`$kNjw*FwDYK&>J7 ziYAi}Gsk<0%M6Edfa(>d6t;r=Ai|vUS=znfZxlOFC&YgVk_hjaGv+Mz6HJhDs5m}@ zO(2aM=+cEf+G~LJE|5r=eIFQ{*R2@#$sUFH^EASnJ2(@hOS>3=wpkxDX)uTe`kW&* zCNWpJf#E2_+hCp#6M&Ip?{CEv!~9|WoVt~456V40#+2m-^Bpj%Q;oaLQK0N_!l1!k zk?9Q6juRRT|9;IlBYjNtni*=$TBvRJH$331gh)bjgc{siLE$gX?lSi989lQ}*Iu zM3K6MxgZ#7=7cTNwnQOpWAaizG8}_M7a=f0JENU$iwK)m3t{KAuq^v)H@WY8|IWuf z@0d=T9}%7q4geAAf;>F{L6!i`M^bgcXyBwn`uy$S7?C_cfvU_c%mwHp$mw-k=00~48 ztoL=45|?;v0bV5hpq_`kobW)5bT?j86BX3Ig*4A%nN$zm08FwMag9{&g#C}?kYALl!&-VotJB<+xy4V)WI{l zRVwe$pT31;Wwb21q-%9?kL}~JD>)CZ$U2?pQ>FUA^FXGvN${KRYM9fR-x#7#KUfE2 zqfIfChMJ(I$~fZ9j1f6aL*LU{_edb93YekKTFU&8u59*kKGWL4%Q%+ zm(Rwu)vdqOw?XL1Ty1yvwm`^bxd1Mf5|s`rY!EobWlV-n_FCrY-a;kVzxKyE%t3hW z6Dkwoxg3Gsx2bU_SC=1VsP&yg7tA+_fB1U0#hkX}vfNAvVBFb~#@63^{>{HdPGD7vo-jdKANq@K_PZx`069+jo^d5#KCURSi{;yq=M`@5DV`g(cF)?8~)D}MWMx!v`%)f@udE1jkICxIXA9dLo@%g z5idT(QHWZ3XVkYS&Ccez+Tw{((+n?nsUZILI%qbDnd_HncHeHQRKD^5o9`uB@5CwLdr8k#N literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..8959a5f6a30f1bcd5587a4a013dab1da1f0cb8d3 GIT binary patch literal 169 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9GG!XV7ZFl&wkP%zrl z#WBR9H~G*1|Mtv=4<>Bp4{P;tdD+HvnD=yG#w6Ys+e=an?rfN|@cyN#Hq2c8@6$jc z8aDf8*N9tmbT8P^F!7Q@isH3z%=-5eMAdJx?qKjtG{`!z!IXhv(tNqHNYfeFKx-I0 MUHx3vIVCg!05Xm^;Q#;t literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..d001aab626ab54bda43c2c9bc44f3e2e9c9248fb GIT binary patch literal 512 zcmV+b0{{JqP);^xJ5vY(;_M=m`V#P zMewiiZwMriU>DLH961O`i1n$5O(G&%?1fk=?s_xD6~SmFPKwLy?c6HKSIrFHd*6F+ zW*n_G081-t-NnDl+1dWo+i0y>T3OTY-@XRmGK#J4QQu3>qbTO{_7{s}Al>}^@F+7b zN)fhxo>43o>2x|8Z~$_T>D+A|u-iPa&ZJa)eNy1$|8X2)NNRy;*;n56?f^>7WR!_W} zG8u3IR02E1{#!7<`xPupMJo)6E5XC>z@%QS)h)2eqZNjX2VHQ6!1p~i&wk+h9)^oPsAlnm!R+ByP#Va-%uzaI#fjkJEenD zrO>~ke}m9MOSj?@2n`gZ)J;gzkfjzW&G1s{sQ6;;h73jRBwmN)#d~>*e$(A?-*>xnUj;>M8Mim@|2Rk- z7g~YsKZj&88G5~*0UUs_+c;~DCbdR0wx*Qk>w`4Eo-UF5ux(-iHhQhmLuW2tgMc}|Z%zbA>GAsTH&S{$ ztA0aDkNHBL`9dB!c+!!)cOT>Hsn600000NkvXXu0mjf DCV}VL literal 0 HcmV?d00001 From 434c2ef3d240554e06704700894d4c696c8e69d5 Mon Sep 17 00:00:00 2001 From: li_waiwai <2157995671a@gmail.com> Date: Wed, 28 Oct 2026 22:50:14 +0800 Subject: [PATCH 2/5] =?UTF-8?q?=E8=AF=B6=E5=BC=BA=E5=88=B6=E8=BE=93?= =?UTF-8?q?=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../util/ImportExportCardHandler.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/main/java/com/formlesslab/ae2additions/util/ImportExportCardHandler.java b/src/main/java/com/formlesslab/ae2additions/util/ImportExportCardHandler.java index 3025117..9d27bfc 100644 --- a/src/main/java/com/formlesslab/ae2additions/util/ImportExportCardHandler.java +++ b/src/main/java/com/formlesslab/ae2additions/util/ImportExportCardHandler.java @@ -29,6 +29,8 @@ 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; @@ -197,6 +199,10 @@ private static void exportToSlot(EntityPlayerMP player, IGrid grid, IEnergySourc 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; @@ -230,6 +236,24 @@ && exportToCell(player, grid, energy, source, stack, exportKey, upgrades)) { } } + 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) { From ea80d2b03e947235d02715c4a5d9355ae2d4ba18 Mon Sep 17 00:00:00 2001 From: li_waiwai <2157995671a@gmail.com> Date: Thu, 29 Oct 2026 00:09:21 +0800 Subject: [PATCH 3/5] fix --- gradle/scripts/dependencies.gradle | 2 +- .../ae2additions/client/gui/GuiAssemblerMatrix.java | 3 ++- .../ae2additions/client/gui/GuiImportExportCard.java | 3 ++- .../ae2additions/client/gui/GuiReactionChamber.java | 3 ++- .../formlesslab/ae2additions/client/gui/GuiWirelessHub.java | 3 ++- 5 files changed, 9 insertions(+), 5 deletions(-) diff --git a/gradle/scripts/dependencies.gradle b/gradle/scripts/dependencies.gradle index 5c93caa..a60bbd2 100644 --- a/gradle/scripts/dependencies.gradle +++ b/gradle/scripts/dependencies.gradle @@ -41,7 +41,7 @@ dependencies { compileOnly "com.cleanroommc:lwjglx:1.0.0" } - modImplementation "com.github.FormlessDragon:Applied-Energistics-2-Supergiant:v1.0.14" + modImplementation "com.github.FormlessDragon:Applied-Energistics-2-Supergiant:v1.1.0" modImplementation "curse.maven:had-enough-items-557549:7938914" modCompileOnly 'curse.maven:mouse-tweaks-unofficial-461660:5876158' modCompileOnly "curse.maven:baubles-227083:2518667" 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 index c8090d5..2f86461 100644 --- a/src/main/java/com/formlesslab/ae2additions/client/gui/GuiImportExportCard.java +++ b/src/main/java/com/formlesslab/ae2additions/client/gui/GuiImportExportCard.java @@ -25,6 +25,7 @@ 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; @@ -147,7 +148,7 @@ public void drawScreen(int mouseX, int mouseY, float partialTicks) { super.drawScreen(mouseX, mouseY, partialTicks); int group = findMassSelectGroup(mouseX, mouseY); if (group >= 0) { - drawTooltipLines(mouseX, mouseY, List.of( + 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"))); 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)); } } From a1c7e474d68d54befb0b8e8ed19767488d745966 Mon Sep 17 00:00:00 2001 From: li_waiwai <2157995671a@gmail.com> Date: Thu, 29 Oct 2026 18:25:30 +0800 Subject: [PATCH 4/5] =?UTF-8?q?=E7=8C=AA=E4=BA=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../util/WirelessTerminalAccess.java | 24 ++++++------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/src/main/java/com/formlesslab/ae2additions/util/WirelessTerminalAccess.java b/src/main/java/com/formlesslab/ae2additions/util/WirelessTerminalAccess.java index 68dbd9f..bb054cd 100644 --- a/src/main/java/com/formlesslab/ae2additions/util/WirelessTerminalAccess.java +++ b/src/main/java/com/formlesslab/ae2additions/util/WirelessTerminalAccess.java @@ -8,9 +8,6 @@ import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.item.ItemStack; -import net.minecraft.world.World; - -import java.util.function.Consumer; public final class WirelessTerminalAccess { @@ -32,27 +29,20 @@ public static IUpgradeInventory getUpgrades(ItemStack stack) { if (!isWirelessTerminal(stack)) { return null; } - try { - Object result = stack.getItem().getClass() - .getMethod("getUpgrades", ItemStack.class) - .invoke(stack.getItem(), stack); - return result instanceof IUpgradeInventory ? (IUpgradeInventory) result : null; - } catch (ReflectiveOperationException ignored) { - 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; } - try { - Object result = stack.getItem().getClass().getMethod("getLinkedGrid", ItemStack.class, - World.class, Consumer.class) - .invoke(stack.getItem(), stack, player.world, null); - return result instanceof IGrid ? (IGrid) result : null; - } catch (ReflectiveOperationException ignored) { - return null; + if (stack.getItem() instanceof WirelessTerminalItem terminal) { + return terminal.getLinkedGrid(stack, player.world, null); } + return null; } } From a869e9d2800264e5c898ad01709b02b0a6466873 Mon Sep 17 00:00:00 2001 From: li_waiwai <2157995671a@gmail.com> Date: Thu, 29 Oct 2026 18:40:12 +0800 Subject: [PATCH 5/5] =?UTF-8?q?=E6=9B=B4=E6=96=B0hei?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gradle/scripts/dependencies.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/scripts/dependencies.gradle b/gradle/scripts/dependencies.gradle index a60bbd2..f087009 100644 --- a/gradle/scripts/dependencies.gradle +++ b/gradle/scripts/dependencies.gradle @@ -42,7 +42,7 @@ dependencies { } modImplementation "com.github.FormlessDragon:Applied-Energistics-2-Supergiant:v1.1.0" - modImplementation "curse.maven:had-enough-items-557549:7938914" + 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'