mirror of
https://github.com/Ellpeck/PrettyPipes.git
synced 2024-11-25 21:18:34 +01:00
crafting terminal JEI logic!
This commit is contained in:
parent
f9c5ea86f7
commit
3104062b82
10 changed files with 236 additions and 25 deletions
|
@ -0,0 +1,41 @@
|
|||
package de.ellpeck.prettypipes.compat.jei;
|
||||
|
||||
import com.google.common.collect.ArrayListMultimap;
|
||||
import com.google.common.collect.ListMultimap;
|
||||
import de.ellpeck.prettypipes.packets.PacketGhostSlot;
|
||||
import de.ellpeck.prettypipes.packets.PacketHandler;
|
||||
import de.ellpeck.prettypipes.terminal.CraftingTerminalTileEntity;
|
||||
import de.ellpeck.prettypipes.terminal.ItemTerminalTileEntity;
|
||||
import de.ellpeck.prettypipes.terminal.containers.CraftingTerminalContainer;
|
||||
import mezz.jei.api.gui.IRecipeLayout;
|
||||
import mezz.jei.api.gui.ingredient.IGuiIngredient;
|
||||
import mezz.jei.api.recipe.transfer.IRecipeTransferError;
|
||||
import mezz.jei.api.recipe.transfer.IRecipeTransferHandler;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.inventory.container.Slot;
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Map;
|
||||
|
||||
public class CraftingTerminalTransferHandler implements IRecipeTransferHandler<CraftingTerminalContainer> {
|
||||
@Override
|
||||
public Class<CraftingTerminalContainer> getContainerClass() {
|
||||
return CraftingTerminalContainer.class;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public IRecipeTransferError transferRecipe(CraftingTerminalContainer container, IRecipeLayout recipeLayout, PlayerEntity player, boolean maxTransfer, boolean doTransfer) {
|
||||
if (!doTransfer)
|
||||
return null;
|
||||
ListMultimap<Integer, ItemStack> stacks = ArrayListMultimap.create();
|
||||
Map<Integer, ? extends IGuiIngredient<ItemStack>> ings = recipeLayout.getItemStacks().getGuiIngredients();
|
||||
for (Map.Entry<Integer, ? extends IGuiIngredient<ItemStack>> entry : ings.entrySet()) {
|
||||
if (entry.getValue().isInput())
|
||||
stacks.putAll(entry.getKey() - 1, entry.getValue().getAllIngredients());
|
||||
}
|
||||
PacketHandler.sendToServer(new PacketGhostSlot(container.getTile().getPos(), stacks));
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -5,6 +5,8 @@ import de.ellpeck.prettypipes.misc.PlayerPrefs;
|
|||
import de.ellpeck.prettypipes.terminal.containers.ItemTerminalGui;
|
||||
import mezz.jei.api.IModPlugin;
|
||||
import mezz.jei.api.JeiPlugin;
|
||||
import mezz.jei.api.constants.VanillaRecipeCategoryUid;
|
||||
import mezz.jei.api.registration.IRecipeTransferRegistration;
|
||||
import mezz.jei.api.runtime.IIngredientFilter;
|
||||
import mezz.jei.api.runtime.IJeiRuntime;
|
||||
import net.minecraft.client.Minecraft;
|
||||
|
@ -46,6 +48,11 @@ public class JEIPrettyPipesPlugin implements IModPlugin {
|
|||
this.runtime = jeiRuntime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerRecipeTransferHandlers(IRecipeTransferRegistration registration) {
|
||||
registration.addRecipeTransferHandler(new CraftingTerminalTransferHandler(), VanillaRecipeCategoryUid.CRAFTING);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onInitGui(InitGuiEvent.Post event) {
|
||||
Screen screen = event.getGui();
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
package de.ellpeck.prettypipes.packets;
|
||||
|
||||
import com.google.common.collect.ArrayListMultimap;
|
||||
import com.google.common.collect.ListMultimap;
|
||||
import de.ellpeck.prettypipes.Utility;
|
||||
import de.ellpeck.prettypipes.terminal.CraftingTerminalTileEntity;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.network.PacketBuffer;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraftforge.fml.network.NetworkEvent;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class PacketGhostSlot {
|
||||
|
||||
private BlockPos pos;
|
||||
private ListMultimap<Integer, ItemStack> stacks;
|
||||
|
||||
public PacketGhostSlot(BlockPos pos, ListMultimap<Integer, ItemStack> stacks) {
|
||||
this.pos = pos;
|
||||
this.stacks = stacks;
|
||||
}
|
||||
|
||||
private PacketGhostSlot() {
|
||||
|
||||
}
|
||||
|
||||
public static PacketGhostSlot fromBytes(PacketBuffer buf) {
|
||||
PacketGhostSlot packet = new PacketGhostSlot();
|
||||
packet.pos = buf.readBlockPos();
|
||||
packet.stacks = ArrayListMultimap.create();
|
||||
for (int i = buf.readInt(); i > 0; i--)
|
||||
packet.stacks.put(buf.readInt(), buf.readItemStack());
|
||||
return packet;
|
||||
}
|
||||
|
||||
public static void toBytes(PacketGhostSlot packet, PacketBuffer buf) {
|
||||
buf.writeBlockPos(packet.pos);
|
||||
buf.writeInt(packet.stacks.size());
|
||||
for (Map.Entry<Integer, ItemStack> entry : packet.stacks.entries()) {
|
||||
buf.writeInt(entry.getKey());
|
||||
buf.writeItemStack(entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("Convert2Lambda")
|
||||
public static void onMessage(PacketGhostSlot message, Supplier<NetworkEvent.Context> ctx) {
|
||||
ctx.get().enqueueWork(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
PlayerEntity player = ctx.get().getSender();
|
||||
if (player == null)
|
||||
player = Minecraft.getInstance().player;
|
||||
CraftingTerminalTileEntity tile = Utility.getTileEntity(CraftingTerminalTileEntity.class, player.world, message.pos);
|
||||
if (tile != null)
|
||||
tile.setGhostItems(message.stacks);
|
||||
}
|
||||
});
|
||||
ctx.get().setPacketHandled(true);
|
||||
}
|
||||
}
|
|
@ -22,6 +22,7 @@ public final class PacketHandler {
|
|||
network.registerMessage(1, PacketButton.class, PacketButton::toBytes, PacketButton::fromBytes, PacketButton::onMessage);
|
||||
network.registerMessage(2, PacketNetworkItems.class, PacketNetworkItems::toBytes, PacketNetworkItems::fromBytes, PacketNetworkItems::onMessage);
|
||||
network.registerMessage(3, PacketRequest.class, PacketRequest::toBytes, PacketRequest::fromBytes, PacketRequest::onMessage);
|
||||
network.registerMessage(4, PacketGhostSlot.class, PacketGhostSlot::toBytes, PacketGhostSlot::fromBytes, PacketGhostSlot::onMessage);
|
||||
}
|
||||
|
||||
public static void sendToAllLoaded(World world, BlockPos pos, Object message) {
|
||||
|
|
|
@ -28,22 +28,25 @@ public class CraftingTerminalBlock extends ItemTerminalBlock {
|
|||
CraftingTerminalTileEntity tile = Utility.getTileEntity(CraftingTerminalTileEntity.class, world, pos);
|
||||
if (tile != null) {
|
||||
ItemStack remain = item.stack;
|
||||
int lowestFitting = -1;
|
||||
int lowestSlot = -1;
|
||||
do {
|
||||
for (int i = 0; i < tile.craftItems.getSlots(); i++) {
|
||||
ItemStack stack = tile.getRequestedCraftItem(i);
|
||||
int count = tile.isGhostItem(i) ? 0 : stack.getCount();
|
||||
if (!ItemHandlerHelper.canItemStacksStackRelaxed(stack, remain))
|
||||
continue;
|
||||
if (lowestFitting < 0 || stack.getCount() < tile.getRequestedCraftItem(lowestFitting).getCount())
|
||||
lowestFitting = i;
|
||||
if (lowestSlot < 0 || !tile.isGhostItem(lowestSlot) && count < tile.getRequestedCraftItem(lowestSlot).getCount())
|
||||
lowestSlot = i;
|
||||
}
|
||||
if (lowestFitting >= 0) {
|
||||
remain = tile.craftItems.insertItem(lowestFitting, remain, false);
|
||||
if (lowestSlot >= 0) {
|
||||
ItemStack copy = remain.copy();
|
||||
copy.setCount(1);
|
||||
remain.shrink(1 - tile.craftItems.insertItem(lowestSlot, copy, false).getCount());
|
||||
if (remain.isEmpty())
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
}
|
||||
while (lowestFitting >= 0);
|
||||
while (lowestSlot >= 0);
|
||||
return ItemHandlerHelper.insertItemStacked(tile.items, remain, false);
|
||||
}
|
||||
return item.stack;
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package de.ellpeck.prettypipes.terminal;
|
||||
|
||||
import com.google.common.collect.ArrayListMultimap;
|
||||
import com.google.common.collect.ListMultimap;
|
||||
import de.ellpeck.prettypipes.PrettyPipes;
|
||||
import de.ellpeck.prettypipes.Registry;
|
||||
import de.ellpeck.prettypipes.misc.EquatableItemStack;
|
||||
|
@ -8,6 +10,8 @@ import de.ellpeck.prettypipes.network.NetworkItem;
|
|||
import de.ellpeck.prettypipes.network.NetworkLocation;
|
||||
import de.ellpeck.prettypipes.network.NetworkLock;
|
||||
import de.ellpeck.prettypipes.network.PipeNetwork;
|
||||
import de.ellpeck.prettypipes.packets.PacketGhostSlot;
|
||||
import de.ellpeck.prettypipes.packets.PacketHandler;
|
||||
import de.ellpeck.prettypipes.terminal.containers.CraftingTerminalContainer;
|
||||
import de.ellpeck.prettypipes.terminal.containers.ItemTerminalContainer;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
|
@ -23,23 +27,69 @@ import net.minecraftforge.items.ItemStackHandler;
|
|||
import org.apache.commons.lang3.mutable.MutableInt;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
public class CraftingTerminalTileEntity extends ItemTerminalTileEntity {
|
||||
|
||||
public final ItemStackHandler craftItems = new ItemStackHandler(9);
|
||||
public final ItemStackHandler craftItems = new ItemStackHandler(9) {
|
||||
@Override
|
||||
protected void onContentsChanged(int slot) {
|
||||
for (PlayerEntity playerEntity : CraftingTerminalTileEntity.this.getLookingPlayers())
|
||||
playerEntity.openContainer.onCraftMatrixChanged(null);
|
||||
}
|
||||
};
|
||||
public final ItemStackHandler ghostItems = new ItemStackHandler(9);
|
||||
|
||||
public CraftingTerminalTileEntity() {
|
||||
super(Registry.craftingTerminalTileEntity);
|
||||
}
|
||||
|
||||
public ItemStack getRequestedCraftItem(int slot) {
|
||||
// TODO put ghost slot contents here
|
||||
return this.craftItems.getStackInSlot(slot);
|
||||
ItemStack stack = this.craftItems.getStackInSlot(slot);
|
||||
if (!stack.isEmpty())
|
||||
return stack;
|
||||
return this.ghostItems.getStackInSlot(slot);
|
||||
}
|
||||
|
||||
public boolean isGhostItem(int slot) {
|
||||
return this.craftItems.getStackInSlot(slot).isEmpty() && !this.ghostItems.getStackInSlot(slot).isEmpty();
|
||||
}
|
||||
|
||||
public void setGhostItems(ListMultimap<Integer, ItemStack> stacks) {
|
||||
this.updateItems();
|
||||
items:
|
||||
for (int i = 0; i < this.ghostItems.getSlots(); i++) {
|
||||
List<ItemStack> items = stacks.get(i);
|
||||
if (items.isEmpty()) {
|
||||
this.ghostItems.setStackInSlot(i, ItemStack.EMPTY);
|
||||
continue;
|
||||
}
|
||||
if (items.size() > 1) {
|
||||
// set the item into the ghost slot that already has a variant of itself available in the system
|
||||
for (ItemStack stack : items) {
|
||||
EquatableItemStack equatable = new EquatableItemStack(stack);
|
||||
NetworkItem network = this.networkItems.get(equatable);
|
||||
if (network == null)
|
||||
continue;
|
||||
if (network.getLocations().stream().anyMatch(l -> l.getItemAmount(this.world, stack, ItemEqualityType.NBT) > 0)) {
|
||||
this.ghostItems.setStackInSlot(i, stack);
|
||||
continue items;
|
||||
}
|
||||
}
|
||||
}
|
||||
// if the ghost slot wasn't set, then we don't have the item in the system
|
||||
// so just pick a random one to put into the slot
|
||||
this.ghostItems.setStackInSlot(i, items.get(0));
|
||||
}
|
||||
|
||||
if (!this.world.isRemote) {
|
||||
ListMultimap<Integer, ItemStack> clients = ArrayListMultimap.create();
|
||||
for (int i = 0; i < this.ghostItems.getSlots(); i++)
|
||||
clients.put(i, this.ghostItems.getStackInSlot(i));
|
||||
PacketHandler.sendToAllLoaded(this.world, this.pos, new PacketGhostSlot(this.pos, clients));
|
||||
}
|
||||
}
|
||||
|
||||
public void requestCraftingItems(PlayerEntity player, boolean all) {
|
||||
|
@ -47,6 +97,8 @@ public class CraftingTerminalTileEntity extends ItemTerminalTileEntity {
|
|||
network.startProfile("terminal_request_crafting");
|
||||
this.updateItems();
|
||||
|
||||
// the highest amount we can craft with the items we have
|
||||
int lowestAvailable = Integer.MAX_VALUE;
|
||||
// this is the amount of items required for each ingredient when crafting ONE
|
||||
Map<EquatableItemStack, MutableInt> requiredItems = new HashMap<>();
|
||||
for (int i = 0; i < this.craftItems.getSlots(); i++) {
|
||||
|
@ -55,15 +107,16 @@ public class CraftingTerminalTileEntity extends ItemTerminalTileEntity {
|
|||
continue;
|
||||
MutableInt amount = requiredItems.computeIfAbsent(new EquatableItemStack(requested), s -> new MutableInt());
|
||||
amount.add(1);
|
||||
int fit = requested.getMaxStackSize() - (this.isGhostItem(i) ? 0 : requested.getCount());
|
||||
if (lowestAvailable > fit)
|
||||
lowestAvailable = fit;
|
||||
}
|
||||
// the highest amount we can craft with the items we have
|
||||
int lowestAvailable = Integer.MAX_VALUE;
|
||||
for (Map.Entry<EquatableItemStack, MutableInt> entry : requiredItems.entrySet()) {
|
||||
EquatableItemStack stack = entry.getKey();
|
||||
NetworkItem item = this.networkItems.get(stack);
|
||||
// total amount of available items of this type
|
||||
int available = 0;
|
||||
if (item != null) {
|
||||
// total amount of available items of this type
|
||||
int available = 0;
|
||||
for (NetworkLocation location : item.getLocations()) {
|
||||
for (int slot : location.getStackSlots(this.world, stack.stack, ItemEqualityType.NBT)) {
|
||||
ItemStack inSlot = location.getItemHandler(this.world).extractItem(slot, Integer.MAX_VALUE, true);
|
||||
|
@ -76,15 +129,12 @@ public class CraftingTerminalTileEntity extends ItemTerminalTileEntity {
|
|||
// divide the total by the amount required to get the amount that
|
||||
// we have available for each crafting slot that contains this item
|
||||
available /= entry.getValue().intValue();
|
||||
int fit = stack.stack.getMaxStackSize() - stack.stack.getCount();
|
||||
if (available > fit)
|
||||
available = fit;
|
||||
if (available < lowestAvailable)
|
||||
lowestAvailable = available;
|
||||
} else {
|
||||
lowestAvailable = 0;
|
||||
}
|
||||
if (lowestAvailable <= 0)
|
||||
if (available <= 0)
|
||||
player.sendMessage(new TranslationTextComponent("info." + PrettyPipes.ID + ".not_found", stack.stack.getDisplayName()).setStyle(new Style().setColor(TextFormatting.RED)));
|
||||
}
|
||||
if (lowestAvailable > 0) {
|
||||
|
|
|
@ -177,7 +177,7 @@ public class ItemTerminalTileEntity extends TileEntity implements INamedContaine
|
|||
return 0;
|
||||
}
|
||||
|
||||
private PlayerEntity[] getLookingPlayers() {
|
||||
protected PlayerEntity[] getLookingPlayers() {
|
||||
return this.world.getPlayers().stream()
|
||||
.filter(p -> p.openContainer instanceof ItemTerminalContainer)
|
||||
.filter(p -> ((ItemTerminalContainer) p.openContainer).tile == this)
|
||||
|
|
|
@ -8,6 +8,7 @@ import net.minecraft.entity.player.ServerPlayerEntity;
|
|||
import net.minecraft.inventory.CraftResultInventory;
|
||||
import net.minecraft.inventory.CraftingInventory;
|
||||
import net.minecraft.inventory.IInventory;
|
||||
import net.minecraft.inventory.container.ClickType;
|
||||
import net.minecraft.inventory.container.ContainerType;
|
||||
import net.minecraft.inventory.container.CraftingResultSlot;
|
||||
import net.minecraft.inventory.container.Slot;
|
||||
|
@ -68,7 +69,17 @@ public class CraftingTerminalContainer extends ItemTerminalContainer {
|
|||
return 65;
|
||||
}
|
||||
|
||||
protected CraftingTerminalTileEntity getTile() {
|
||||
@Override
|
||||
public ItemStack slotClick(int slotId, int dragType, ClickType clickTypeIn, PlayerEntity player) {
|
||||
if (slotId > 0) {
|
||||
Slot slot = this.inventorySlots.get(slotId);
|
||||
if (slot.inventory == this.craftInventory)
|
||||
this.getTile().ghostItems.setStackInSlot(slot.getSlotIndex(), ItemStack.EMPTY);
|
||||
}
|
||||
return super.slotClick(slotId, dragType, clickTypeIn, player);
|
||||
}
|
||||
|
||||
public CraftingTerminalTileEntity getTile() {
|
||||
return (CraftingTerminalTileEntity) this.tile;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package de.ellpeck.prettypipes.terminal.containers;
|
||||
|
||||
import com.mojang.blaze3d.platform.GlStateManager;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import de.ellpeck.prettypipes.PrettyPipes;
|
||||
import de.ellpeck.prettypipes.packets.PacketButton;
|
||||
import de.ellpeck.prettypipes.packets.PacketHandler;
|
||||
|
@ -9,6 +11,7 @@ import net.minecraft.client.gui.widget.Widget;
|
|||
import net.minecraft.client.gui.widget.button.Button;
|
||||
import net.minecraft.client.resources.I18n;
|
||||
import net.minecraft.entity.player.PlayerInventory;
|
||||
import net.minecraft.inventory.container.Slot;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.text.ITextComponent;
|
||||
|
@ -29,13 +32,41 @@ public class CraftingTerminalGui extends ItemTerminalGui {
|
|||
int all = hasShiftDown() ? 1 : 0;
|
||||
PacketHandler.sendToServer(new PacketButton(this.container.tile.getPos(), PacketButton.ButtonResult.CRAFT_TERMINAL_REQUEST, all));
|
||||
}));
|
||||
this.requestButton.active = !this.getCraftingContainer().craftInventory.isEmpty();
|
||||
this.requestButton.active = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
super.tick();
|
||||
this.requestButton.active = !this.getCraftingContainer().craftInventory.isEmpty();
|
||||
CraftingTerminalTileEntity tile = this.getCraftingContainer().getTile();
|
||||
this.requestButton.active = false;
|
||||
for (int i = 0; i < tile.craftItems.getSlots(); i++) {
|
||||
if (!tile.getRequestedCraftItem(i).isEmpty()) {
|
||||
this.requestButton.active = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void drawGuiContainerForegroundLayer(int mouseX, int mouseY) {
|
||||
super.drawGuiContainerForegroundLayer(mouseX, mouseY);
|
||||
|
||||
CraftingTerminalContainer container = this.getCraftingContainer();
|
||||
CraftingTerminalTileEntity tile = container.getTile();
|
||||
for (int i = 0; i < tile.ghostItems.getSlots(); i++) {
|
||||
if (!tile.craftItems.getStackInSlot(i).isEmpty())
|
||||
continue;
|
||||
ItemStack ghost = tile.ghostItems.getStackInSlot(i);
|
||||
if (ghost.isEmpty())
|
||||
continue;
|
||||
int finalI = i;
|
||||
Slot slot = container.inventorySlots.stream().filter(s -> s.inventory == container.craftInventory && s.getSlotIndex() == finalI).findFirst().orElse(null);
|
||||
if (slot == null)
|
||||
continue;
|
||||
this.minecraft.getItemRenderer().renderItemIntoGUI(ghost, slot.xPos, slot.yPos);
|
||||
this.minecraft.getItemRenderer().renderItemOverlayIntoGUI(this.font, ghost, slot.xPos, slot.yPos, "0");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -96,6 +96,8 @@ public class ItemTerminalGui extends ContainerScreen<ItemTerminalContainer> {
|
|||
this.search = this.addButton(new TextFieldWidget(this.font, this.guiLeft + this.getXOffset() + 97, this.guiTop + 6, 86, 8, ""));
|
||||
this.search.setEnableBackgroundDrawing(false);
|
||||
this.lastSearchText = "";
|
||||
if (this.items != null)
|
||||
this.updateWidgets();
|
||||
}
|
||||
|
||||
protected int getXOffset() {
|
||||
|
|
Loading…
Reference in a new issue