package com.infinityraider.agricraft.tiles.storage; import com.infinityraider.agricraft.api.seed.AgriSeed; import com.infinityraider.agricraft.apiimpl.SeedRegistry; import com.infinityraider.agricraft.network.MessageTileEntitySeedStorage; import com.infinityraider.agricraft.reference.Reference; import com.infinityraider.agricraft.tiles.TileEntityCustomWood; import com.infinityraider.agricraft.utility.NBTHelper; import com.infinityraider.infinitylib.utility.debug.IDebuggable; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.inventory.ISidedInventory; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraft.util.EnumFacing; import net.minecraft.util.text.ITextComponent; import net.minecraft.util.text.TextComponentString; import com.agricraft.agricore.core.AgriCore; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import com.infinityraider.agricraft.reference.AgriNBT; import java.util.Optional; import java.util.function.Consumer; import javax.annotation.Nullable; public class TileEntitySeedStorage extends TileEntityCustomWood implements ISeedStorageControllable, IDebuggable, ISidedInventory { private AgriSeed lockedSeed; /** * Slots stored in a HashMap to use with Container and with Controller */ private Map<Integer, SeedStorageSlot> slots = new HashMap<>(); /** * Slots also stored in an ArrayList to use with ISidedInventory */ private ArrayList<SeedStorageSlot> slotsList = new ArrayList<>(); private ISeedStorageController controller; public TileEntitySeedStorage() { super(); } @Override protected void writeNBT(NBTTagCompound tag) { if (this.lockedSeed != null) { //add the locked SEED tag.setTag(AgriNBT.SEED, this.lockedSeed.toStack().writeToNBT(new NBTTagCompound())); if (this.slots != null) { //add the slots NBTTagList tagList = new NBTTagList(); for (Map.Entry<Integer, SeedStorageSlot> entry : slots.entrySet()) { if (entry != null && entry.getValue() != null) { NBTTagCompound slotTag = new NBTTagCompound(); entry.getValue().writeToNbt(slotTag); tagList.appendTag(slotTag); } } tag.setTag(AgriNBT.INVENTORY, tagList); } } if (this.hasController()) { NBTHelper.addCoordsToNBT(this.controller.getCoordinates(), tag); } } @Override protected void readNBT(NBTTagCompound tag) { this.slots = new HashMap<>(); this.slotsList = new ArrayList<>(); if (tag.hasKey(AgriNBT.SEED)) { //read the locked SEED ItemStack seedStack = ItemStack.loadItemStackFromNBT(tag.getCompoundTag(AgriNBT.SEED)); this.lockedSeed = SeedRegistry.getInstance().valueOf(seedStack).orElse(null); if (tag.hasKey(AgriNBT.INVENTORY)) { //read the slots NBTTagList tagList = tag.getTagList(AgriNBT.INVENTORY, 10); int invId = this.getControllableID(); for (int i = 0; i < tagList.tagCount(); i++) { SeedStorageSlot slot = SeedStorageSlot.readFromNbt(tagList.getCompoundTagAt(i), invId).orElse(null); if (slot != null) { slots.put(slot.getId(), slot); slotsList.add(slot); } } } } else { this.lockedSeed = null; } int[] coords = NBTHelper.getCoordsFromNBT(tag); if (coords != null && coords.length == 3) { this.controller = (ISeedStorageController) worldObj.getTileEntity(getPos()); } } public void syncSlotToClient(SeedStorageSlot slot) { new MessageTileEntitySeedStorage(this.getPos(), slot).sendToDimension(this.worldObj.provider.getDimension()); this.worldObj.getChunkFromBlockCoords(this.getPos()).setChunkModified(); } //Debug method @Override public void addServerDebugInfo(Consumer<String> consumer) { final String info = this.getLockedSeed().map(s -> s.getPlant().getPlantName()).orElse("null"); final int mapSize = this.slots == null ? 0 : this.slots.size(); final int listSize = this.slotsList == null ? 0 : this.slotsList.size(); consumer.accept("Locked Seed: " + info); consumer.accept("Nr of map entries: " + mapSize); consumer.accept("Nr of list entries: " + listSize); } @Override @SideOnly(Side.CLIENT) @SuppressWarnings("unchecked") public void addDisplayInfo(List information) { final String tool = AgriCore.getTranslator().translate("agricraft_tooltip.storage"); final String none = AgriCore.getTranslator().translate("agricraft_tooltip.none"); information.add(tool + ": " + this.getLockedSeed().map(s -> s.getPlant().getPlantName()).orElse(none)); super.addDisplayInfo(information); } //SEED STORAGE METHODS //-------------------- @Override public boolean addStackToInventory(ItemStack stack) { // Fetch the seed. final AgriSeed seed = SeedRegistry.getInstance().valueOf(stack).filter(s -> s.getStat().isAnalyzed()).orElse(null); if (seed == null || worldObj.isRemote) { return false; } // Set the locked seed if no locked seed is present. if (this.lockedSeed == null) { this.lockedSeed = seed; this.setSlotContents(0, stack); return true; } // Determine if the seed matches this chest's type. if (this.lockedSeed.getPlant() == seed.getPlant()) { for (Map.Entry<Integer, SeedStorageSlot> entry : this.slots.entrySet()) { if (entry.getValue() != null) { if (ItemStack.areItemStackTagsEqual(lockedSeed.toStack(), stack)) { ItemStack newStack = stack.copy(); newStack.stackSize = newStack.stackSize + entry.getValue().count; this.setSlotContents(entry.getKey(), newStack); return true; } } } // If no slot to combine with was found, add to a new slot. int slotId = getFirstFreeSlot(); if (slotId >= 0) { this.setSlotContents(slotId, stack); return true; } } // The seed could not be added to the storage unit. return false; } private int getFirstFreeSlot() { for (int i = 0; i < slots.size(); i++) { if (!slots.containsKey(i)) { return i; } } return slots.size(); } @Override @Nullable public ItemStack getStackForSlotId(int slotId) { final SeedStorageSlot slot = slots.get(slotId); if (slot != null) { return slot.toStack(); } else { return null; } } @Override public void setSlotContents(int realSlotId, ItemStack inputStack) { if (realSlotId < 0) { this.addStackToInventory(inputStack); return; } if (this.isValidForSlot(realSlotId, inputStack)) { SeedStorageSlot slotAt = this.slots.get(realSlotId); if (slotAt != null) { slotAt.count = inputStack.stackSize; if (slotAt.count <= 0) { slots.remove(realSlotId); slotsList.remove(slotAt); } } else { final AgriSeed seed = SeedRegistry.getInstance().valueOf(inputStack).get(); slotAt = new SeedStorageSlot(seed, inputStack.stackSize, realSlotId, this.getControllableID()); if (slotAt.count > 0) { this.slots.put(realSlotId, slotAt); this.slotsList.add(slotAt); } } if (!this.worldObj.isRemote) { this.syncSlotToClient(slotAt); } else { this.markForUpdate(); } } } private boolean isValidForSlot(int realSlot, ItemStack stack) { // Fetch the seed. AgriSeed seed = SeedRegistry.getInstance().valueOf(stack).orElse(null); // If the seed is not analyzed then it is not valid. if (seed == null || !seed.getStat().isAnalyzed()) { return false; } // If there is no locked seed, then this seed is valid. if (this.lockedSeed == null) { return true; } // If the seed type matches the locked seed type, then the seed is valid. if (this.lockedSeed.getPlant() == seed.getPlant()) { final SeedStorageSlot slotAt = this.slots.get(realSlot); return slotAt == null || ItemStack.areItemStackTagsEqual(stack, slotAt.getSeed().toStack()); } return false; } @Override public ItemStack decreaseStackSizeInSlot(int realSlotId, int amount) { if (!worldObj.isRemote) { ItemStack stackInSlot = null; if (this.slots != null) { SeedStorageSlot slotAt = this.slots.get(realSlotId); if (slotAt != null) { stackInSlot = slotAt.toStack(); stackInSlot.stackSize = Math.min(amount, slotAt.count); if (slotAt.count <= amount) { this.slots.remove(realSlotId); this.slotsList.remove(slotAt); slotAt.count = 0; } else { slotAt.count = slotAt.count - amount; } } this.syncSlotToClient(slotAt); } return stackInSlot; } return null; } @Override public ArrayList<ItemStack> getInventory() { ArrayList<ItemStack> stacks = new ArrayList<>(); if (this.hasLockedSeed()) { for (Map.Entry<Integer, SeedStorageSlot> entries : slots.entrySet()) { if (entries != null && entries.getValue() != null) { stacks.add(entries.getValue().toStack()); } } } return stacks; } @Override public List<SeedStorageSlot> getSlots() { if (slotsList == null) { slotsList = new ArrayList<>(); } return slotsList; } @Override public int[] getControllerCoords() { return this.controller != null ? this.controller.getCoordinates() : null; } @Override public int[] getCoords() { return new int[]{this.xCoord(), this.yCoord(), this.zCoord()}; } @Override public ISeedStorageController getController() { return this.controller; } @Override public boolean hasController() { return this.controller != null; } @Override public boolean hasLockedSeed() { return this.lockedSeed != null; } @Override public boolean setLockedSeed(AgriSeed seed) { if (!this.hasLockedSeed()) { this.lockedSeed = seed; this.markForUpdate(); return true; } else { return false; } } @Override public void clearLockedSeed() { if (this.slots.isEmpty()) { this.lockedSeed = null; this.markForUpdate(); } } @Override public Optional<AgriSeed> getLockedSeed() { return Optional.ofNullable(this.lockedSeed); } @Override public int getControllableID() { int id = -1; if (this.hasController()) { id = this.getController().getControllableID(this); } return id; } //INVENTORY METHODS //----------------- @Override public int getSizeInventory() { //One extra for the 'fake' input only slot return this.slots.size() + 1; } @Override public ItemStack getStackInSlot(int slot) { if (slotsList == null || slot >= slotsList.size() || (!this.hasLockedSeed())) { return null; } return slotsList.get(slot).toStack(); } @Override public ItemStack decrStackSize(int slot, int amount) { if (slotsList == null || slot >= slotsList.size() || (!this.hasLockedSeed())) { return null; } if (!worldObj.isRemote) { ItemStack stackInSlot = null; SeedStorageSlot slotAt = this.slotsList.get(slot); if (slotAt != null) { stackInSlot = slotAt.toStack(); stackInSlot.stackSize = Math.min(amount, slotAt.count); if (slotAt.count <= amount) { this.slots.remove(slotAt.getId()); this.slotsList.remove(slotAt); slotAt.count = 0; } else { slotAt.count = slotAt.count - amount; } } this.syncSlotToClient(slotAt); return stackInSlot; } return null; } @Override public ItemStack removeStackFromSlot(int slot) { if (slotsList == null || slot >= slotsList.size() || (!this.hasLockedSeed())) { return null; } SeedStorageSlot slotAt = slotsList.get(slot); ItemStack stackInSlot = slotAt.toStack(); this.slots.remove(slotAt.getId()); this.slotsList.remove(slotAt); return stackInSlot; } @Override public void setInventorySlotContents(int slot, ItemStack inputStack) { if (slotsList == null || slot >= slotsList.size()) { this.addStackToInventory(inputStack); return; } if (inputStack == null) { inputStack = slotsList.get(slot).toStack(); inputStack.stackSize = 0; } if (this.isItemValidForSlot(slot, inputStack)) { SeedStorageSlot slotAt = this.slotsList.get(slot); if (slotAt != null) { slotAt.count = inputStack.stackSize; if (slotAt.count <= 0) { slots.remove(slotAt.getId()); slotsList.remove(slotAt); } if (!this.worldObj.isRemote) { this.syncSlotToClient(slotAt); } } else { this.addStackToInventory(inputStack); } } } /** * TODO: FIXME! * * @return */ @Override public String getName() { return Reference.MOD_ID + ":seed_storage"; } @Override public boolean hasCustomName() { return true; } @Override public ITextComponent getDisplayName() { return new TextComponentString(getName()); } @Override public int getField(int id) { return 0; } @Override public void setField(int id, int value) { } @Override public int getFieldCount() { return 0; } @Override public void clear() { this.slotsList = new ArrayList<>(); this.slots = new HashMap<>(); this.lockedSeed = null; } @Override public int getInventoryStackLimit() { return Integer.MAX_VALUE; } @Override public boolean isUseableByPlayer(EntityPlayer player) { return true; } @Override public void openInventory(EntityPlayer player) { } @Override public void closeInventory(EntityPlayer player) { } @Override public boolean isItemValidForSlot(int slot, ItemStack stack) { AgriSeed seed = SeedRegistry.getInstance().valueOf(stack).orElse(null); if (seed == null || !seed.getStat().isAnalyzed()) { return false; } if (this.hasLockedSeed()) { if (this.lockedSeed.getPlant() == seed.getPlant()) { if (slot >= slotsList.size()) { return true; } SeedStorageSlot slotAt = this.slotsList.get(slot); return slotAt == null || ItemStack.areItemStackTagsEqual(stack, this.getStackInSlot(slot)); } } else { return true; } return false; } @Override public int[] getSlotsForFace(EnumFacing side) { int[] array = new int[slotsList.size() + 1]; for (int i = 0; i < array.length; i++) { array[i] = i; } return array; } @Override public boolean canInsertItem(int slot, ItemStack stack, EnumFacing side) { if (worldObj.isRemote) { return false; } if (slot >= slotsList.size()) { //0 is a virtual slot only used for inputs, this is a workaround to prevent derpy behaviour with code which modifies stacks in slots directly return isItemValidForSlot(slot, stack); } return false; } @Override public boolean canExtractItem(int slot, ItemStack stack, EnumFacing side) { return this.slots.containsKey(slot) && this.isItemValidForSlot(slot, stack) && this.slots.get(slot).count > 0; } }