diff --git a/src/main/java/refinedstorage/apiimpl/autocrafting/AutoCraftInfoStack.java b/src/main/java/refinedstorage/apiimpl/autocrafting/AutoCraftInfoStack.java new file mode 100644 index 000000000..3ad75b030 --- /dev/null +++ b/src/main/java/refinedstorage/apiimpl/autocrafting/AutoCraftInfoStack.java @@ -0,0 +1,88 @@ +package refinedstorage.apiimpl.autocrafting; + +import io.netty.buffer.ByteBuf; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; + +public class AutoCraftInfoStack { + private ItemStack stack; + private int needed; + private int stock; + private int extras; + private boolean cantCraft; + + public AutoCraftInfoStack(ItemStack stack, int needed, int stock) { + this.stack = stack; + this.needed = needed; + this.stock = stock; + this.extras = 0; + this.cantCraft = false; + } + + public void writeToByteBuf(ByteBuf buf) { + buf.writeInt(Item.getIdFromItem(stack.getItem())); + buf.writeInt(stack.getMetadata()); + buf.writeInt(needed); + buf.writeInt(stock); + buf.writeInt(extras); + buf.writeBoolean(cantCraft); + } + + public static AutoCraftInfoStack fromByteBuf(ByteBuf buf) { + Item item = Item.getItemById(buf.readInt()); + int meta = buf.readInt(); + int toCraft = buf.readInt(); + int available = buf.readInt(); + AutoCraftInfoStack stack = new AutoCraftInfoStack(new ItemStack(item, 1, meta), toCraft, available); + stack.extras = buf.readInt(); + stack.cantCraft = buf.readBoolean(); + return stack; + } + + public void addNeeded(int quantity) { + this.needed += quantity; + } + + public void addExtras(int quantity) { + this.extras += quantity; + } + + public int getAvailable() { + return this.stock + this.extras - this.needed; + } + + public ItemStack getStack() { + return stack; + } + + public int getStock() { + return stock; + } + + public int getNeeded() { + return needed; + } + + public boolean needsCrafting() { + return this.needed > this.stock; + } + + public boolean cantCraft() { + return this.cantCraft; + } + + public int getToCraft() { + return this.needed - this.stock; + } + + public void setCantCraft(boolean cantCraft) { + this.cantCraft = cantCraft; + } + + @Override + public String toString() { + return stack.toString() + ", needed=" + needed + ", stock=" + stock + ", extras=" + extras + ", canCraft=" + !cantCraft; + } + + +} diff --git a/src/main/java/refinedstorage/apiimpl/autocrafting/CraftingUtils.java b/src/main/java/refinedstorage/apiimpl/autocrafting/CraftingUtils.java new file mode 100644 index 000000000..ac59c8f6a --- /dev/null +++ b/src/main/java/refinedstorage/apiimpl/autocrafting/CraftingUtils.java @@ -0,0 +1,72 @@ +package refinedstorage.apiimpl.autocrafting; + +import net.minecraft.item.ItemStack; +import refinedstorage.api.autocrafting.ICraftingPattern; +import refinedstorage.api.network.INetworkMaster; +import refinedstorage.api.network.NetworkUtils; +import refinedstorage.api.storage.CompareUtils; + +import java.util.Collection; +import java.util.HashMap; + +public final class CraftingUtils { + public static Collection getItems(INetworkMaster network, ItemStack stack, int quantity) { + AutoCraftInfoData data = new AutoCraftInfoData(network); + calcItems(network, stack, quantity, data); + return data.values(); + } + + private static void calcItems(INetworkMaster network, ItemStack stack, int quantity, AutoCraftInfoData data) { + quantity = -data.add(stack, quantity); + if (quantity > 0) { + ICraftingPattern pattern = NetworkUtils.getPattern(network, stack); + if (pattern != null) { + int quantityPerRequest = pattern.getQuantityPerRequest(stack); + while (quantity > 0) { + for (ItemStack ingredient : pattern.getInputs()) { + calcItems(network, ingredient, ingredient.stackSize, data); + } + data.get(stack).addExtras(quantityPerRequest); + quantity -= quantityPerRequest; + } + } else { + data.get(stack).setCantCraft(true); + } + } + } + + private static class AutoCraftInfoData { + private HashMap data; + private INetworkMaster network; + + private AutoCraftInfoData(INetworkMaster network) { + this.data = new HashMap<>(); + this.network = network; + } + + /** + * @return available stacks, if negative needs crafting + */ + private int add(ItemStack stack, int quantity) { + int hash = NetworkUtils.getItemStackHashCode(stack); + if (data.containsKey(hash)) { + AutoCraftInfoStack infoStack = data.get(hash); + infoStack.addNeeded(quantity); + return infoStack.getAvailable(); + } else { + ItemStack networkStack = network.getItemStorage().get(stack, CompareUtils.COMPARE_DAMAGE | CompareUtils.COMPARE_NBT); + AutoCraftInfoStack infoStack = new AutoCraftInfoStack(stack, quantity, networkStack == null ? 0 : networkStack.stackSize); + data.put(hash, infoStack); + return infoStack.getAvailable(); + } + } + + private AutoCraftInfoStack get(ItemStack stack) { + return this.data.get(NetworkUtils.getItemStackHashCode(stack)); + } + + private Collection values() { + return this.data.values(); + } + } +} diff --git a/src/main/java/refinedstorage/gui/GuiCraftingPreview.java b/src/main/java/refinedstorage/gui/GuiCraftingPreview.java new file mode 100644 index 000000000..61a10cd86 --- /dev/null +++ b/src/main/java/refinedstorage/gui/GuiCraftingPreview.java @@ -0,0 +1,182 @@ +package refinedstorage.gui; + +import com.google.common.primitives.Ints; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.RenderHelper; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.Container; +import net.minecraft.item.ItemStack; +import net.minecraftforge.fml.client.FMLClientHandler; +import org.lwjgl.input.Keyboard; +import refinedstorage.RefinedStorage; +import refinedstorage.apiimpl.autocrafting.AutoCraftInfoStack; +import refinedstorage.network.MessageGridCraftingPreview; +import refinedstorage.network.MessageGridCraftingStart; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +public class GuiCraftingPreview extends GuiBase { + private static final int VISIBLE_ROWS = 8; + + private List stacks; + private GuiScreen parent; + + private int hash, quantity; + + private GuiButton startButton; + private GuiButton cancelButton; + + public GuiCraftingPreview(GuiScreen parent, Collection stacks, int hash, int quantity) { + super(new Container() { + @Override + public boolean canInteractWith(EntityPlayer playerIn) { + return false; + } + }, 176, 181); + this.stacks = new ArrayList<>(stacks); + this.parent = parent; + + this.hash = hash; + this.quantity = quantity; + + this.scrollbar = new Scrollbar(157, 20, 12, 59); + } + + @Override + public void init(int x, int y) { + startButton = addButton(x + 100, y + 150, 50, 20, t("misc.refinedstorage:start")); + cancelButton = addButton(x + 20, y + 150, 50, 20, t("gui.cancel")); + + startButton.enabled = stacks.stream().filter(AutoCraftInfoStack::cantCraft).count() == 0; + } + + @Override + public void update(int x, int y) { + scrollbar.setEnabled(getRows() > VISIBLE_ROWS); + scrollbar.setMaxOffset(getRows() - VISIBLE_ROWS); + } + + @Override + public void drawBackground(int x, int y, int mouseX, int mouseY) { + bindTexture("gui/crafting_preview.png"); + + drawTexture(x, y, 0, 0, width, height); + + x += 15; + y += 20; + + int slot = scrollbar.getOffset() * 2; + for (int i = 0; i < 8; ++i) { + if (slot < stacks.size()) { + AutoCraftInfoStack stack = stacks.get(slot); + + if (stack.cantCraft()) { + drawTexture(x, y, 0, 185, 67, 29); + } + } + + if (i%2 == 1) { + x -= 68; + y += 30; + } else { + x += 68; + } + slot++; + } + } + + @Override + public void drawForeground(int mouseX, int mouseY) { + drawString(7, 7, t("gui.refinedstorage:crafting_preview.title")); + + int x = 22; + int y = 22; + + int slot = scrollbar.getOffset() * 2; + + RenderHelper.enableGUIStandardItemLighting(); + + ItemStack hoveringStack = null; + + for (int i = 0; i < 8; ++i) { + if (slot < stacks.size()) { + AutoCraftInfoStack stack = stacks.get(slot); + + drawItem(x, y + 5, stack.getStack()); + + float scale = 0.5f; + + GlStateManager.pushMatrix(); + GlStateManager.scale(scale, scale, 1); + + if (stack.needsCrafting()) { + + String format = stack.cantCraft() ? "gui.refinedstorage:crafting_preview.missing" : "gui.refinedstorage:crafting_preview.to_craft"; + drawString(calculateOffsetOnScale(x + 20, scale), calculateOffsetOnScale(y + 8, scale), t(format, stack.getToCraft())); + } + if (stack.getStock() > 0) { + drawString(calculateOffsetOnScale(x + 20, scale), calculateOffsetOnScale(y + 13, scale), t("gui.refinedstorage:crafting_preview.available", stack.getStock())); + } + + GlStateManager.popMatrix(); + + if (inBounds(x, y, 16, 16, mouseX, mouseY)) { + hoveringStack = stack.getStack(); + } + } + + if (i%2 == 1) { + x -= 68; + y += 30; + } else { + x += 68; + } + + slot++; + } + if (hoveringStack != null) { + drawTooltip(mouseX, mouseY, hoveringStack.getTooltip(Minecraft.getMinecraft().thePlayer, false)); + } + } + + @Override + protected void keyTyped(char character, int keyCode) throws IOException { + if (keyCode == Keyboard.KEY_RETURN && startButton.enabled) { + startRequest(); + } else if (keyCode == Keyboard.KEY_ESCAPE) { + close(); + } else { + super.keyTyped(character, keyCode); + } + } + + @Override + protected void actionPerformed(GuiButton button) throws IOException { + super.actionPerformed(button); + + if (button.id == startButton.id) { + startRequest(); + } else if (button.id == cancelButton.id) { + close(); + } + } + + private void startRequest() { + RefinedStorage.INSTANCE.network.sendToServer(new MessageGridCraftingStart(hash, quantity)); + close(); + } + + private int getRows() { + return Math.max(0, (int) Math.ceil((float) stacks.size() / 2F)); + } + + private void close() { + FMLClientHandler.instance().showGuiScreen(parent); + } +} diff --git a/src/main/java/refinedstorage/gui/grid/GuiCraftingStart.java b/src/main/java/refinedstorage/gui/grid/GuiCraftingStart.java index 5b15f5d7f..f46e1c937 100755 --- a/src/main/java/refinedstorage/gui/grid/GuiCraftingStart.java +++ b/src/main/java/refinedstorage/gui/grid/GuiCraftingStart.java @@ -2,6 +2,7 @@ package refinedstorage.gui.grid; import com.google.common.primitives.Ints; import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.gui.GuiScreen; import net.minecraft.client.gui.GuiTextField; import net.minecraft.entity.player.EntityPlayer; import net.minecraftforge.fml.client.FMLClientHandler; @@ -10,7 +11,7 @@ import refinedstorage.RefinedStorage; import refinedstorage.container.ContainerCraftingSettings; import refinedstorage.gui.GuiBase; import refinedstorage.gui.grid.stack.ClientStackItem; -import refinedstorage.network.MessageGridCraftingStart; +import refinedstorage.network.MessageGridCraftingPreview; import java.io.IOException; @@ -124,13 +125,15 @@ public class GuiCraftingStart extends GuiBase { Integer quantity = Ints.tryParse(amountField.getText()); if (quantity != null && quantity > 0) { - RefinedStorage.INSTANCE.network.sendToServer(new MessageGridCraftingStart(stack.getHash(), quantity)); - - close(); + RefinedStorage.INSTANCE.network.sendToServer(new MessageGridCraftingPreview(stack.getHash(), quantity)); } } private void close() { FMLClientHandler.instance().showGuiScreen(gui); } + + public GuiGrid getGui() { + return gui; + } } diff --git a/src/main/java/refinedstorage/network/MessageGridCraftingPreview.java b/src/main/java/refinedstorage/network/MessageGridCraftingPreview.java new file mode 100644 index 000000000..39680276c --- /dev/null +++ b/src/main/java/refinedstorage/network/MessageGridCraftingPreview.java @@ -0,0 +1,53 @@ +package refinedstorage.network; + +import io.netty.buffer.ByteBuf; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.inventory.Container; +import net.minecraft.tileentity.TileEntity; +import net.minecraftforge.fml.common.network.simpleimpl.IMessage; +import refinedstorage.RefinedStorage; +import refinedstorage.api.network.INetworkMaster; +import refinedstorage.apiimpl.autocrafting.AutoCraftInfoStack; +import refinedstorage.apiimpl.autocrafting.CraftingUtils; +import refinedstorage.container.ContainerGrid; + +import java.util.Collection; + +public class MessageGridCraftingPreview extends MessageHandlerPlayerToServer implements IMessage { + private int hash; + private int quantity; + + public MessageGridCraftingPreview() { + } + + public MessageGridCraftingPreview(int hash, int quantity) { + this.hash = hash; + this.quantity = quantity; + } + + @Override + public void fromBytes(ByteBuf buf) { + hash = buf.readInt(); + quantity = buf.readInt(); + } + + @Override + public void toBytes(ByteBuf buf) { + buf.writeInt(hash); + buf.writeInt(quantity); + } + + @Override + public void handle(MessageGridCraftingPreview message, EntityPlayerMP player) { + Container container = player.openContainer; + + if (container instanceof ContainerGrid) { + TileEntity entity = player.getEntityWorld().getTileEntity(((ContainerGrid) container).getGrid().getNetworkPosition()); + if (entity != null && entity instanceof INetworkMaster) { + INetworkMaster network = (INetworkMaster) entity; + Collection stacks = CraftingUtils.getItems(network, network.getItemStorage().get(message.hash), message.quantity); + RefinedStorage.INSTANCE.network.sendTo(new MessageGridCraftingPreviewResponse(stacks, message.hash, message.quantity), player); + } + } + } +} diff --git a/src/main/java/refinedstorage/network/MessageGridCraftingPreviewResponse.java b/src/main/java/refinedstorage/network/MessageGridCraftingPreviewResponse.java new file mode 100644 index 000000000..a632ef314 --- /dev/null +++ b/src/main/java/refinedstorage/network/MessageGridCraftingPreviewResponse.java @@ -0,0 +1,64 @@ +package refinedstorage.network; + +import io.netty.buffer.ByteBuf; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiScreen; +import net.minecraftforge.fml.common.FMLCommonHandler; +import net.minecraftforge.fml.common.network.simpleimpl.IMessage; +import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler; +import net.minecraftforge.fml.common.network.simpleimpl.MessageContext; +import refinedstorage.apiimpl.autocrafting.AutoCraftInfoStack; +import refinedstorage.gui.GuiCraftingPreview; +import refinedstorage.gui.grid.GuiCraftingStart; + +import java.util.Collection; +import java.util.LinkedList; + +public class MessageGridCraftingPreviewResponse implements IMessage, IMessageHandler { + + private Collection stacks; + private int hash, quantity; + + public MessageGridCraftingPreviewResponse() { + + } + + public MessageGridCraftingPreviewResponse(Collection stacks, int hash, int quantity) { + this.stacks = stacks; + this.hash = hash; + this.quantity = quantity; + } + + @Override + public void fromBytes(ByteBuf buf) { + this.hash = buf.readInt(); + this.quantity = buf.readInt(); + + this.stacks = new LinkedList<>(); + int size = buf.readInt(); + for (int i = 0 ; i < size; i++) { + this.stacks.add(AutoCraftInfoStack.fromByteBuf(buf)); + } + } + + @Override + public void toBytes(ByteBuf buf) { + buf.writeInt(this.hash); + buf.writeInt(this.quantity); + + buf.writeInt(stacks.size()); + for (AutoCraftInfoStack stack : stacks) { + stack.writeToByteBuf(buf); + } + } + + @Override + public IMessage onMessage(MessageGridCraftingPreviewResponse message, MessageContext ctx) { + GuiScreen screen = Minecraft.getMinecraft().currentScreen; + if (screen instanceof GuiCraftingStart) { + screen = ((GuiCraftingStart) screen).getGui(); + } + FMLCommonHandler.instance().showGuiScreen(new GuiCraftingPreview(screen, message.stacks, message.hash, message.quantity)); + return null; + } +} diff --git a/src/main/java/refinedstorage/proxy/CommonProxy.java b/src/main/java/refinedstorage/proxy/CommonProxy.java index 928687e7c..5c220f292 100755 --- a/src/main/java/refinedstorage/proxy/CommonProxy.java +++ b/src/main/java/refinedstorage/proxy/CommonProxy.java @@ -69,6 +69,8 @@ public class CommonProxy { RefinedStorage.INSTANCE.network.registerMessage(MessageGridFluidInsertHeld.class, MessageGridFluidInsertHeld.class, id++, Side.SERVER); RefinedStorage.INSTANCE.network.registerMessage(MessageProcessingPatternEncoderClear.class, MessageProcessingPatternEncoderClear.class, id++, Side.SERVER); RefinedStorage.INSTANCE.network.registerMessage(MessageGridFilterUpdate.class, MessageGridFilterUpdate.class, id++, Side.SERVER); + RefinedStorage.INSTANCE.network.registerMessage(MessageGridCraftingPreview.class, MessageGridCraftingPreview.class, id++, Side.SERVER); + RefinedStorage.INSTANCE.network.registerMessage(MessageGridCraftingPreviewResponse.class, MessageGridCraftingPreviewResponse.class, id++, Side.CLIENT); NetworkRegistry.INSTANCE.registerGuiHandler(RefinedStorage.INSTANCE, new GuiHandler()); diff --git a/src/main/resources/assets/refinedstorage/lang/en_US.lang b/src/main/resources/assets/refinedstorage/lang/en_US.lang index 5a5266a3c..964acae7e 100755 --- a/src/main/resources/assets/refinedstorage/lang/en_US.lang +++ b/src/main/resources/assets/refinedstorage/lang/en_US.lang @@ -41,6 +41,10 @@ gui.refinedstorage:network_transmitter.missing_upgrade=Insert upgrade gui.refinedstorage:fluid_interface=Fluid Interface gui.refinedstorage:fluid_interface.in=In gui.refinedstorage:fluid_interface.out=Out +gui.refinedstorage:crafting_preview.title=Crafting Preview +gui.refinedstorage:crafting_preview.to_craft=To Craft: %d +gui.refinedstorage:crafting_preview.available=Available: %d +gui.refinedstorage:crafting_preview.missing=Missing: %d misc.refinedstorage:energy_stored=%d / %d RS misc.refinedstorage:energy_usage=Usage: %d RS/t diff --git a/src/main/resources/assets/refinedstorage/textures/gui/crafting_preview.png b/src/main/resources/assets/refinedstorage/textures/gui/crafting_preview.png new file mode 100644 index 000000000..a1950cafc Binary files /dev/null and b/src/main/resources/assets/refinedstorage/textures/gui/crafting_preview.png differ