package gregtech.api.logic; import java.util.Collection; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; import javax.annotation.Nonnull; import javax.annotation.Nullable; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraftforge.common.util.Constants; import com.gtnewhorizons.modularui.api.forge.IItemHandlerModifiable; import com.gtnewhorizons.modularui.api.forge.ItemStackHandler; import com.gtnewhorizons.modularui.api.forge.ListItemHandler; import com.gtnewhorizons.modularui.api.math.Size; import com.gtnewhorizons.modularui.api.widget.Widget; import com.gtnewhorizons.modularui.common.widget.Scrollable; import com.gtnewhorizons.modularui.common.widget.SlotWidget; import gregtech.api.util.GTUtility; import gregtech.api.util.item.ItemHolder; /** * Generic Item logic for MuTEs. * * @author BlueWeabo */ public class ItemInventoryLogic { private static final int DEFAULT_COLUMNS_PER_ROW = 4; private static final int POSITION_INTERVAL = 18; private static final Size SIZE = new Size(18, 18); protected String displayName; @Nonnull protected final IItemHandlerModifiable inventory; protected UUID connectedFluidInventory; protected int tier; protected boolean isUpgradeInventory; protected Map cachedItemMap; protected boolean inRecipeCheck; public ItemInventoryLogic(int numberOfSlots) { this(numberOfSlots, 0); } public ItemInventoryLogic(int numberOfSlots, int tier) { this(new ItemStackHandler(numberOfSlots), tier, false); } public ItemInventoryLogic(int numberOfSlots, int tier, boolean isUpgradeInventory) { this(new ItemStackHandler(numberOfSlots), tier, isUpgradeInventory); } public ItemInventoryLogic(@Nonnull IItemHandlerModifiable inventory, int tier, boolean isUpgradeInventory) { this.inventory = inventory; this.tier = tier; this.isUpgradeInventory = isUpgradeInventory; } public ItemInventoryLogic(Collection inventories) { this(new ListItemHandler(inventories), -1, false); } @Nullable public String getDisplayName() { return displayName; } public int getTier() { return tier; } public boolean isUpgradeInventory() { return isUpgradeInventory; } public int getSlots() { return getInventory().getSlots(); } public void setDisplayName(@Nullable String displayName) { this.displayName = displayName; } @Nullable public UUID getConnectedFluidInventoryID() { return connectedFluidInventory; } public void setConnectedFluidInventoryID(@Nullable UUID connectedFluidTank) { this.connectedFluidInventory = connectedFluidTank; } /** * * @return The Item Inventory Logic as an NBTTagCompound to be saved in another nbt as how one wants. */ @Nonnull public NBTTagCompound saveToNBT() { final NBTTagCompound nbt = new NBTTagCompound(); final NBTTagList tList = new NBTTagList(); for (int slot = 0; slot < inventory.getSlots(); slot++) { final ItemStack tStack = inventory.getStackInSlot(slot); if (tStack == null) continue; final NBTTagCompound tag = new NBTTagCompound(); tag.setByte("s", (byte) slot); tStack.writeToNBT(tag); tList.appendTag(tag); } nbt.setTag("inventory", tList); nbt.setInteger("tier", tier); if (displayName != null) { nbt.setString("displayName", displayName); } nbt.setBoolean("isUpgradeInventory", isUpgradeInventory); if (connectedFluidInventory != null) { nbt.setString("connectedFluidInventory", connectedFluidInventory.toString()); } return nbt; } /** * Loads the Item Inventory Logic from an NBTTagCompound. */ public void loadFromNBT(@Nonnull NBTTagCompound nbt) { tier = nbt.getInteger("tier"); if (nbt.hasKey("displayName")) { displayName = nbt.getString("displayName"); } isUpgradeInventory = nbt.getBoolean("isUpgradeInventory"); if (nbt.hasKey("connectedFluidInventory")) { connectedFluidInventory = UUID.fromString(nbt.getString("connectedFluidInventory")); } NBTTagList nbtList = nbt.getTagList("inventory", Constants.NBT.TAG_COMPOUND); if (nbtList == null) return; for (int i = 0; i < nbtList.tagCount(); i++) { final NBTTagCompound tNBT = nbtList.getCompoundTagAt(i); final int tSlot = tNBT.getShort("s"); if (tSlot >= 0 && tSlot < inventory.getSlots()) { inventory.setStackInSlot(tSlot, GTUtility.loadItem(tNBT)); } } } @Nonnull public IItemHandlerModifiable getInventory() { return inventory; } @Nonnull public ItemStack[] getStoredItems() { final ItemStack[] items = inventory.getStacks() .stream() .filter(Objects::nonNull) .toArray(ItemStack[]::new); if (items == null) { return new ItemStack[0]; } return items; } public boolean isStackValid(ItemStack item) { return true; } @Nullable public ItemStack insertItem(ItemStack item) { if (!isStackValid(item)) return item; for (int i = 0; i < inventory.getSlots() && item != null && item.stackSize > 0; i++) { item = inventory.insertItem(i, item, false); } return item; } @Nullable public ItemStack extractItem(int slot, int amount) { return inventory.extractItem(slot, amount, false); } public boolean subtractItemAmount(@Nonnull ItemHolder item, long amount, boolean simulate) { Map itemMap = getMapOfStoredItems(); if (!itemMap.containsKey(item)) { return false; } if (itemMap.get(item) < amount) { return false; } if (simulate) { return true; } itemMap.put(item, itemMap.get(item) - amount); return true; } @Nullable public ItemStack getItemInSlot(int slot) { return inventory.getStackInSlot(slot); } public void sort() { Map itemMap = getMapOfStoredItems(); List sortedItems = itemMap.keySet() .stream() .sorted( Comparator.comparing( a -> a.getItem() .getUnlocalizedName() + a.getMeta())) .collect(Collectors.toList()); putInItemsFromMap(itemMap, sortedItems); } public void update(boolean shouldSort) { if (shouldSort) { sort(); } for (int i = 0; i < inventory.getSlots(); i++) { ItemStack item = inventory.getStackInSlot(i); if (item == null) continue; if (item.stackSize > 0) continue; inventory.setStackInSlot(i, null); } } /** * Return a scrollable widget with only the inventory. */ @Nonnull public Widget getGuiPart() { return getGUIPart(DEFAULT_COLUMNS_PER_ROW); } /** * Return a scrollable widget with only the inventory. */ @Nonnull public Widget getGUIPart(int columnsPerRow) { final Scrollable scrollable = new Scrollable(); scrollable.setVerticalScroll(); for (int rows = 0; rows * columnsPerRow < Math.min(inventory.getSlots(), 128); rows++) { final int columnsToMake = Math .min(Math.min(inventory.getSlots(), 128) - rows * columnsPerRow, columnsPerRow); for (int column = 0; column < columnsToMake; column++) { scrollable.widget( new SlotWidget(inventory, rows * columnsPerRow + column) .setPos(column * POSITION_INTERVAL, rows * POSITION_INTERVAL) .setSize(SIZE)); } } return scrollable; } public void startRecipeCheck() { cachedItemMap = getMapOfStoredItems(); inRecipeCheck = true; } public void stopRecipeCheck() { inRecipeCheck = false; putInItemsFromMap(cachedItemMap, null); cachedItemMap = null; } @Nonnull public Map getMapOfStoredItems() { if (inRecipeCheck) return cachedItemMap; Map items = new HashMap<>(); for (int i = 0; i < inventory.getSlots(); i++) { ItemStack item = extractItem(i, Integer.MAX_VALUE); if (item == null) continue; ItemHolder itemHolder = new ItemHolder(item); items.put(itemHolder, items.getOrDefault(itemHolder, 0L) + item.stackSize); } return items; } protected void putInItemsFromMap(@Nonnull Map itemMap, @Nullable List sortedList) { for (ItemHolder itemHolder : (sortedList == null ? itemMap.keySet() : sortedList)) { long itemAmount = itemMap.get(itemHolder); ItemStack item = new ItemStack(itemHolder.getItem(), 0, itemHolder.getMeta()); item.setTagCompound(itemHolder.getNBT()); while (itemAmount > 0) { item.stackSize = (int) Math.min(item.getMaxStackSize(), itemAmount); itemAmount -= item.stackSize; insertItem(item); } } } public long calculateAmountOfTimesItemCanBeTaken(ItemHolder item, long amount) { return getMapOfStoredItems().getOrDefault(item, 0L) / amount; } public Set getSetOfStoredItems() { return getMapOfStoredItems().keySet(); } }