diff --git a/src/main/java/de/ellpeck/prettypipes/Registry.java b/src/main/java/de/ellpeck/prettypipes/Registry.java index 9326054..acfbd62 100644 --- a/src/main/java/de/ellpeck/prettypipes/Registry.java +++ b/src/main/java/de/ellpeck/prettypipes/Registry.java @@ -23,6 +23,10 @@ import de.ellpeck.prettypipes.pipe.modules.retrieval.RetrievalModuleItem; import de.ellpeck.prettypipes.pipe.modules.stacksize.StackSizeModuleContainer; import de.ellpeck.prettypipes.pipe.modules.stacksize.StackSizeModuleGui; import de.ellpeck.prettypipes.pipe.modules.stacksize.StackSizeModuleItem; +import de.ellpeck.prettypipes.terminal.ItemTerminalBlock; +import de.ellpeck.prettypipes.terminal.ItemTerminalTileEntity; +import de.ellpeck.prettypipes.terminal.containers.ItemTerminalContainer; +import de.ellpeck.prettypipes.terminal.containers.ItemTerminalGui; import net.minecraft.block.Block; import net.minecraft.client.gui.ScreenManager; import net.minecraft.client.renderer.RenderType; @@ -77,10 +81,14 @@ public final class Registry { public static Block pipeBlock; public static TileEntityType pipeTileEntity; + public static ContainerType pipeContainer; + + public static Block itemTerminalBlock; + public static TileEntityType itemTerminalTileEntity; + public static ContainerType itemTerminalContainer; public static EntityType pipeFrameEntity; - public static ContainerType pipeContainer; public static ContainerType extractionModuleContainer; public static ContainerType filterModuleContainer; public static ContainerType retrievalModuleContainer; @@ -89,7 +97,8 @@ public final class Registry { @SubscribeEvent public static void registerBlocks(RegistryEvent.Register event) { event.getRegistry().registerAll( - pipeBlock = new PipeBlock().setRegistryName("pipe") + pipeBlock = new PipeBlock().setRegistryName("pipe"), + itemTerminalBlock = new ItemTerminalBlock().setRegistryName("item_terminal") ); } @@ -118,7 +127,8 @@ public final class Registry { @SubscribeEvent public static void registerTiles(RegistryEvent.Register> event) { event.getRegistry().registerAll( - pipeTileEntity = (TileEntityType) TileEntityType.Builder.create(PipeTileEntity::new, pipeBlock).build(null).setRegistryName("pipe") + pipeTileEntity = (TileEntityType) TileEntityType.Builder.create(PipeTileEntity::new, pipeBlock).build(null).setRegistryName("pipe"), + itemTerminalTileEntity = (TileEntityType) TileEntityType.Builder.create(ItemTerminalTileEntity::new, itemTerminalBlock).build(null).setRegistryName("item_terminal") ); } @@ -132,8 +142,8 @@ public final class Registry { @SubscribeEvent public static void registerContainers(RegistryEvent.Register> event) { event.getRegistry().registerAll( - // this needs to be registered manually since it doesn't send the module slot pipeContainer = (ContainerType) IForgeContainerType.create((windowId, inv, data) -> new MainPipeContainer(pipeContainer, windowId, inv.player, data.readBlockPos())).setRegistryName("pipe"), + itemTerminalContainer = (ContainerType) IForgeContainerType.create((windowId, inv, data) -> new ItemTerminalContainer(itemTerminalContainer, windowId, inv.player, data.readBlockPos())).setRegistryName("item_terminal"), extractionModuleContainer = createPipeContainer("extraction_module"), filterModuleContainer = createPipeContainer("filter_module"), retrievalModuleContainer = createPipeContainer("retrieval_module"), @@ -180,6 +190,7 @@ public final class Registry { RenderingRegistry.registerEntityRenderingHandler(pipeFrameEntity, PipeFrameRenderer::new); ScreenManager.registerFactory(pipeContainer, MainPipeGui::new); + ScreenManager.registerFactory(itemTerminalContainer, ItemTerminalGui::new); ScreenManager.registerFactory(extractionModuleContainer, ExtractionModuleGui::new); ScreenManager.registerFactory(filterModuleContainer, FilterModuleGui::new); ScreenManager.registerFactory(retrievalModuleContainer, RetrievalModuleGui::new); diff --git a/src/main/java/de/ellpeck/prettypipes/Utility.java b/src/main/java/de/ellpeck/prettypipes/Utility.java index 8ca4814..0efc7c3 100644 --- a/src/main/java/de/ellpeck/prettypipes/Utility.java +++ b/src/main/java/de/ellpeck/prettypipes/Utility.java @@ -1,8 +1,12 @@ package de.ellpeck.prettypipes; +import de.ellpeck.prettypipes.items.IModule; import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.resources.I18n; +import net.minecraft.entity.player.PlayerEntity; import net.minecraft.inventory.InventoryHelper; +import net.minecraft.inventory.container.Container; +import net.minecraft.inventory.container.Slot; import net.minecraft.item.ItemStack; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.Direction; @@ -10,8 +14,11 @@ import net.minecraft.util.math.BlockPos; import net.minecraft.util.text.*; import net.minecraft.world.World; import net.minecraftforge.items.IItemHandler; +import org.apache.commons.lang3.tuple.Pair; import java.util.List; +import java.util.function.Function; +import java.util.stream.IntStream; public final class Utility { @@ -43,4 +50,51 @@ public final class Utility { tooltip.add(new TranslationTextComponent("info." + PrettyPipes.ID + ".shift").setStyle(new Style().setColor(TextFormatting.DARK_GRAY))); } } + + public static ItemStack transferStackInSlot(Container container, IMergeItemStack merge, PlayerEntity player, int slotIndex, Function> predicate) { + int inventoryStart = (int) container.inventorySlots.stream().filter(slot -> slot.inventory != player.inventory).count(); + int inventoryEnd = inventoryStart + 26; + int hotbarStart = inventoryEnd + 1; + int hotbarEnd = hotbarStart + 8; + + Slot slot = container.inventorySlots.get(slotIndex); + if (slot != null && slot.getHasStack()) { + ItemStack newStack = slot.getStack(); + ItemStack currentStack = newStack.copy(); + + if (slotIndex >= inventoryStart) { + // shift into this container here + // mergeItemStack with the slots that newStack should go into + // return an empty stack if mergeItemStack fails + Pair slots = predicate.apply(newStack); + if (slots != null) { + if (!merge.mergeItemStack(newStack, slots.getLeft(), slots.getRight(), false)) + return ItemStack.EMPTY; + } + // end custom code + else if (slotIndex >= inventoryStart && slotIndex <= inventoryEnd) { + if (!merge.mergeItemStack(newStack, hotbarStart, hotbarEnd + 1, false)) + return ItemStack.EMPTY; + } else if (slotIndex >= inventoryEnd + 1 && slotIndex < hotbarEnd + 1 && !merge.mergeItemStack(newStack, inventoryStart, inventoryEnd + 1, false)) { + return ItemStack.EMPTY; + } + } else if (!merge.mergeItemStack(newStack, inventoryStart, hotbarEnd + 1, false)) { + return ItemStack.EMPTY; + } + if (newStack.isEmpty()) { + slot.putStack(ItemStack.EMPTY); + } else { + slot.onSlotChanged(); + } + if (newStack.getCount() == currentStack.getCount()) + return ItemStack.EMPTY; + slot.onTake(player, newStack); + return currentStack; + } + return ItemStack.EMPTY; + } + + public interface IMergeItemStack { + boolean mergeItemStack(ItemStack stack, int startIndex, int endIndex, boolean reverseDirection); + } } diff --git a/src/main/java/de/ellpeck/prettypipes/entities/PipeFrameEntity.java b/src/main/java/de/ellpeck/prettypipes/entities/PipeFrameEntity.java index cce301b..47dd5ee 100644 --- a/src/main/java/de/ellpeck/prettypipes/entities/PipeFrameEntity.java +++ b/src/main/java/de/ellpeck/prettypipes/entities/PipeFrameEntity.java @@ -68,7 +68,7 @@ public class PipeFrameEntity extends ItemFrameEntity implements IEntityAdditiona ItemStack stack = this.getDisplayedItem(); if (!stack.isEmpty()) { List items = network.getOrderedNetworkItems(node); - int amount = items.stream().mapToInt(i -> i.getItemAmount(stack)).sum(); + int amount = items.stream().mapToInt(i -> i.getItemAmount(stack, false)).sum(); this.dataManager.set(AMOUNT, amount); return; } diff --git a/src/main/java/de/ellpeck/prettypipes/misc/EquatableItemStack.java b/src/main/java/de/ellpeck/prettypipes/misc/EquatableItemStack.java new file mode 100644 index 0000000..8584a6e --- /dev/null +++ b/src/main/java/de/ellpeck/prettypipes/misc/EquatableItemStack.java @@ -0,0 +1,27 @@ +package de.ellpeck.prettypipes.misc; + +import net.minecraft.item.ItemStack; + +import java.util.Objects; + +public class EquatableItemStack { + + public final ItemStack stack; + + public EquatableItemStack(ItemStack stack) { + this.stack = stack; + } + + public boolean equals(Object o) { + if (o instanceof EquatableItemStack) { + ItemStack other = ((EquatableItemStack) o).stack; + return ItemStack.areItemsEqual(this.stack, other) && ItemStack.areItemStackTagsEqual(this.stack, other); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hash(this.stack.getItem(), this.stack.getTag()); + } +} diff --git a/src/main/java/de/ellpeck/prettypipes/misc/SlotFilter.java b/src/main/java/de/ellpeck/prettypipes/misc/FilterSlot.java similarity index 86% rename from src/main/java/de/ellpeck/prettypipes/misc/SlotFilter.java rename to src/main/java/de/ellpeck/prettypipes/misc/FilterSlot.java index 0d63220..64389ee 100644 --- a/src/main/java/de/ellpeck/prettypipes/misc/SlotFilter.java +++ b/src/main/java/de/ellpeck/prettypipes/misc/FilterSlot.java @@ -7,16 +7,16 @@ import net.minecraft.item.ItemStack; import net.minecraftforge.items.IItemHandler; import net.minecraftforge.items.SlotItemHandler; -public class SlotFilter extends SlotItemHandler { - public SlotFilter(IItemHandler itemHandler, int index, int xPosition, int yPosition) { +public class FilterSlot extends SlotItemHandler { + public FilterSlot(IItemHandler itemHandler, int index, int xPosition, int yPosition) { super(itemHandler, index, xPosition, yPosition); } public static boolean checkFilter(Container container, int slotId, PlayerEntity player) { if (slotId >= 0 && slotId < container.inventorySlots.size()) { Slot slot = container.getSlot(slotId); - if (slot instanceof SlotFilter) { - ((SlotFilter) slot).slotClick(player); + if (slot instanceof FilterSlot) { + ((FilterSlot) slot).slotClick(player); return true; } } diff --git a/src/main/java/de/ellpeck/prettypipes/misc/ItemFilter.java b/src/main/java/de/ellpeck/prettypipes/misc/ItemFilter.java index 3ed9181..db7ac75 100644 --- a/src/main/java/de/ellpeck/prettypipes/misc/ItemFilter.java +++ b/src/main/java/de/ellpeck/prettypipes/misc/ItemFilter.java @@ -1,7 +1,6 @@ package de.ellpeck.prettypipes.misc; import de.ellpeck.prettypipes.PrettyPipes; -import de.ellpeck.prettypipes.items.IModule; import de.ellpeck.prettypipes.packets.PacketButton; import de.ellpeck.prettypipes.pipe.PipeTileEntity; import de.ellpeck.prettypipes.pipe.modules.FilterModifierModule; @@ -25,7 +24,6 @@ import java.util.ArrayList; import java.util.List; import java.util.function.Supplier; import java.util.stream.Collectors; -import java.util.stream.Stream; public class ItemFilter extends ItemStackHandler { @@ -47,7 +45,7 @@ public class ItemFilter extends ItemStackHandler { public List getSlots(int x, int y) { List slots = new ArrayList<>(); for (int i = 0; i < this.getSlots(); i++) - slots.add(new SlotFilter(this, i, x + i % 9 * 18, y + i / 9 * 18)); + slots.add(new FilterSlot(this, i, x + i % 9 * 18, y + i / 9 * 18)); return slots; } diff --git a/src/main/java/de/ellpeck/prettypipes/misc/ItemTerminalWidget.java b/src/main/java/de/ellpeck/prettypipes/misc/ItemTerminalWidget.java new file mode 100644 index 0000000..d2912b5 --- /dev/null +++ b/src/main/java/de/ellpeck/prettypipes/misc/ItemTerminalWidget.java @@ -0,0 +1,84 @@ +package de.ellpeck.prettypipes.misc; + +import com.mojang.blaze3d.systems.RenderSystem; +import de.ellpeck.prettypipes.PrettyPipes; +import de.ellpeck.prettypipes.terminal.containers.ItemTerminalGui; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.client.gui.screen.inventory.ContainerScreen; +import net.minecraft.client.gui.widget.Widget; +import net.minecraft.client.renderer.ItemRenderer; +import net.minecraft.item.ItemStack; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.text.TextFormatting; +import net.minecraftforge.fml.client.gui.GuiUtils; + +import java.util.List; + +public class ItemTerminalWidget extends Widget { + + private static final ResourceLocation FONT = new ResourceLocation(PrettyPipes.ID, "unicode"); + private final ItemStack stack; + private final ItemTerminalGui screen; + public final int gridX; + public final int gridY; + public boolean hidden; + + public ItemTerminalWidget(int xIn, int yIn, int gridX, int gridY, ItemStack stack, ItemTerminalGui screen) { + super(xIn, yIn, 16, 16, stack.getDisplayName().getFormattedText()); + this.gridX = gridX; + this.gridY = gridY; + this.stack = stack; + this.screen = screen; + } + + @Override + protected boolean clicked(double x, double y) { + return false; + } + + @Override + public void render(int mouseX, int mouseY, float partialTicks) { + if (!this.hidden) + super.render(mouseX, mouseY, partialTicks); + } + + @Override + public void renderButton(int mouseX, int mouseY, float partialTicks) { + Minecraft mc = this.screen.getMinecraft(); + ItemRenderer renderer = mc.getItemRenderer(); + this.setBlitOffset(100); + renderer.zLevel = 100; + RenderSystem.enableDepthTest(); + renderer.renderItemAndEffectIntoGUI(mc.player, this.stack, this.x, this.y); + int amount = this.stack.getCount(); + String amountStrg = this.stack.getCount() >= 1000 ? amount / 1000 + "k" : String.valueOf(amount); + FontRenderer font = mc.getFontResourceManager().getFontRenderer(FONT); + renderer.renderItemOverlayIntoGUI(font, this.stack, this.x, this.y, amountStrg); + renderer.zLevel = 0; + this.setBlitOffset(0); + + if (this.isHovered()) { + RenderSystem.disableDepthTest(); + RenderSystem.colorMask(true, true, true, false); + this.fillGradient(this.x, this.y, this.x + 16, this.y + 16, -2130706433, -2130706433); + RenderSystem.colorMask(true, true, true, true); + RenderSystem.enableDepthTest(); + } + } + + @Override + public void renderToolTip(int mouseX, int mouseY) { + if (this.isHovered()) { + FontRenderer font = this.stack.getItem().getFontRenderer(this.stack); + if (font == null) + font = this.screen.getMinecraft().fontRenderer; + GuiUtils.preItemToolTip(this.stack); + List tooltip = this.screen.getTooltipFromItem(this.stack); + if (this.stack.getCount() >= 1000) + tooltip.set(0, tooltip.get(0) + TextFormatting.BOLD + " (" + this.stack.getCount() + ')'); + this.screen.renderTooltip(tooltip, mouseX, mouseY, font); + GuiUtils.postItemToolTip(); + } + } +} diff --git a/src/main/java/de/ellpeck/prettypipes/network/NetworkItem.java b/src/main/java/de/ellpeck/prettypipes/network/NetworkItem.java new file mode 100644 index 0000000..d9e426f --- /dev/null +++ b/src/main/java/de/ellpeck/prettypipes/network/NetworkItem.java @@ -0,0 +1,36 @@ +package de.ellpeck.prettypipes.network; + +import de.ellpeck.prettypipes.misc.EquatableItemStack; +import net.minecraft.item.ItemStack; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class NetworkItem { + + private final Set locations = new HashSet<>(); + private final EquatableItemStack item; + private int amount; + + public NetworkItem(EquatableItemStack item) { + this.item = item; + } + + public void add(NetworkLocation location, ItemStack stack) { + this.locations.add(location); + this.amount += stack.getCount(); + } + + public ItemStack asStack() { + ItemStack stack = this.item.stack.copy(); + stack.setCount(this.amount); + return stack; + } + + @Override + public String toString() { + return "NetworkItem{" + "locations=" + this.locations + ", item=" + this.item + ", amount=" + this.amount + '}'; + } +} diff --git a/src/main/java/de/ellpeck/prettypipes/network/NetworkLocation.java b/src/main/java/de/ellpeck/prettypipes/network/NetworkLocation.java index 4a66b3d..f446958 100644 --- a/src/main/java/de/ellpeck/prettypipes/network/NetworkLocation.java +++ b/src/main/java/de/ellpeck/prettypipes/network/NetworkLocation.java @@ -8,10 +8,7 @@ import net.minecraft.util.math.BlockPos; import net.minecraftforge.items.IItemHandler; import org.apache.commons.lang3.tuple.Pair; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; public class NetworkLocation { @@ -44,12 +41,16 @@ public class NetworkLocation { return -1; } - public int getItemAmount(ItemStack stack) { + public int getItemAmount(ItemStack stack, boolean compareTag) { return this.items.values().stream() - .filter(i -> i.isItemEqual(stack)) + .filter(i -> ItemStack.areItemsEqual(i, stack) && (!compareTag || ItemStack.areItemStackTagsEqual(i, stack))) .mapToInt(ItemStack::getCount).sum(); } + public Collection getItems() { + return this.items.values(); + } + public boolean isEmpty() { return this.items == null || this.items.isEmpty(); } diff --git a/src/main/java/de/ellpeck/prettypipes/network/PipeNetwork.java b/src/main/java/de/ellpeck/prettypipes/network/PipeNetwork.java index a0fb1b6..da113f1 100644 --- a/src/main/java/de/ellpeck/prettypipes/network/PipeNetwork.java +++ b/src/main/java/de/ellpeck/prettypipes/network/PipeNetwork.java @@ -353,11 +353,11 @@ public class PipeNetwork implements ICapabilitySerializable, GraphL public void vertexRemoved(GraphVertexChangeEvent e) { } - private void startProfile(String name) { + public void startProfile(String name) { this.world.getProfiler().startSection(() -> PrettyPipes.ID + ":pipe_network_" + name); } - private void endProfile() { + public void endProfile() { this.world.getProfiler().endSection(); } diff --git a/src/main/java/de/ellpeck/prettypipes/packets/PacketHandler.java b/src/main/java/de/ellpeck/prettypipes/packets/PacketHandler.java index eb2e3e4..71a059c 100644 --- a/src/main/java/de/ellpeck/prettypipes/packets/PacketHandler.java +++ b/src/main/java/de/ellpeck/prettypipes/packets/PacketHandler.java @@ -20,14 +20,15 @@ public final class PacketHandler { network = NetworkRegistry.newSimpleChannel(new ResourceLocation(PrettyPipes.ID, "network"), () -> VERSION, VERSION::equals, VERSION::equals); network.registerMessage(0, PacketItemEnterPipe.class, PacketItemEnterPipe::toBytes, PacketItemEnterPipe::fromBytes, PacketItemEnterPipe::onMessage); network.registerMessage(1, PacketButton.class, PacketButton::toBytes, PacketButton::fromBytes, PacketButton::onMessage); + network.registerMessage(2, PacketNetworkItems.class, PacketNetworkItems::toBytes, PacketNetworkItems::fromBytes, PacketNetworkItems::onMessage); } public static void sendToAllLoaded(World world, BlockPos pos, Object message) { network.send(PacketDistributor.TRACKING_CHUNK.with(() -> world.getChunkAt(pos)), message); } - public static void sendToAllAround(IWorld world, BlockPos pos, int range, Object message) { - network.send(PacketDistributor.NEAR.with(() -> new PacketDistributor.TargetPoint(pos.getX(), pos.getY(), pos.getZ(), range, world.getDimension().getType())), message); + public static void sendTo(PlayerEntity player, Object message) { + network.send(PacketDistributor.PLAYER.with(() -> (ServerPlayerEntity) player), message); } public static void sendToServer(Object message) { diff --git a/src/main/java/de/ellpeck/prettypipes/packets/PacketNetworkItems.java b/src/main/java/de/ellpeck/prettypipes/packets/PacketNetworkItems.java new file mode 100644 index 0000000..ca0502a --- /dev/null +++ b/src/main/java/de/ellpeck/prettypipes/packets/PacketNetworkItems.java @@ -0,0 +1,64 @@ +package de.ellpeck.prettypipes.packets; + +import de.ellpeck.prettypipes.Utility; +import de.ellpeck.prettypipes.network.PipeItem; +import de.ellpeck.prettypipes.pipe.PipeTileEntity; +import de.ellpeck.prettypipes.terminal.containers.ItemTerminalContainer; +import de.ellpeck.prettypipes.terminal.containers.ItemTerminalGui; +import net.minecraft.client.Minecraft; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.inventory.container.Container; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.network.PacketBuffer; +import net.minecraft.util.math.BlockPos; +import net.minecraftforge.fml.network.NetworkEvent; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Supplier; + +public class PacketNetworkItems { + + private List items; + + public PacketNetworkItems(List items) { + this.items = items; + } + + private PacketNetworkItems() { + + } + + public static PacketNetworkItems fromBytes(PacketBuffer buf) { + PacketNetworkItems client = new PacketNetworkItems(); + client.items = new ArrayList<>(); + for (int i = buf.readVarInt(); i > 0; i--) { + ItemStack stack = buf.readItemStack(); + stack.setCount(buf.readVarInt()); + client.items.add(stack); + } + return client; + } + + public static void toBytes(PacketNetworkItems packet, PacketBuffer buf) { + buf.writeVarInt(packet.items.size()); + for (ItemStack stack : packet.items) { + buf.writeItemStack(stack); + buf.writeVarInt(stack.getCount()); + } + } + + @SuppressWarnings("Convert2Lambda") + public static void onMessage(PacketNetworkItems message, Supplier ctx) { + ctx.get().enqueueWork(new Runnable() { + @Override + public void run() { + Minecraft mc = Minecraft.getInstance(); + if (mc.currentScreen instanceof ItemTerminalGui) + ((ItemTerminalGui) mc.currentScreen).updateItemList(message.items); + } + }); + ctx.get().setPacketHandled(true); + } +} diff --git a/src/main/java/de/ellpeck/prettypipes/pipe/containers/AbstractPipeContainer.java b/src/main/java/de/ellpeck/prettypipes/pipe/containers/AbstractPipeContainer.java index b574f12..547651b 100644 --- a/src/main/java/de/ellpeck/prettypipes/pipe/containers/AbstractPipeContainer.java +++ b/src/main/java/de/ellpeck/prettypipes/pipe/containers/AbstractPipeContainer.java @@ -2,7 +2,7 @@ package de.ellpeck.prettypipes.pipe.containers; import de.ellpeck.prettypipes.Utility; import de.ellpeck.prettypipes.items.IModule; -import de.ellpeck.prettypipes.misc.SlotFilter; +import de.ellpeck.prettypipes.misc.FilterSlot; import de.ellpeck.prettypipes.pipe.PipeTileEntity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.inventory.container.ClickType; @@ -11,6 +11,7 @@ import net.minecraft.inventory.container.ContainerType; import net.minecraft.inventory.container.Slot; import net.minecraft.item.ItemStack; import net.minecraft.util.math.BlockPos; +import org.apache.commons.lang3.tuple.Pair; import javax.annotation.Nullable; @@ -45,50 +46,16 @@ public abstract class AbstractPipeContainer extends Container @Override public ItemStack transferStackInSlot(PlayerEntity player, int slotIndex) { - int inventoryStart = (int) this.inventorySlots.stream().filter(slot -> slot.inventory != player.inventory).count(); - int inventoryEnd = inventoryStart + 26; - int hotbarStart = inventoryEnd + 1; - int hotbarEnd = hotbarStart + 8; - - Slot slot = this.inventorySlots.get(slotIndex); - if (slot != null && slot.getHasStack()) { - ItemStack newStack = slot.getStack(); - ItemStack currentStack = newStack.copy(); - - if (slotIndex >= inventoryStart) { - // shift into this container here - // mergeItemStack with the slots that newStack should go into - // return an empty stack if mergeItemStack fails - if (newStack.getItem() instanceof IModule) { - if (!this.mergeItemStack(newStack, 0, 3, false)) - return ItemStack.EMPTY; - } - // end custom code - else if (slotIndex >= inventoryStart && slotIndex <= inventoryEnd) { - if (!this.mergeItemStack(newStack, hotbarStart, hotbarEnd + 1, false)) - return ItemStack.EMPTY; - } else if (slotIndex >= inventoryEnd + 1 && slotIndex < hotbarEnd + 1 && !this.mergeItemStack(newStack, inventoryStart, inventoryEnd + 1, false)) { - return ItemStack.EMPTY; - } - } else if (!this.mergeItemStack(newStack, inventoryStart, hotbarEnd + 1, false)) { - return ItemStack.EMPTY; - } - if (newStack.isEmpty()) { - slot.putStack(ItemStack.EMPTY); - } else { - slot.onSlotChanged(); - } - if (newStack.getCount() == currentStack.getCount()) - return ItemStack.EMPTY; - slot.onTake(player, newStack); - return currentStack; - } - return ItemStack.EMPTY; + return Utility.transferStackInSlot(this, this::mergeItemStack, player, slotIndex, stack -> { + if (stack.getItem() instanceof IModule) + return Pair.of(0, 3); + return null; + }); } @Override public ItemStack slotClick(int slotId, int dragType, ClickType clickTypeIn, PlayerEntity player) { - if (SlotFilter.checkFilter(this, slotId, player)) + if (FilterSlot.checkFilter(this, slotId, player)) return ItemStack.EMPTY; return super.slotClick(slotId, dragType, clickTypeIn, player); } diff --git a/src/main/java/de/ellpeck/prettypipes/terminal/ItemTerminalBlock.java b/src/main/java/de/ellpeck/prettypipes/terminal/ItemTerminalBlock.java new file mode 100644 index 0000000..79f1650 --- /dev/null +++ b/src/main/java/de/ellpeck/prettypipes/terminal/ItemTerminalBlock.java @@ -0,0 +1,60 @@ +package de.ellpeck.prettypipes.terminal; + +import de.ellpeck.prettypipes.Utility; +import de.ellpeck.prettypipes.pipe.ConnectionType; +import de.ellpeck.prettypipes.pipe.IPipeConnectable; +import de.ellpeck.prettypipes.pipe.PipeTileEntity; +import net.minecraft.block.BlockRenderType; +import net.minecraft.block.BlockState; +import net.minecraft.block.ContainerBlock; +import net.minecraft.block.SoundType; +import net.minecraft.block.material.Material; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.entity.player.ServerPlayerEntity; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.ActionResultType; +import net.minecraft.util.Direction; +import net.minecraft.util.Hand; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.BlockRayTraceResult; +import net.minecraft.world.IBlockReader; +import net.minecraft.world.World; +import net.minecraftforge.fml.network.NetworkHooks; + +import javax.annotation.Nullable; + +public class ItemTerminalBlock extends ContainerBlock implements IPipeConnectable { + public ItemTerminalBlock() { + super(Properties.create(Material.ROCK).hardnessAndResistance(3).sound(SoundType.STONE)); + } + + @Override + public ActionResultType onBlockActivated(BlockState state, World worldIn, BlockPos pos, PlayerEntity player, Hand handIn, BlockRayTraceResult result) { + if (!player.getHeldItem(handIn).isEmpty()) + return ActionResultType.PASS; + ItemTerminalTileEntity tile = Utility.getTileEntity(ItemTerminalTileEntity.class, worldIn, pos); + if (tile == null) + return ActionResultType.PASS; + if (!worldIn.isRemote) { + NetworkHooks.openGui((ServerPlayerEntity) player, tile, pos); + tile.updateItems(player); + } + return ActionResultType.SUCCESS; + } + + @Nullable + @Override + public TileEntity createNewTileEntity(IBlockReader worldIn) { + return new ItemTerminalTileEntity(); + } + + @Override + public ConnectionType getConnectionType(World world, BlockPos pos, BlockState state, BlockPos pipePos, Direction direction) { + return ConnectionType.CONNECTED; + } + + @Override + public BlockRenderType getRenderType(BlockState state) { + return BlockRenderType.MODEL; + } +} diff --git a/src/main/java/de/ellpeck/prettypipes/terminal/ItemTerminalTileEntity.java b/src/main/java/de/ellpeck/prettypipes/terminal/ItemTerminalTileEntity.java new file mode 100644 index 0000000..5d2a92e --- /dev/null +++ b/src/main/java/de/ellpeck/prettypipes/terminal/ItemTerminalTileEntity.java @@ -0,0 +1,143 @@ +package de.ellpeck.prettypipes.terminal; + +import de.ellpeck.prettypipes.PrettyPipes; +import de.ellpeck.prettypipes.Registry; +import de.ellpeck.prettypipes.Utility; +import de.ellpeck.prettypipes.misc.EquatableItemStack; +import de.ellpeck.prettypipes.network.NetworkItem; +import de.ellpeck.prettypipes.network.NetworkLocation; +import de.ellpeck.prettypipes.network.PipeNetwork; +import de.ellpeck.prettypipes.packets.PacketHandler; +import de.ellpeck.prettypipes.packets.PacketNetworkItems; +import de.ellpeck.prettypipes.pipe.PipeTileEntity; +import de.ellpeck.prettypipes.terminal.containers.ItemTerminalContainer; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.entity.player.ServerPlayerEntity; +import net.minecraft.inventory.container.Container; +import net.minecraft.inventory.container.INamedContainerProvider; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.tileentity.ITickableTileEntity; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.tileentity.TileEntityType; +import net.minecraft.util.Direction; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.TranslationTextComponent; +import net.minecraft.world.server.ServerWorld; +import net.minecraftforge.items.ItemStackHandler; +import org.apache.commons.lang3.mutable.MutableInt; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.*; +import java.util.stream.Collectors; + +public class ItemTerminalTileEntity extends TileEntity implements INamedContainerProvider, ITickableTileEntity { + + public final ItemStackHandler items = new ItemStackHandler(12) { + @Override + public boolean isItemValid(int slot, @Nonnull ItemStack stack) { + return slot >= 6; + } + }; + public Collection networkItems; + + public ItemTerminalTileEntity() { + super(Registry.itemTerminalTileEntity); + } + + @Override + public void tick() { + if (this.world.isRemote) + return; + PipeNetwork network = PipeNetwork.get(this.world); + PipeTileEntity pipe = this.getConnectedPipe(); + if (pipe == null) + return; + + if (this.world.getGameTime() % 10 == 0) { + for (int i = 6; i < 12; i++) { + ItemStack extracted = this.items.extractItem(i, Integer.MAX_VALUE, true); + if (extracted.isEmpty()) + continue; + if (!network.tryInsertItem(pipe.getPos(), this.pos, extracted, true)) + continue; + this.items.extractItem(i, extracted.getCount(), false); + break; + } + } + + if (this.world.getGameTime() % 100 == 0) { + PlayerEntity[] lookingPlayers = this.world.getPlayers().stream() + .filter(p -> p.openContainer instanceof ItemTerminalContainer) + .filter(p -> ((ItemTerminalContainer) p.openContainer).tile == this) + .toArray(PlayerEntity[]::new); + if (lookingPlayers.length > 0) + this.updateItems(lookingPlayers); + } + } + + private PipeTileEntity getConnectedPipe() { + PipeNetwork network = PipeNetwork.get(this.world); + for (Direction dir : Direction.values()) { + PipeTileEntity pipe = network.getPipe(this.pos.offset(dir)); + if (pipe != null) + return pipe; + } + return null; + } + + public void updateItems(PlayerEntity... playersToSync) { + this.networkItems = this.collectItems(); + List clientItems = this.networkItems.stream().map(NetworkItem::asStack).collect(Collectors.toList()); + for (PlayerEntity player : playersToSync) { + if (!(player.openContainer instanceof ItemTerminalContainer)) + continue; + ItemTerminalTileEntity tile = ((ItemTerminalContainer) player.openContainer).tile; + if (tile != this) + continue; + PacketHandler.sendTo(player, new PacketNetworkItems(clientItems)); + } + } + + private Collection collectItems() { + PipeNetwork network = PipeNetwork.get(this.world); + network.startProfile("terminal_collect_items"); + PipeTileEntity pipe = this.getConnectedPipe(); + Map items = new HashMap<>(); + for (NetworkLocation location : network.getOrderedNetworkItems(pipe.getPos())) { + for (ItemStack stack : location.getItems()) { + EquatableItemStack equatable = new EquatableItemStack(stack); + NetworkItem item = items.computeIfAbsent(equatable, NetworkItem::new); + item.add(location, stack); + } + } + network.endProfile(); + return items.values(); + } + + @Override + public CompoundNBT write(CompoundNBT compound) { + compound.put("items", this.items.serializeNBT()); + return super.write(compound); + } + + @Override + public void read(CompoundNBT compound) { + this.items.deserializeNBT(compound.getCompound("items")); + super.read(compound); + } + + @Override + public ITextComponent getDisplayName() { + return new TranslationTextComponent("container." + PrettyPipes.ID + ".item_terminal"); + } + + @Nullable + @Override + public Container createMenu(int window, PlayerInventory inv, PlayerEntity player) { + return new ItemTerminalContainer(Registry.itemTerminalContainer, window, player, this.pos); + } +} diff --git a/src/main/java/de/ellpeck/prettypipes/terminal/containers/ItemTerminalContainer.java b/src/main/java/de/ellpeck/prettypipes/terminal/containers/ItemTerminalContainer.java new file mode 100644 index 0000000..de37ba7 --- /dev/null +++ b/src/main/java/de/ellpeck/prettypipes/terminal/containers/ItemTerminalContainer.java @@ -0,0 +1,57 @@ +package de.ellpeck.prettypipes.terminal.containers; + +import de.ellpeck.prettypipes.Utility; +import de.ellpeck.prettypipes.network.NetworkItem; +import de.ellpeck.prettypipes.packets.PacketHandler; +import de.ellpeck.prettypipes.packets.PacketNetworkItems; +import de.ellpeck.prettypipes.pipe.PipeTileEntity; +import de.ellpeck.prettypipes.terminal.ItemTerminalTileEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.entity.player.ServerPlayerEntity; +import net.minecraft.inventory.container.ClickType; +import net.minecraft.inventory.container.Container; +import net.minecraft.inventory.container.ContainerType; +import net.minecraft.inventory.container.Slot; +import net.minecraft.item.ItemStack; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.server.ServerWorld; +import net.minecraftforge.items.SlotItemHandler; +import org.apache.commons.lang3.tuple.Pair; + +import javax.annotation.Nullable; +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +public class ItemTerminalContainer extends Container { + + public final ItemTerminalTileEntity tile; + + public ItemTerminalContainer(@Nullable ContainerType type, int id, PlayerEntity player, BlockPos pos) { + super(type, id); + this.tile = Utility.getTileEntity(ItemTerminalTileEntity.class, player.world, pos); + + for (int i = 0; i < 6; i++) + this.addSlot(new SlotItemHandler(this.tile.items, i, 8 + i % 3 * 18, 102 + i / 3 * 18)); + for (int i = 0; i < 6; i++) + this.addSlot(new SlotItemHandler(this.tile.items, i + 6, 116 + i % 3 * 18, 102 + i / 3 * 18)); + + for (int l = 0; l < 3; ++l) + for (int j1 = 0; j1 < 9; ++j1) + this.addSlot(new Slot(player.inventory, j1 + l * 9 + 9, 8 + j1 * 18, 154 + l * 18)); + for (int i1 = 0; i1 < 9; ++i1) + this.addSlot(new Slot(player.inventory, i1, 8 + i1 * 18, 212)); + } + + @Override + public ItemStack transferStackInSlot(PlayerEntity player, int slotIndex) { + return Utility.transferStackInSlot(this, this::mergeItemStack, player, slotIndex, stack -> { + return Pair.of(6, 12); + }); + } + + @Override + public boolean canInteractWith(PlayerEntity playerIn) { + return true; + } +} diff --git a/src/main/java/de/ellpeck/prettypipes/terminal/containers/ItemTerminalGui.java b/src/main/java/de/ellpeck/prettypipes/terminal/containers/ItemTerminalGui.java new file mode 100644 index 0000000..4b7d38e --- /dev/null +++ b/src/main/java/de/ellpeck/prettypipes/terminal/containers/ItemTerminalGui.java @@ -0,0 +1,65 @@ +package de.ellpeck.prettypipes.terminal.containers; + +import de.ellpeck.prettypipes.PrettyPipes; +import de.ellpeck.prettypipes.misc.ItemTerminalWidget; +import net.minecraft.client.gui.screen.inventory.ContainerScreen; +import net.minecraft.client.gui.widget.Widget; +import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.item.ItemStack; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.text.ITextComponent; + +import java.util.List; + +public class ItemTerminalGui extends ContainerScreen { + private static final ResourceLocation TEXTURE = new ResourceLocation(PrettyPipes.ID, "textures/gui/item_terminal.png"); + + public ItemTerminalGui(ItemTerminalContainer screenContainer, PlayerInventory inv, ITextComponent titleIn) { + super(screenContainer, inv, titleIn); + this.xSize = 176 + 15; + this.ySize = 236; + } + + public void updateItemList(List items) { + this.buttons.removeIf(w -> w instanceof ItemTerminalWidget); + int x = 0; + int y = 0; + for (ItemStack stack : items) { + this.buttons.add(new ItemTerminalWidget(this.guiLeft + 8 + x * 18, this.guiTop + 18 + y * 18, x, y, stack, this)); + x++; + if (x > 8) { + x = 0; + y++; + } + } + } + + @Override + public void render(int mouseX, int mouseY, float partialTicks) { + this.renderBackground(); + super.render(mouseX, mouseY, partialTicks); + for (Widget widget : this.buttons) { + if (widget instanceof ItemTerminalWidget) + widget.renderToolTip(mouseX, mouseY); + } + this.renderHoveredToolTip(mouseX, mouseY); + } + + @Override + protected void drawGuiContainerForegroundLayer(int mouseX, int mouseY) { + this.font.drawString(this.playerInventory.getDisplayName().getFormattedText(), 8, this.ySize - 96 + 2, 4210752); + this.font.drawString(this.title.getFormattedText(), 8, 6, 4210752); + } + + @Override + protected void drawGuiContainerBackgroundLayer(float partialTicks, int mouseX, int mouseY) { + this.getMinecraft().getTextureManager().bindTexture(TEXTURE); + this.blit(this.guiLeft, this.guiTop, 0, 0, this.xSize, this.ySize); + } + + // public overload for ItemTerminalWidget + @Override + public void renderTooltip(ItemStack stack, int x, int y) { + super.renderTooltip(stack, x, y); + } +} diff --git a/src/main/resources/assets/prettypipes/font/unicode.json b/src/main/resources/assets/prettypipes/font/unicode.json new file mode 100644 index 0000000..3b31a44 --- /dev/null +++ b/src/main/resources/assets/prettypipes/font/unicode.json @@ -0,0 +1,10 @@ +{ + "_comment": "Unicode fonts only", + "providers": [ + { + "type": "legacy_unicode", + "sizes": "minecraft:font/glyph_sizes.bin", + "template": "minecraft:font/unicode_page_%s.png" + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/prettypipes/textures/gui/item_terminal.png b/src/main/resources/assets/prettypipes/textures/gui/item_terminal.png new file mode 100644 index 0000000..46f2a72 Binary files /dev/null and b/src/main/resources/assets/prettypipes/textures/gui/item_terminal.png differ