diff --git a/build.gradle.kts b/build.gradle.kts index ffd55fa..50a47cf 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -13,7 +13,7 @@ plugins { // Project properties group = "alecsio.modularmachineryaddons" -version = "2.0.3" +version = "2.0.4" // Set the toolchain version to decouple the Java we run Gradle with from the Java used to compile and run the mod java { diff --git a/src/main/java/github/alecsio/mmceaddons/ModularMachineryAddons.java b/src/main/java/github/alecsio/mmceaddons/ModularMachineryAddons.java index a57f6db..38de1c6 100644 --- a/src/main/java/github/alecsio/mmceaddons/ModularMachineryAddons.java +++ b/src/main/java/github/alecsio/mmceaddons/ModularMachineryAddons.java @@ -44,13 +44,13 @@ + "after:mekanism@[1.12.2-9.8.3.390,);" , acceptedMinecraftVersions = "[1.12]", - acceptableRemoteVersions = "[2.0.3]" + acceptableRemoteVersions = "[2.0.4]" ) public class ModularMachineryAddons { public static final String MODID = "modularmachineryaddons"; public static final String NAME = "Modular Machinery: Community Edition Addons"; - public static final String VERSION = "2.0.3"; + public static final String VERSION = "2.0.4"; public static final String CLIENT_PROXY = "github.alecsio.mmceaddons.client.ClientProxy"; public static final String COMMON_PROXY = "github.alecsio.mmceaddons.CommonProxy"; diff --git a/src/main/java/github/alecsio/mmceaddons/common/hatch/vanilla/gui/ContainerSingularityItemBus.java b/src/main/java/github/alecsio/mmceaddons/common/hatch/vanilla/gui/ContainerSingularityItemBus.java index 67ccc78..00e9dbd 100644 --- a/src/main/java/github/alecsio/mmceaddons/common/hatch/vanilla/gui/ContainerSingularityItemBus.java +++ b/src/main/java/github/alecsio/mmceaddons/common/hatch/vanilla/gui/ContainerSingularityItemBus.java @@ -6,16 +6,91 @@ import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.InventoryPlayer; import net.minecraft.inventory.ClickType; +import net.minecraft.inventory.IContainerListener; +import net.minecraft.inventory.Slot; import net.minecraft.item.ItemStack; import javax.annotation.Nonnull; public class ContainerSingularityItemBus extends ContainerItemBus { + private static final int PLAYER_SLOT_COUNT = 36; + private static final int COUNT_PROPERTY_BASE = 0x4000; + private final int machineSlotCount; + private final boolean[] changedMachineSlots; + private final int[] pendingCountLow; + public ContainerSingularityItemBus(TileItemBus owner, EntityPlayer opening) { super(owner, opening); + this.machineSlotCount = owner.getSize().getSlotCount(); + this.changedMachineSlots = new boolean[this.machineSlotCount]; + this.pendingCountLow = new int[this.machineSlotCount]; } + @Override + public void detectAndSendChanges() { + if (this.listeners.isEmpty()) { + super.detectAndSendChanges(); + return; + } + for (int machineSlot = 0; machineSlot < this.machineSlotCount; machineSlot++) { + int containerSlot = PLAYER_SLOT_COUNT + machineSlot; + this.changedMachineSlots[machineSlot] = !ItemStack.areItemStacksEqual(this.inventoryItemStacks.get(containerSlot), this.inventorySlots.get(containerSlot).getStack()); + } + super.detectAndSendChanges(); + for (int machineSlot = 0; machineSlot < this.machineSlotCount; machineSlot++) { + if (!this.changedMachineSlots[machineSlot]) { + continue; + } + this.changedMachineSlots[machineSlot] = false; + ItemStack stack = this.inventorySlots.get(PLAYER_SLOT_COUNT + machineSlot).getStack(); + int count = stack.getCount(); + if (count <= Byte.MAX_VALUE) { + continue; + } + int property = COUNT_PROPERTY_BASE + machineSlot * 2; + for (IContainerListener listener : this.listeners) { + listener.sendWindowProperty(this, property, count & 0xFFFF); + listener.sendWindowProperty(this, property + 1, count >>> 16); + } + } + } + + @Override + public void updateProgressBar(int id, int data) { + int property = id - COUNT_PROPERTY_BASE; + if (property < 0 || property >= this.machineSlotCount * 2) { + super.updateProgressBar(id, data); + return; + } + int machineSlot = property / 2; + if ((property & 1) == 0) { + this.pendingCountLow[machineSlot] = data & 0xFFFF; + return; + } + int count = this.pendingCountLow[machineSlot] | (data & 0xFFFF) << 16; + if (count <= Byte.MAX_VALUE) { + return; + } + Slot slot = this.inventorySlots.get(PLAYER_SLOT_COUNT + machineSlot); + if (!(slot instanceof SingularitySlotItemHandler)) { + return; + } + ItemStack stack = slot.getStack(); + if (stack != ItemStack.EMPTY) { + stack.setCount(count); + } + } + + int findRenderedMachineSlot(ItemStack stack, int x, int y) { + for (int machineSlot = 0; machineSlot < this.machineSlotCount; machineSlot++) { + Slot slot = this.inventorySlots.get(PLAYER_SLOT_COUNT + machineSlot); + if (slot instanceof SingularitySlotItemHandler && slot.xPos == x && slot.yPos == y && slot.getStack() == stack) { + return machineSlot; + } + } + return -1; + } @Nonnull @Override @@ -41,4 +116,41 @@ public ItemStack slotClick(int slotId, int dragType, @Nonnull ClickType clickTyp } return super.slotClick(slotId, dragType, clickTypeIn, player); } -} + + @Override + protected boolean mergeItemStack(ItemStack stack, int startIndex, int endIndex, boolean reverseDirection) { + if (startIndex != PLAYER_SLOT_COUNT || endIndex != this.inventorySlots.size()) { + return super.mergeItemStack(stack, startIndex, endIndex, reverseDirection); + } + boolean changed = mergeIntoSingularitySlots(stack, startIndex, endIndex, reverseDirection, false); + if (!stack.isEmpty()) { + changed |= mergeIntoSingularitySlots(stack, startIndex, endIndex, reverseDirection, true); + } + return changed; + } + + private boolean mergeIntoSingularitySlots(ItemStack stack, int startIndex, int endIndex, boolean reverseDirection, boolean emptySlots) { + boolean changed = false; + int index = reverseDirection ? endIndex - 1 : startIndex; + while (!stack.isEmpty() && index >= startIndex && index < endIndex) { + Slot slot = this.inventorySlots.get(index); + ItemStack existing = slot.getStack(); + if (slot instanceof SingularitySlotItemHandler singularitySlot + && existing.isEmpty() == emptySlots + && singularitySlot.getSlotStackLimit() > 0 + && (emptySlots || existing.getCount() < singularitySlot.getSlotStackLimit() + && existing.isItemEqual(stack) + && ItemStack.areItemStackTagsEqual(existing, stack))) { + int previousCount = stack.getCount(); + ItemStack remainder = singularitySlot.insertItem(stack); + int remainingCount = remainder.isEmpty() ? 0 : remainder.getCount(); + if (remainingCount < previousCount) { + stack.setCount(remainingCount); + changed = true; + } + } + index += reverseDirection ? -1 : 1; + } + return changed; + } +} \ No newline at end of file diff --git a/src/main/java/github/alecsio/mmceaddons/common/hatch/vanilla/gui/GuiContainerSingularityItemBus.java b/src/main/java/github/alecsio/mmceaddons/common/hatch/vanilla/gui/GuiContainerSingularityItemBus.java index ee10f85..d0831eb 100644 --- a/src/main/java/github/alecsio/mmceaddons/common/hatch/vanilla/gui/GuiContainerSingularityItemBus.java +++ b/src/main/java/github/alecsio/mmceaddons/common/hatch/vanilla/gui/GuiContainerSingularityItemBus.java @@ -1,26 +1,54 @@ package github.alecsio.mmceaddons.common.hatch.vanilla.gui; +import github.alecsio.mmceaddons.util.SingularitySlotItemHandler; import hellfirepvp.modularmachinery.client.gui.GuiContainerBase; import hellfirepvp.modularmachinery.common.block.prop.ItemBusSize; -import hellfirepvp.modularmachinery.common.container.ContainerItemBus; import hellfirepvp.modularmachinery.common.tiles.base.TileItemBus; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.FontRenderer; import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.RenderItem; +import net.minecraft.client.resources.I18n; +import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.Slot; +import net.minecraft.item.ItemStack; import net.minecraft.util.ResourceLocation; +import net.minecraft.util.text.TextFormatting; -public class GuiContainerSingularityItemBus extends GuiContainerBase { +import javax.annotation.Nullable; +import java.text.NumberFormat; +import java.util.List; +import java.util.Locale; + +public class GuiContainerSingularityItemBus extends GuiContainerBase { + private static CompactCountRenderItem compactCountRenderItem; + private static final NumberFormat EXACT_COUNT_FORMAT = NumberFormat.getIntegerInstance(Locale.US); public GuiContainerSingularityItemBus(TileItemBus itemBus, EntityPlayer opening) { super(new ContainerSingularityItemBus(itemBus, opening)); } + @Override + public void initGui() { + super.initGui(); + RenderItem vanillaRenderItem = this.mc.getRenderItem(); + if (compactCountRenderItem == null || !compactCountRenderItem.wraps(vanillaRenderItem)) { + compactCountRenderItem = new CompactCountRenderItem(this.mc, vanillaRenderItem);} + this.itemRender = compactCountRenderItem; + } + @Override + public void drawScreen(int mouseX, int mouseY, float partialTicks) { + compactCountRenderItem.setContainer(this.container); + try {super.drawScreen(mouseX, mouseY, partialTicks); + } finally {compactCountRenderItem.clearContainer(this.container); + } + } private ResourceLocation getTextureInventory() { ItemBusSize size = this.container.getOwner().getSize(); return new ResourceLocation("modularmachinery", "textures/gui/inventory_" + size.name().toLowerCase() + ".png"); } - protected void setWidthHeight() { - } - + protected void setWidthHeight() {} protected void drawGuiContainerBackgroundLayer(float partialTicks, int mouseX, int mouseY) { GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); this.mc.getTextureManager().bindTexture(this.getTextureInventory()); @@ -28,4 +56,118 @@ protected void drawGuiContainerBackgroundLayer(float partialTicks, int mouseX, i int j = (this.height - this.ySize) / 2; this.drawTexturedModalRect(i, j, 0, 0, this.xSize, this.ySize); } -} + + static String formatCompactCount(int count) { + if (count < 1_000) {return Integer.toString(count);} + int divisor; + char suffix; + if (count < 1_000_000) { + divisor = 1_000; + suffix = 'k'; + } else if (count < 1_000_000_000) { + divisor = 1_000_000; + suffix = 'M'; + } else { + divisor = 1_000_000_000; + suffix = 'B'; + } + int whole = count / divisor; + if (whole >= 100) {return Integer.toString(whole) + suffix;} + int decimal = count % divisor / (divisor / 10); + if (decimal == 0) {return Integer.toString(whole) + suffix;} + return Integer.toString(whole) + '.' + decimal + suffix; + } + + private static final class CompactCountRenderItem extends RenderItem { + + private static final int CACHE_SIZE = ItemBusSize.LUDICROUS.getSlotCount(); + private final RenderItem delegate; + private final int[] cachedCounts = new int[CACHE_SIZE]; + private final String[] cachedText = new String[CACHE_SIZE]; + private ContainerSingularityItemBus container; + + private CompactCountRenderItem(Minecraft minecraft, RenderItem delegate) { + super(minecraft.getTextureManager(), delegate.getItemModelMesher().getModelManager(), minecraft.getItemColors()); + this.delegate = delegate; + } + + private boolean wraps(RenderItem renderItem) {return this.delegate == renderItem;} + private void setContainer(ContainerSingularityItemBus container) {this.container = container;} + private void clearContainer(ContainerSingularityItemBus container) {if (this.container == container) { + this.container = null;} + } + + @Override + public void renderItemAndEffectIntoGUI(@Nullable EntityLivingBase entity, ItemStack stack, int x, int y) { + float previousZLevel = this.delegate.zLevel; + this.delegate.zLevel = this.zLevel; + try { + this.delegate.renderItemAndEffectIntoGUI(entity, stack, x, y); + } finally { + this.delegate.zLevel = previousZLevel; + } + } + + @Override + public void renderItemOverlayIntoGUI(FontRenderer fontRenderer, ItemStack stack, int x, int y, @Nullable String text) { + float previousZLevel = this.delegate.zLevel; + this.delegate.zLevel = this.zLevel; + try { + if (text == null && stack.getCount() >= 1_000 && this.container != null) { + int machineSlot = this.container.findRenderedMachineSlot(stack, x, y); + if (machineSlot >= 0) { + this.delegate.renderItemOverlayIntoGUI(fontRenderer, stack, x, y, ""); + drawCompactCount(fontRenderer, getCachedText(machineSlot, stack.getCount()), x, y); + return; + } + } + this.delegate.renderItemOverlayIntoGUI(fontRenderer, stack, x, y, text); + } finally { + this.delegate.zLevel = previousZLevel; + } + } + + private String getCachedText(int machineSlot, int count) { + if (this.cachedText[machineSlot] == null || this.cachedCounts[machineSlot] != count) { + this.cachedCounts[machineSlot] = count; + this.cachedText[machineSlot] = formatCompactCount(count); + } + return this.cachedText[machineSlot]; + } + + private static void drawCompactCount(FontRenderer fontRenderer, String text, int x, int y) { + final float scale = 0.5F; + final float inverseScale = 1.0F / scale; + final int offset = -1; + boolean unicodeFlag = fontRenderer.getUnicodeFlag(); + fontRenderer.setUnicodeFlag(false); + GlStateManager.disableLighting(); + GlStateManager.disableDepth(); + GlStateManager.disableBlend(); + GlStateManager.pushMatrix(); + try { + GlStateManager.scale(scale, scale, scale); + int drawX = (int) ((x + offset + 16.0F - fontRenderer.getStringWidth(text) * scale) * inverseScale); + int drawY = (int) ((y + offset + 16.0F - 7.0F * scale) * inverseScale); + fontRenderer.drawStringWithShadow(text, drawX, drawY, 0xFFFFFF); + } finally { + GlStateManager.popMatrix(); + GlStateManager.enableLighting(); + GlStateManager.enableDepth(); + GlStateManager.enableBlend(); + fontRenderer.setUnicodeFlag(unicodeFlag); + } + } + } + @Override + public List getItemToolTip(ItemStack stack) { + List tooltip = super.getItemToolTip(stack); + Slot hoveredSlot = this.getSlotUnderMouse(); + if (stack.getCount() >= 1_000 + && hoveredSlot instanceof SingularitySlotItemHandler + && hoveredSlot.getStack() == stack) { + tooltip.add(TextFormatting.GRAY + I18n.format("tooltip.modularmachineryaddons.singularity.stored", EXACT_COUNT_FORMAT.format(stack.getCount()))); + } + return tooltip; + } +} \ No newline at end of file diff --git a/src/main/java/github/alecsio/mmceaddons/common/mixin/ContainerMixin.java b/src/main/java/github/alecsio/mmceaddons/common/mixin/ContainerMixin.java index b2a4dd3..d4d0b9d 100644 --- a/src/main/java/github/alecsio/mmceaddons/common/mixin/ContainerMixin.java +++ b/src/main/java/github/alecsio/mmceaddons/common/mixin/ContainerMixin.java @@ -1,5 +1,6 @@ package github.alecsio.mmceaddons.common.mixin; +import github.alecsio.mmceaddons.common.hatch.vanilla.gui.ContainerSingularityItemBus; import github.alecsio.mmceaddons.util.SingularitySlotItemHandler; import net.minecraftforge.items.IItemHandler; import net.minecraftforge.items.SlotItemHandler; @@ -12,6 +13,8 @@ public class ContainerMixin { @Redirect(method = "addInventorySlots(Lnet/minecraftforge/items/IItemHandlerModifiable;Lhellfirepvp/modularmachinery/common/block/prop/ItemBusSize;)V", at = @At(value = "NEW", target = "(Lnet/minecraftforge/items/IItemHandler;III)Lnet/minecraftforge/items/SlotItemHandler;"), remap=false) public SlotItemHandler replaceSlotItemHandlers(IItemHandler itemHandler, int index, int xPosition, int yPosition) { - return new SingularitySlotItemHandler(itemHandler, index, xPosition, yPosition); + if ((Object) this instanceof ContainerSingularityItemBus) { + return new SingularitySlotItemHandler(itemHandler, index, xPosition, yPosition);} + return new SlotItemHandler(itemHandler, index, xPosition, yPosition); } -} +} \ No newline at end of file diff --git a/src/main/java/github/alecsio/mmceaddons/util/SingularitySlotItemHandler.java b/src/main/java/github/alecsio/mmceaddons/util/SingularitySlotItemHandler.java index 1fb5317..c90448c 100644 --- a/src/main/java/github/alecsio/mmceaddons/util/SingularitySlotItemHandler.java +++ b/src/main/java/github/alecsio/mmceaddons/util/SingularitySlotItemHandler.java @@ -7,10 +7,16 @@ import javax.annotation.Nonnull; public class SingularitySlotItemHandler extends SlotItemHandler { + private final int handlerSlot; public SingularitySlotItemHandler(IItemHandler itemHandler, int index, int xPosition, int yPosition) { super(itemHandler, index, xPosition, yPosition); + this.handlerSlot = index; } + @Nonnull + public ItemStack insertItem(@Nonnull ItemStack stack) { + return this.getItemHandler().insertItem(this.handlerSlot, stack, false); + } @Override public int getItemStackLimit(@Nonnull ItemStack stack) { return Integer.MAX_VALUE; diff --git a/src/main/resources/assets/modularmachineryaddons/lang/en_us.lang b/src/main/resources/assets/modularmachineryaddons/lang/en_us.lang index 239106d..33c2c4e 100644 --- a/src/main/resources/assets/modularmachineryaddons/lang/en_us.lang +++ b/src/main/resources/assets/modularmachineryaddons/lang/en_us.lang @@ -43,6 +43,7 @@ item.modularmachineryaddons.itemadvancedmachinedisassembler.name=Advanced Machin item.modularmachineryaddons.itemradiationsponge.name=Radiation Sponge item.modularmachineryaddons.iteminactiveradiationsponge.name=Inactive Radiation Sponge tooltip.modularmachineryaddons.cheese.lie=The cheese is §llie +tooltip.modularmachineryaddons.singularity.stored=Stored: %s commands.mmcea.getcacheinfo.usage=/getScrubbedChunksCacheInfo diff --git a/src/main/resources/assets/modularmachineryaddons/lang/zh_cn.lang b/src/main/resources/assets/modularmachineryaddons/lang/zh_cn.lang index bb5968d..75cde39 100644 --- a/src/main/resources/assets/modularmachineryaddons/lang/zh_cn.lang +++ b/src/main/resources/assets/modularmachineryaddons/lang/zh_cn.lang @@ -43,6 +43,7 @@ item.modularmachineryaddons.itemadvancedmachinedisassembler.name=高级机械拆 item.modularmachineryaddons.itemradiationsponge.name=辐射海绵 item.modularmachineryaddons.iteminactiveradiationsponge.name=未激活的辐射海绵 tooltip.modularmachineryaddons.cheese.lie=奶酪是个§l谎言 +tooltip.modularmachineryaddons.singularity.stored=已储存:%s commands.mmcea.getcacheinfo.usage=/getScrubbedChunksCacheInfo