package lumaceon.mods.clockworkphase2.tile.machine; import lumaceon.mods.clockworkphase2.api.capabilities.EnergyStorageModular; import lumaceon.mods.clockworkphase2.api.util.ClockworkHelper; import lumaceon.mods.clockworkphase2.tile.generic.TileMod; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.inventory.ISidedInventory; import net.minecraft.inventory.Slot; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraft.util.EnumFacing; import net.minecraft.util.ITickable; import net.minecraft.util.text.ITextComponent; import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.common.capabilities.CapabilityInject; import net.minecraftforge.energy.IEnergyStorage; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; import net.minecraftforge.items.IItemHandler; import net.minecraftforge.items.ItemHandlerHelper; import net.minecraftforge.items.wrapper.SidedInvWrapper; import javax.annotation.Nullable; import java.util.ArrayList; import java.util.Arrays; public abstract class TileClockworkMachine extends TileMod implements ISidedInventory, ITickable { @CapabilityInject(IItemHandler.class) static Capability<IItemHandler> ITEM_HANDLER_CAPABILITY = null; @CapabilityInject(IEnergyStorage.class) static Capability<IEnergyStorage> ENERGY_STORAGE_CAPABILITY = null; SidedInvWrapper invUP = new SidedInvWrapper(this, EnumFacing.UP); SidedInvWrapper invDOWN = new SidedInvWrapper(this, EnumFacing.DOWN); SidedInvWrapper invNORTH = new SidedInvWrapper(this, EnumFacing.NORTH); SidedInvWrapper invSOUTH = new SidedInvWrapper(this, EnumFacing.SOUTH); SidedInvWrapper invEAST = new SidedInvWrapper(this, EnumFacing.EAST); SidedInvWrapper invWEST = new SidedInvWrapper(this, EnumFacing.WEST); public int[] slotsUP = new int[0]; public int[] slotsDOWN = new int[0]; public int[] slotsFRONT = new int[0]; public int[] slotsBACK = new int[0]; public int[] slotsRIGHT = new int[0]; public int[] slotsLEFT = new int[0]; public EnergyStorageModular energyStorage = new EnergyStorageModular(1); public Slot[] slots; //Must remain in the same order as the inventory. protected ItemStack[] inventory; protected int inventoryStackSizeLimit = 64; protected int progressTimer = 0; protected int quality = 250; protected int speed = 500; protected int speedOfMachine = 50; //A good par is about 50 for simple machines. Must be greater than 0. protected int progressForOperation = 10000; public TileClockworkMachine() { slots = new Slot[0]; inventory = new ItemStack[1]; } public TileClockworkMachine(int inventorySize, int maxStackSize, int speedOfMachine, int parEnergyForOperation) { this.inventory = new ItemStack[inventorySize]; this.inventoryStackSizeLimit = maxStackSize; this.speedOfMachine = speedOfMachine; this.progressForOperation = parEnergyForOperation; } @Override public void update() { boolean isDirty = false; if(!this.worldObj.isRemote) { int energyCostPerTick = getEnergyCostPerTick(); if(isOperable(energyCostPerTick)) { if(canWork()) { if(energyStorage.extractEnergy(energyCostPerTick, false) >= energyCostPerTick) { this.progressTimer += (int) (ClockworkHelper.getStandardExponentialSpeedMultiplier(speed) * speedOfMachine); if(progressTimer >= progressForOperation) { progressTimer -= progressForOperation; completeAction(); } } isDirty = true; } } } if(isDirty) markDirty(); } public boolean isOperable(int energyCost) { return energyStorage.getEnergyStored() >= energyCost && speed > 0 && quality > 0; } public int getEnergyCostPerTick() { //Because the exponential gain will make the timer go up faster, we scale the base cost itself. //The base cost should be relative to the amount of work done. return ClockworkHelper.getTensionCostFromStatsMachine((int) (ClockworkHelper.getStandardExponentialSpeedMultiplier(speed) * speedOfMachine), quality, speed) / speedOfMachine; } @SideOnly(Side.CLIENT) public int getProgressScaled(int maxValue) { return progressTimer * maxValue / progressForOperation; } public abstract boolean canWork(); /** * Processes a completed action, such as a furnace smelting an ingot. */ public abstract void completeAction(); /** * Attempt to export the given stack (usually a custom recipe result) to the given slots. * @return Leftover items that weren't added to the slots, or null if all found a home. */ public ItemStack exportItem(ItemStack export, int[] slots, boolean simulate) { int simulatedExportStackSize = export.stackSize; for(int i : slots) { ItemStack stackInSlot = getStackInSlot(i); if(stackInSlot == null) { if(!simulate) setInventorySlotContents(i, export); return null; } else if(ItemHandlerHelper.canItemStacksStack(stackInSlot, export)) { int maxStack = Math.min(this.getInventoryStackLimit(), stackInSlot.getMaxStackSize()); int amountToMove = Math.min(maxStack - stackInSlot.stackSize, export.stackSize); if(!simulate) { stackInSlot.stackSize += amountToMove; export.stackSize -= amountToMove; if(export.stackSize <= 0) return null; } else { simulatedExportStackSize -= amountToMove; if(simulatedExportStackSize <= 0) return null; } } } if(simulate) { ItemStack ret = export.copy(); ret.stackSize = simulatedExportStackSize; return ret; } return export; } public void changeIO(EnumFacing direction, int slotID, boolean activate) { if(!activate) { if(direction.equals(EnumFacing.UP)) slotsUP = deactivate(slotsUP, slotID); if(direction.equals(EnumFacing.DOWN)) slotsDOWN = deactivate(slotsDOWN, slotID); if(direction.equals(EnumFacing.NORTH)) slotsFRONT = deactivate(slotsFRONT, slotID); if(direction.equals(EnumFacing.SOUTH)) slotsBACK = deactivate(slotsBACK, slotID); if(direction.equals(EnumFacing.EAST)) slotsRIGHT = deactivate(slotsRIGHT, slotID); if(direction.equals(EnumFacing.WEST)) slotsLEFT = deactivate(slotsLEFT, slotID); } else { if(direction.equals(EnumFacing.UP)) slotsUP = activate(slotsUP, slotID); if(direction.equals(EnumFacing.DOWN)) slotsDOWN = activate(slotsDOWN, slotID); if(direction.equals(EnumFacing.NORTH)) slotsFRONT = activate(slotsFRONT, slotID); if(direction.equals(EnumFacing.SOUTH)) slotsBACK = activate(slotsBACK, slotID); if(direction.equals(EnumFacing.EAST)) slotsRIGHT = activate(slotsRIGHT, slotID); if(direction.equals(EnumFacing.WEST)) slotsLEFT = activate(slotsLEFT, slotID); } } private int[] activate(int[] slots, int slotID) { for(int slot : slots) if(slot == slotID) return slots; int[] ret = Arrays.copyOf(slots, slots.length + 1); ret[ret.length - 1] = slotID; return ret; } private int[] deactivate(int[] slots, int slotID) { ArrayList<Integer> retList = new ArrayList<Integer>(slots.length); boolean found = false; for(int slot : slots) { if(slot != slotID) retList.add(slot); else found = true; } if(!found) return slots; int[] ret = new int[retList.size()]; for(int i = 0; i < retList.size(); i++) { int temp = retList.get(i); ret[i] = temp; } return ret; } @Override public int getSizeInventory() { return inventory.length; } @Nullable @Override public ItemStack getStackInSlot(int index) { return index < inventory.length ? inventory[index] : null; } @Nullable @Override public ItemStack decrStackSize(int index, int count) { ItemStack is = getStackInSlot(index); if(is != null) { if(count >= is.stackSize) { setInventorySlotContents(index, null); } else { is = is.splitStack(count); if(is.stackSize == 0) { setInventorySlotContents(index, null); } } markDirty(); } return is; } @Nullable @Override public ItemStack removeStackFromSlot(int index) { ItemStack ret = inventory[index]; inventory[index] = null; markDirty(); return ret; } @Override public void setInventorySlotContents(int index, @Nullable ItemStack stack) { inventory[index] = stack; if(stack != null && stack.stackSize > this.getInventoryStackLimit()) stack.stackSize = this.getInventoryStackLimit(); this.markDirty(); } @Override public int getInventoryStackLimit() { return inventoryStackSizeLimit; } @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 index, ItemStack stack) { //Confirm we have an array of slots and the index is in-bounds. if(slots == null || index >= slots.length) return false; return slots[index].isItemValid(stack); } @Override public int getField(int id) { switch(id) { case 0: //Progress Timer return progressTimer; case 1: //Energy return energyStorage.getEnergyStored(); case 2: //Max Energy return energyStorage.getMaxEnergyStored(); } return 0; } @Override public void setField(int id, int value) { switch(id) { case 0: //Progress Timer progressTimer = value; break; case 1: //Energy energyStorage.setEnergy(value); break; case 2: //Max Energy energyStorage.setMaxCapacity(value); break; } } @Override public int getFieldCount() { return 3; } @Override public void clear() { for(int i = 0; i < inventory.length; i++) inventory[i] = null; markDirty(); } @Override public String getName() { return null; } @Override public boolean hasCustomName() { return false; } @Override public ITextComponent getDisplayName() { return null; } @Override public int[] getSlotsForFace(EnumFacing side) { side = rotate(side); if(side.equals(EnumFacing.UP)) return slotsUP; if(side.equals(EnumFacing.DOWN)) return slotsDOWN; if(side.equals(EnumFacing.NORTH)) return slotsFRONT; if(side.equals(EnumFacing.SOUTH)) return slotsBACK; if(side.equals(EnumFacing.EAST)) return slotsRIGHT; if(side.equals(EnumFacing.WEST)) return slotsLEFT; return new int[0]; } /** * Translates from a global EnumFacing to a local EnumFacing. * In the case of local, the faces are relative to the front, with north being the front face. */ private EnumFacing rotate(EnumFacing facing) { return facing; } @Override public boolean canInsertItem(int index, ItemStack itemStackIn, EnumFacing direction) { //Confirm we have an array of slots and the index is in-bounds. if(slots == null || index >= slots.length) return false; //Confirm this index is accessible for the direction. boolean foundSlot = false; int[] validSlots = getSlotsForFace(direction); for(int slot : validSlots) { if(slot == index) { foundSlot = true; break; } } return foundSlot && slots[index].isItemValid(itemStackIn); } @Override public boolean canExtractItem(int index, ItemStack stack, EnumFacing direction) { int[] validSlots = getSlotsForFace(direction); for(int slot : validSlots) if(slot == index) return true; return false; } @Override public NBTTagCompound writeToNBT(NBTTagCompound nbt) { super.writeToNBT(nbt); if(inventory != null) { NBTTagList nbtList = new NBTTagList(); for(int index = 0; index < inventory.length; index++) { if(inventory[index] != null) { NBTTagCompound tag = new NBTTagCompound(); tag.setByte("slot_index", (byte)index); inventory[index].writeToNBT(tag); nbtList.appendTag(tag); } } nbt.setTag("machine_inventory", nbtList); } nbt.setInteger("progress", progressTimer); if(energyStorage != null) { nbt.setInteger("energy", energyStorage.getEnergyStored()); nbt.setInteger("max_energy", energyStorage.getMaxEnergyStored()); } return nbt; } @Override public void readFromNBT(NBTTagCompound nbt) { super.readFromNBT(nbt); if(nbt.hasKey("machine_inventory")) { NBTTagList tagList = nbt.getTagList("machine_inventory", 10); inventory = new ItemStack[getSizeInventory()]; for(int i = 0; i < tagList.tagCount(); ++i) { NBTTagCompound tagCompound = tagList.getCompoundTagAt(i); byte slotIndex = tagCompound.getByte("slot_index"); if(slotIndex >= 0 && slotIndex < inventory.length) inventory[slotIndex] = ItemStack.loadItemStackFromNBT(tagCompound); } } if(nbt.hasKey("progress")) progressTimer = nbt.getInteger("progress"); if(energyStorage == null) energyStorage = new EnergyStorageModular(1); if(nbt.hasKey("max_energy")) energyStorage.setMaxCapacity(nbt.getInteger("max_energy")); if(nbt.hasKey("energy")) energyStorage.setEnergy(nbt.getInteger("energy")); } @Override public boolean hasCapability(net.minecraftforge.common.capabilities.Capability<?> capability, net.minecraft.util.EnumFacing facing) { return capability != null && capability == ITEM_HANDLER_CAPABILITY || capability == ENERGY_STORAGE_CAPABILITY || super.hasCapability(capability, facing); } @Override public <T> T getCapability(net.minecraftforge.common.capabilities.Capability<T> capability, net.minecraft.util.EnumFacing facing) { if(hasCapability(capability, facing)) { if(capability == ENERGY_STORAGE_CAPABILITY) { return ENERGY_STORAGE_CAPABILITY.cast(energyStorage); } else { if(facing == EnumFacing.UP) return ITEM_HANDLER_CAPABILITY.cast(invUP); if(facing == EnumFacing.DOWN) return ITEM_HANDLER_CAPABILITY.cast(invDOWN); if(facing == EnumFacing.NORTH) return ITEM_HANDLER_CAPABILITY.cast(invNORTH); if(facing == EnumFacing.SOUTH) return ITEM_HANDLER_CAPABILITY.cast(invSOUTH); if(facing == EnumFacing.EAST) return ITEM_HANDLER_CAPABILITY.cast(invEAST); if(facing == EnumFacing.WEST) return ITEM_HANDLER_CAPABILITY.cast(invWEST); } } return super.getCapability(capability, facing); } }