diff --git a/src/resonantinduction/battery/BatteryManager.java b/src/resonantinduction/battery/BatteryManager.java new file mode 100644 index 00000000..a52a84ac --- /dev/null +++ b/src/resonantinduction/battery/BatteryManager.java @@ -0,0 +1,39 @@ +package resonantinduction.battery; + +import net.minecraft.inventory.IInventory; +import net.minecraft.inventory.Slot; +import net.minecraft.item.ItemStack; +import universalelectricity.core.item.IElectricalItem; + +public class BatteryManager +{ + public static final int CELLS_PER_BATTERY = 16; + + public static class SlotOut extends Slot + { + public SlotOut(IInventory inventory, int index, int x, int y) + { + super(inventory, index, x, y); + } + + @Override + public boolean isItemValid(ItemStack itemstack) + { + return false; + } + } + + public static class SlotBattery extends Slot + { + public SlotBattery(IInventory inventory, int index, int x, int y) + { + super(inventory, index, x, y); + } + + @Override + public boolean isItemValid(ItemStack itemstack) + { + return itemstack.getItem() instanceof IElectricalItem; + } + } +} diff --git a/src/resonantinduction/battery/BatteryUpdateProtocol.java b/src/resonantinduction/battery/BatteryUpdateProtocol.java new file mode 100644 index 00000000..6aa3c731 --- /dev/null +++ b/src/resonantinduction/battery/BatteryUpdateProtocol.java @@ -0,0 +1,298 @@ +package resonantinduction.battery; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import net.minecraft.entity.item.EntityItem; +import net.minecraft.item.ItemStack; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.world.World; +import net.minecraftforge.common.ForgeDirection; +import resonantinduction.base.ListUtil; +import universalelectricity.api.vector.Vector3; + +public class BatteryUpdateProtocol +{ + /** The battery nodes that have already been iterated over. */ + public Set iteratedNodes = new HashSet(); + + /** The structures found, all connected by some nodes to the pointer. */ + public SynchronizedBatteryData structureFound = null; + + /** The original block the calculation is getting run from. */ + public TileEntity pointer; + + public BatteryUpdateProtocol(TileEntity tileEntity) + { + pointer = tileEntity; + } + + private void loopThrough(TileEntity tile) + { + if (structureFound == null) + { + World worldObj = tile.worldObj; + + int origX = tile.xCoord, origY = tile.yCoord, origZ = tile.zCoord; + + boolean isCorner = true; + boolean rightBlocks = true; + + Set locations = new HashSet(); + + int xmin = 0, xmax = 0, ymin = 0, ymax = 0, zmin = 0, zmax = 0; + + int x = 0, y = 0, z = 0; + + if ((isBattery(origX + 1, origY, origZ) && isBattery(origX - 1, origY, origZ)) || (isBattery(origX, origY + 1, origZ) && isBattery(origX, origY - 1, origZ)) || (isBattery(origX, origY, origZ + 1) && isBattery(origX, origY, origZ - 1))) + { + isCorner = false; + } + + if (isCorner) + { + if (isBattery(origX + 1, origY, origZ)) + { + xmin = 0; + + while (isBattery(origX + x + 1, origY, origZ)) + { + x++; + } + + xmax = x; + } + else + { + xmax = 0; + + while (isBattery(origX + x - 1, origY, origZ)) + { + x--; + } + + xmin = x; + } + + if (isBattery(origX, origY + 1, origZ)) + { + ymin = 0; + + while (isBattery(origX, origY + y + 1, origZ)) + { + y++; + } + + ymax = y; + } + else + { + ymax = 0; + + while (isBattery(origX, origY + y - 1, origZ)) + { + y--; + } + + ymin = y; + } + + if (isBattery(origX, origY, origZ + 1)) + { + zmin = 0; + + while (isBattery(origX, origY, origZ + z + 1)) + { + z++; + } + + zmax = z; + } + else + { + zmax = 0; + + while (isBattery(origX, origY, origZ + z - 1)) + { + z--; + } + + zmin = z; + } + + for (x = xmin; x <= xmax; x++) + { + for (y = ymin; y <= ymax; y++) + { + for (z = zmin; z <= zmax; z++) + { + if (!isBattery(origX + x, origY + y, origZ + z)) + { + rightBlocks = false; + break; + } + else + { + locations.add(new Vector3(tile).translate(new Vector3(x, y, z))); + } + } + + if (!rightBlocks) + { + break; + } + } + + if (!rightBlocks) + { + break; + } + } + } + + if (locations.size() >= 1 && locations.size() < 512) + { + if (rightBlocks && isCorner) + { + SynchronizedBatteryData structure = new SynchronizedBatteryData(); + structure.locations = locations; + structure.length = Math.abs(xmax - xmin) + 1; + structure.height = Math.abs(ymax - ymin) + 1; + structure.width = Math.abs(zmax - zmin) + 1; + + if (structure.getVolume() > 1) + { + structure.isMultiblock = true; + } + + if (structure.locations.contains(new Vector3(pointer))) + { + structureFound = structure; + } + } + } + } + + iteratedNodes.add((TileEntityBattery) tile); + + for (ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) + { + TileEntity tileEntity = new Vector3(tile).modifyPositionFromSide(side).getTileEntity(tile.worldObj); + + if (tileEntity instanceof TileEntityBattery) + { + if (!iteratedNodes.contains(tileEntity)) + { + loopThrough(tileEntity); + } + } + } + } + + private boolean isBattery(int x, int y, int z) + { + if (pointer.worldObj.getBlockTileEntity(x, y, z) instanceof TileEntityBattery) + { + return true; + } + + return false; + } + + private void disperseCells() + { + SynchronizedBatteryData oldStructure = null; + + for (TileEntityBattery tile : iteratedNodes) + { + if (tile.structure.isMultiblock) + { + oldStructure = tile.structure; + break; + } + } + + if (oldStructure != null) + { + int maxCells = iteratedNodes.size() * BatteryManager.CELLS_PER_BATTERY; + + List rejected = ListUtil.capRemains(oldStructure.inventory, maxCells); + ejectItems(rejected, new Vector3(pointer)); + + ArrayList> inventories = ListUtil.split(ListUtil.cap(oldStructure.inventory, maxCells), iteratedNodes.size()); + List iterList = ListUtil.asList(iteratedNodes); + + boolean didVisibleInventory = false; + + for (int i = 0; i < iterList.size(); i++) + { + TileEntityBattery tile = iterList.get(i); + tile.structure = SynchronizedBatteryData.getBase(tile, inventories.get(i)); + + if (!didVisibleInventory) + { + tile.structure.visibleInventory = oldStructure.visibleInventory; + didVisibleInventory = true; + } + } + } + } + + private void ejectItems(List items, Vector3 vec) + { + for (ItemStack itemStack : items) + { + float motion = 0.7F; + double motionX = (pointer.worldObj.rand.nextFloat() * motion) + (1.0F - motion) * 0.5D; + double motionY = (pointer.worldObj.rand.nextFloat() * motion) + (1.0F - motion) * 0.5D; + double motionZ = (pointer.worldObj.rand.nextFloat() * motion) + (1.0F - motion) * 0.5D; + + EntityItem entityItem = new EntityItem(pointer.worldObj, vec.x + motionX, vec.y + motionY, vec.z + motionZ, itemStack); + + pointer.worldObj.spawnEntityInWorld(entityItem); + } + } + + public void updateBatteries() + { + loopThrough(pointer); + + if (structureFound != null) + { + for (TileEntityBattery tileEntity : iteratedNodes) + { + if (!structureFound.locations.contains(new Vector3(tileEntity))) + { + disperseCells(); + + return; + } + } + + for (Vector3 obj : structureFound.locations) + { + TileEntityBattery tileEntity = (TileEntityBattery) obj.getTileEntity(pointer.worldObj); + + structureFound.inventory = ListUtil.merge(structureFound.inventory, tileEntity.structure.inventory); + + if (tileEntity.structure.hasVisibleInventory()) + { + structureFound.visibleInventory = tileEntity.structure.visibleInventory; + } + + tileEntity.structure = structureFound; + } + + List rejected = ListUtil.capRemains(structureFound.inventory, structureFound.getMaxCells()); + ejectItems(rejected, new Vector3(pointer)); + + structureFound.inventory = ListUtil.cap(structureFound.inventory, structureFound.getMaxCells()); + } + else + { + disperseCells(); + } + } +} diff --git a/src/resonantinduction/battery/BlockBattery.java b/src/resonantinduction/battery/BlockBattery.java new file mode 100644 index 00000000..b010f11d --- /dev/null +++ b/src/resonantinduction/battery/BlockBattery.java @@ -0,0 +1,225 @@ +/** + * + */ +package resonantinduction.battery; + +import net.minecraft.block.ITileEntityProvider; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.item.EntityItem; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.world.World; +import net.minecraftforge.common.ForgeDirection; +import resonantinduction.ResonantInduction; +import resonantinduction.api.ICapacitor; +import resonantinduction.base.BlockBase; +import resonantinduction.base.ListUtil; +import resonantinduction.render.BlockRenderingHandler; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; + +/** + * A block that detects power. + * + * @author Calclavia + * + */ +public class BlockBattery extends BlockBase implements ITileEntityProvider +{ + public BlockBattery(int id) + { + super("battery", id); + this.setTextureName(ResonantInduction.PREFIX + "machine"); + } + + @Override + public void onBlockClicked(World world, int x, int y, int z, EntityPlayer entityPlayer) + { + if (!world.isRemote) + { + if (!entityPlayer.capabilities.isCreativeMode) + { + TileEntityBattery tileEntity = (TileEntityBattery) world.getBlockTileEntity(x, y, z); + ItemStack itemStack = ListUtil.getTop(tileEntity.structure.inventory); + + if (tileEntity.structure.inventory.remove(itemStack)) + { + entityPlayer.dropPlayerItem(itemStack); + tileEntity.updateAllClients(); + } + } + } + } + + @Override + public boolean onBlockActivated(World world, int x, int y, int z, EntityPlayer entityPlayer, int side, float xClick, float yClick, float zClick) + { + TileEntityBattery tileEntity = (TileEntityBattery) world.getBlockTileEntity(x, y, z); + + if (entityPlayer.isSneaking()) + { + boolean result = tileEntity.toggleSide(ForgeDirection.getOrientation(side)); + + if (!world.isRemote) + { + entityPlayer.addChatMessage("Toggled side to: " + (result ? "input" : "output")); + } + + return true; + } + else + { + if (entityPlayer.getCurrentEquippedItem() != null) + { + if (entityPlayer.getCurrentEquippedItem().getItem() instanceof ICapacitor) + { + if (side != 0 && side != 1) + { + if (!world.isRemote) + { + if (tileEntity.structure.addCell(entityPlayer.getCurrentEquippedItem())) + { + entityPlayer.inventory.setInventorySlotContents(entityPlayer.inventory.currentItem, null); + tileEntity.updateAllClients(); + } + } + + /** + * Place cells into block. 2 Dimensional Click Zone. + * + * TODO: In the future. + */ + float xHit = 0; + float yHit = yClick; + + if (side == 2 || side == 3) + { + xHit = xClick; + + if (side == 2) + { + xHit = 1 - xHit; + } + } + else if (side == 4 || side == 5) + { + xHit = zClick; + + if (side == 5) + { + xHit = 1 - xHit; + } + } + + // Convert to quadrant coords. + xHit -= 0.5f; + yHit -= 0.5f; + + // Quadrant 1 + if (xHit > 0 && yHit > 0) + { + + }// Quadrant 2 + if (xHit > 0 && yHit < 0) + { + + }// Quadrant 3 + if (xHit < 0 && yHit < 0) + { + + }// Quadrant 4 + if (xHit < 0 && yHit > 0) + { + + } + return true; + } + } + } + } + + if (!world.isRemote) + { + entityPlayer.openGui(ResonantInduction.INSTANCE, 0, world, x, y, z); + } + + return true; + } + + @Override + public void onNeighborBlockChange(World world, int x, int y, int z, int id) + { + if (!world.isRemote) + { + if (id == blockID) + { + TileEntityBattery battery = (TileEntityBattery) world.getBlockTileEntity(x, y, z); + + battery.update(); + } + } + } + + @Override + public void onBlockPlacedBy(World world, int x, int y, int z, EntityLivingBase entityliving, ItemStack itemstack) + { + if (!world.isRemote) + { + TileEntityBattery battery = (TileEntityBattery) world.getBlockTileEntity(x, y, z); + + battery.update(); + } + } + + @Override + public boolean renderAsNormalBlock() + { + return false; + } + + @Override + public boolean isOpaqueCube() + { + return false; + } + + @Override + public boolean removeBlockByPlayer(World world, EntityPlayer player, int x, int y, int z) + { + if (!world.isRemote && canHarvestBlock(player, world.getBlockMetadata(x, y, z))) + { + TileEntityBattery tileEntity = (TileEntityBattery) world.getBlockTileEntity(x, y, z); + + if (!tileEntity.structure.isMultiblock) + { + for (ItemStack itemStack : tileEntity.structure.inventory) + { + float motion = 0.7F; + double motionX = (world.rand.nextFloat() * motion) + (1.0F - motion) * 0.5D; + double motionY = (world.rand.nextFloat() * motion) + (1.0F - motion) * 0.5D; + double motionZ = (world.rand.nextFloat() * motion) + (1.0F - motion) * 0.5D; + + EntityItem entityItem = new EntityItem(world, x + motionX, y + motionY, z + motionZ, itemStack); + + world.spawnEntityInWorld(entityItem); + } + } + } + + return super.removeBlockByPlayer(world, player, x, y, z); + } + + @Override + @SideOnly(Side.CLIENT) + public int getRenderType() + { + return BlockRenderingHandler.INSTANCE.getRenderId(); + } + + @Override + public TileEntity createNewTileEntity(World world) + { + return new TileEntityBattery(); + } +} diff --git a/src/resonantinduction/battery/ContainerBattery.java b/src/resonantinduction/battery/ContainerBattery.java new file mode 100644 index 00000000..d925c5a3 --- /dev/null +++ b/src/resonantinduction/battery/ContainerBattery.java @@ -0,0 +1,87 @@ +package resonantinduction.battery; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.inventory.ICrafting; +import net.minecraft.inventory.Slot; +import net.minecraft.item.ItemStack; +import resonantinduction.battery.BatteryManager.SlotBattery; +import resonantinduction.battery.BatteryManager.SlotOut; +import calclavia.lib.gui.ContainerBase; + +public class ContainerBattery extends ContainerBase +{ + private TileEntityBattery tileEntity; + + public ContainerBattery(InventoryPlayer inventory, TileEntityBattery unit) + { + super(unit); + tileEntity = unit; + addSlotToContainer(new SlotBattery(unit, 0, 8, 22)); + addSlotToContainer(new SlotOut(unit, 1, 8, 58)); + addSlotToContainer(new SlotBattery(unit, 2, 31, 22)); + addSlotToContainer(new SlotBattery(unit, 3, 31, 58)); + + int slotX; + + for (slotX = 0; slotX < 3; ++slotX) + { + for (int slotY = 0; slotY < 9; ++slotY) + { + addSlotToContainer(new Slot(inventory, slotY + slotX * 9 + 9, 8 + slotY * 18, 125 + slotX * 18)); + } + } + + for (slotX = 0; slotX < 9; ++slotX) + { + addSlotToContainer(new Slot(inventory, slotX, 8 + slotX * 18, 183)); + } + + tileEntity.openChest(); + tileEntity.playersUsing.add(inventory.player); + } + + @Override + public ItemStack slotClick(int slotID, int par2, int par3, EntityPlayer par4EntityPlayer) + { + ItemStack stack = super.slotClick(slotID, par2, par3, par4EntityPlayer); + + if (slotID == 1) + { + ItemStack itemstack = ((Slot) inventorySlots.get(slotID)).getStack(); + ItemStack itemstack1 = itemstack == null ? null : itemstack.copy(); + inventoryItemStacks.set(slotID, itemstack1); + + for (int j = 0; j < crafters.size(); ++j) + { + ((ICrafting) crafters.get(j)).sendSlotContents(this, slotID, itemstack1); + } + } + + return stack; + } + + @Override + public void onContainerClosed(EntityPlayer entityplayer) + { + super.onContainerClosed(entityplayer); + tileEntity.closeChest(); + tileEntity.playersUsing.remove(entityplayer); + } + + @Override + public boolean canInteractWith(EntityPlayer entityplayer) + { + return tileEntity.isUseableByPlayer(entityplayer); + } + + @Override + public ItemStack transferStackInSlot(EntityPlayer par1EntityPlayer, int slotID) + { + if (slotID != 1) + { + return super.transferStackInSlot(par1EntityPlayer, slotID); + } + return null; + } +} diff --git a/src/resonantinduction/battery/ItemCapacitor.java b/src/resonantinduction/battery/ItemCapacitor.java new file mode 100644 index 00000000..38fa8822 --- /dev/null +++ b/src/resonantinduction/battery/ItemCapacitor.java @@ -0,0 +1,43 @@ +/** + * + */ +package resonantinduction.battery; + +import net.minecraft.item.ItemStack; +import net.minecraftforge.common.Configuration; +import resonantinduction.ResonantInduction; +import resonantinduction.TabRI; +import resonantinduction.api.ICapacitor; +import universalelectricity.compatibility.ItemUniversalElectric; + +/** + * Stores power. + * + * @author Calclavia + * + */ +public class ItemCapacitor extends ItemUniversalElectric implements ICapacitor +{ + public ItemCapacitor(int id) + { + super(ResonantInduction.CONFIGURATION.get(Configuration.CATEGORY_ITEM, "capacitor", id).getInt(id)); + this.setCreativeTab(TabRI.INSTANCE); + this.setUnlocalizedName(ResonantInduction.PREFIX + "capacitor"); + this.setTextureName(ResonantInduction.PREFIX + "capacitor"); + this.setMaxStackSize(1); + this.setMaxDamage(100); + } + + @Override + public float getTransfer(ItemStack itemStack) + { + return this.getMaxEnergyStored(itemStack) * 0.05F; + } + + @Override + public float getElectricityCapacity(ItemStack theItem) + { + return 500; + } + +} diff --git a/src/resonantinduction/battery/ItemInfiniteCapacitor.java b/src/resonantinduction/battery/ItemInfiniteCapacitor.java new file mode 100644 index 00000000..0a2776b6 --- /dev/null +++ b/src/resonantinduction/battery/ItemInfiniteCapacitor.java @@ -0,0 +1,81 @@ +/** + * + */ +package resonantinduction.battery; + +import java.util.List; + +import net.minecraft.creativetab.CreativeTabs; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraftforge.common.Configuration; +import resonantinduction.ResonantInduction; +import resonantinduction.TabRI; +import universalelectricity.compatibility.ItemUniversalElectric; + +/** + * Stores power. + * + * @author Calclavia + * + */ +public class ItemInfiniteCapacitor extends ItemUniversalElectric +{ + public ItemInfiniteCapacitor(int id) + { + super(ResonantInduction.CONFIGURATION.get(Configuration.CATEGORY_ITEM, "infiniteCapacitor", id).getInt(id)); + this.setCreativeTab(TabRI.INSTANCE); + this.setUnlocalizedName(ResonantInduction.PREFIX + "infiniteCapacitor"); + this.setTextureName(ResonantInduction.PREFIX + "capacitor"); + this.setMaxStackSize(1); + this.setMaxDamage(100); + } + + @Override + public void addInformation(ItemStack itemStack, EntityPlayer entityPlayer, List list, boolean par4) + { + list.add("Infinite"); + } + + @Override + public float recharge(ItemStack itemStack, float energy, boolean doReceive) + { + return energy; + } + + @Override + public float discharge(ItemStack itemStack, float energy, boolean doTransfer) + { + return energy; + } + + @Override + public void setElectricity(ItemStack itemStack, float joules) + { + + } + + @Override + public float getTransfer(ItemStack itemStack) + { + return Float.POSITIVE_INFINITY; + } + + @Override + public float getElectricityStored(ItemStack itemStack) + { + return Float.POSITIVE_INFINITY; + } + + @Override + public float getElectricityCapacity(ItemStack theItem) + { + return Float.POSITIVE_INFINITY; + } + + @Override + public void getSubItems(int par1, CreativeTabs par2CreativeTabs, List par3List) + { + par3List.add(new ItemStack(this)); + } +} diff --git a/src/resonantinduction/battery/SynchronizedBatteryData.java b/src/resonantinduction/battery/SynchronizedBatteryData.java new file mode 100644 index 00000000..c222a7b2 --- /dev/null +++ b/src/resonantinduction/battery/SynchronizedBatteryData.java @@ -0,0 +1,163 @@ +package resonantinduction.battery; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import net.minecraft.item.ItemStack; +import resonantinduction.base.ListUtil; +import universalelectricity.api.vector.Vector3; +import universalelectricity.core.item.IElectricalItem; + +public class SynchronizedBatteryData +{ + public Set locations = new HashSet(); + + public List inventory = new ArrayList(); + + /** + * Slot 0: Cell input slot Slot 1: Battery charge slot Slot 2: Battery discharge slot + */ + public ItemStack[] visibleInventory = new ItemStack[3]; + + public int length; + + public int width; + + public int height; + + public ItemStack tempStack; + + public boolean isMultiblock; + + public boolean didTick; + + public boolean wroteInventory; + + public int getVolume() + { + return length * width * height; + } + + public int getMaxCells() + { + return getVolume() * BatteryManager.CELLS_PER_BATTERY; + } + + public boolean addCell(ItemStack cell) + { + if (this.inventory.size() < this.getMaxCells()) + { + this.inventory.add(cell); + this.sortInventory(); + return true; + } + + return false; + } + + public void sortInventory() + { + Object[] array = ListUtil.copy(inventory).toArray(); + + ItemStack[] toSort = new ItemStack[array.length]; + + for (int i = 0; i < array.length; i++) + { + toSort[i] = (ItemStack) array[i]; + } + + boolean cont = true; + ItemStack temp; + + while (cont) + { + cont = false; + + for (int i = 0; i < toSort.length - 1; i++) + { + if (((IElectricalItem) toSort[i].getItem()).getElectricityStored(toSort[i]) < ((IElectricalItem) toSort[i + 1].getItem()).getElectricityStored(toSort[i + 1])) + { + temp = toSort[i]; + toSort[i] = toSort[i + 1]; + toSort[i + 1] = temp; + cont = true; + } + } + } + + inventory = new ArrayList(); + + for (ItemStack itemStack : toSort) + { + inventory.add(itemStack); + } + } + + public boolean hasVisibleInventory() + { + for (ItemStack itemStack : visibleInventory) + { + if (itemStack != null) + { + return true; + } + } + + return false; + } + + public static SynchronizedBatteryData getBase(TileEntityBattery tileEntity, List inventory) + { + SynchronizedBatteryData structure = getBase(tileEntity); + structure.inventory = inventory; + + return structure; + } + + public static SynchronizedBatteryData getBase(TileEntityBattery tileEntity) + { + SynchronizedBatteryData structure = new SynchronizedBatteryData(); + structure.length = 1; + structure.width = 1; + structure.height = 1; + structure.locations.add(new Vector3(tileEntity)); + + return structure; + } + + @Override + public int hashCode() + { + int code = 1; + code = 31 * locations.hashCode(); + code = 31 * length; + code = 31 * width; + code = 31 * height; + return code; + } + + @Override + public boolean equals(Object obj) + { + if (!(obj instanceof SynchronizedBatteryData)) + { + return false; + } + + SynchronizedBatteryData data = (SynchronizedBatteryData) obj; + + if (!data.locations.equals(locations)) + { + return false; + } + + if (data.length != length || data.width != width || data.height != height) + { + return false; + } + + return true; + } +} diff --git a/src/resonantinduction/battery/TileEntityBattery.java b/src/resonantinduction/battery/TileEntityBattery.java new file mode 100644 index 00000000..a7c55796 --- /dev/null +++ b/src/resonantinduction/battery/TileEntityBattery.java @@ -0,0 +1,692 @@ +/** + * + */ +package resonantinduction.battery; + +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.item.EntityItem; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.IInventory; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraft.util.AxisAlignedBB; +import net.minecraftforge.common.ForgeDirection; +import resonantinduction.ResonantInduction; +import resonantinduction.api.ICapacitor; +import resonantinduction.base.ListUtil; +import universalelectricity.api.electricity.ElectricityPack; +import universalelectricity.api.vector.Vector3; +import universalelectricity.core.item.IElectricalItem; +import calclavia.lib.network.IPacketReceiver; +import calclavia.lib.network.IPacketSender; +import calclavia.lib.tile.TileEntityElectrical; + +import com.google.common.io.ByteArrayDataInput; + +import cpw.mods.fml.common.network.PacketDispatcher; +import cpw.mods.fml.common.network.Player; + +/** + * A modular battery with no GUI. + * + * @author AidanBrady + */ +public class TileEntityBattery extends TileEntityElectrical implements IPacketSender, IPacketReceiver, IInventory +{ + public Set playersUsing = new HashSet(); + + public SynchronizedBatteryData structure = SynchronizedBatteryData.getBase(this); + + public SynchronizedBatteryData prevStructure; + + public float clientEnergy; + public int clientCells; + public float clientMaxEnergy; + + private EnumSet inputSides = EnumSet.allOf(ForgeDirection.class); + + @Override + public void updateEntity() + { + super.updateEntity(); + + if (!this.worldObj.isRemote) + { + if (this.ticks == 5 && !this.structure.isMultiblock) + { + this.update(); + } + + if (this.structure.visibleInventory[0] != null) + { + if (structure.inventory.size() < structure.getMaxCells()) + { + if (structure.visibleInventory[0].getItem() instanceof ICapacitor) + { + structure.inventory.add(structure.visibleInventory[0]); + structure.visibleInventory[0] = null; + structure.sortInventory(); + updateAllClients(); + } + } + } + + /** + * Attempt to charge entities above it. + */ + ItemStack chargeItem = null; + + if (this.worldObj.isBlockIndirectlyGettingPowered(this.xCoord, this.yCoord, this.zCoord)) + { + List entities = this.worldObj.getEntitiesWithinAABB(Entity.class, AxisAlignedBB.getBoundingBox(this.xCoord, this.yCoord + 1, this.zCoord, this.xCoord + 1, this.yCoord + 2, this.zCoord + 1)); + + electricItemLoop: + for (Entity entity : entities) + { + if (entity instanceof EntityPlayer) + { + IInventory inventory = ((EntityPlayer) entity).inventory; + for (int i = 0; i < inventory.getSizeInventory(); i++) + { + ItemStack checkStack = inventory.getStackInSlot(i); + + if (checkStack != null) + { + if (checkStack.getItem() instanceof IElectricalItem) + { + if (((IElectricalItem) checkStack.getItem()).recharge(checkStack, provideElectricity(this.getTransferThreshhold(), false).getWatts(), false) > 0) + { + chargeItem = checkStack; + break electricItemLoop; + } + } + } + } + } + else if (entity instanceof EntityItem) + { + ItemStack checkStack = ((EntityItem) entity).getEntityItem(); + + if (checkStack != null) + { + if (checkStack.getItem() instanceof IElectricalItem) + { + if (((IElectricalItem) checkStack.getItem()).recharge(checkStack, provideElectricity(this.getTransferThreshhold(), false).getWatts(), false) > 0) + { + chargeItem = checkStack; + break electricItemLoop; + } + } + } + } + } + } + + if (chargeItem == null) + { + chargeItem = this.structure.visibleInventory[1]; + } + + if (chargeItem != null) + { + ItemStack itemStack = chargeItem; + IElectricalItem battery = (IElectricalItem) itemStack.getItem(); + + float energyStored = getMaxEnergyStored(); + float batteryNeeded = battery.recharge(itemStack, provideElectricity(this.getTransferThreshhold(), false).getWatts(), false); + float toGive = Math.min(energyStored, Math.min(battery.getTransfer(itemStack), batteryNeeded)); + battery.recharge(itemStack, provideElectricity(toGive, true).getWatts(), true); + } + + if (structure.visibleInventory[2] != null) + { + ItemStack itemStack = structure.visibleInventory[2]; + IElectricalItem battery = (IElectricalItem) itemStack.getItem(); + + float energyNeeded = getMaxEnergyStored() - getEnergyStored(); + float batteryStored = battery.getElectricityStored(itemStack); + float toReceive = Math.min(energyNeeded, Math.min(this.getTransferThreshhold(), Math.min(battery.getTransfer(itemStack), batteryStored))); + battery.discharge(itemStack, receiveElectricity(toReceive, true), true); + } + + if (prevStructure != structure) + { + for (EntityPlayer player : playersUsing) + { + player.closeScreen(); + } + + updateClient(); + } + + this.prevStructure = structure; + + this.structure.wroteInventory = false; + this.structure.didTick = false; + + if (this.playersUsing.size() > 0) + { + updateClient(); + } + + for (EntityPlayer player : this.playersUsing) + { + PacketDispatcher.sendPacketToPlayer(ResonantInduction.PACKET_TILE.getPacket(this, this.getPacketData(0).toArray()), (Player) player); + } + + this.produce(); + } + } + + public float getTransferThreshhold() + { + return this.structure.getVolume() * 50; + } + + public void updateClient() + { + PacketDispatcher.sendPacketToAllPlayers(ResonantInduction.PACKET_TILE.getPacket(this, getPacketData(0).toArray())); + } + + public void updateAllClients() + { + for (Vector3 vec : structure.locations) + { + TileEntityBattery battery = (TileEntityBattery) vec.getTileEntity(worldObj); + PacketDispatcher.sendPacketToAllPlayers(ResonantInduction.PACKET_TILE.getPacket(battery, battery.getPacketData(0).toArray())); + } + } + + @Override + public void readFromNBT(NBTTagCompound nbtTags) + { + super.readFromNBT(nbtTags); + + // Main inventory + if (nbtTags.hasKey("Items")) + { + NBTTagList tagList = nbtTags.getTagList("Items"); + structure.inventory = new ArrayList(); + + for (int tagCount = 0; tagCount < tagList.tagCount(); tagCount++) + { + NBTTagCompound tagCompound = (NBTTagCompound) tagList.tagAt(tagCount); + int slotID = tagCompound.getInteger("Slot"); + structure.inventory.add(slotID, ItemStack.loadItemStackFromNBT(tagCompound)); + } + } + + // Visible inventory + if (nbtTags.hasKey("VisibleItems")) + { + NBTTagList tagList = nbtTags.getTagList("VisibleItems"); + structure.visibleInventory = new ItemStack[3]; + + for (int tagCount = 0; tagCount < tagList.tagCount(); tagCount++) + { + NBTTagCompound tagCompound = (NBTTagCompound) tagList.tagAt(tagCount); + byte slotID = tagCompound.getByte("Slot"); + + if (slotID >= 0 && slotID < structure.visibleInventory.length) + { + if (slotID == 0) + { + setInventorySlotContents(slotID, ItemStack.loadItemStackFromNBT(tagCompound)); + } + else + { + setInventorySlotContents(slotID + 1, ItemStack.loadItemStackFromNBT(tagCompound)); + } + } + } + } + + this.inputSides = EnumSet.noneOf(ForgeDirection.class); + + NBTTagList tagList = nbtTags.getTagList("inputSides"); + + for (int tagCount = 0; tagCount < tagList.tagCount(); tagCount++) + { + NBTTagCompound tagCompound = (NBTTagCompound) tagList.tagAt(tagCount); + byte side = tagCompound.getByte("side"); + this.inputSides.add(ForgeDirection.getOrientation(side)); + } + + this.inputSides.remove(ForgeDirection.UNKNOWN); + } + + @Override + public void writeToNBT(NBTTagCompound nbt) + { + super.writeToNBT(nbt); + + if (!structure.wroteInventory) + { + // Inventory + if (structure.inventory != null) + { + NBTTagList tagList = new NBTTagList(); + + for (int slotCount = 0; slotCount < structure.inventory.size(); slotCount++) + { + if (structure.inventory.get(slotCount) != null) + { + NBTTagCompound tagCompound = new NBTTagCompound(); + tagCompound.setInteger("Slot", slotCount); + structure.inventory.get(slotCount).writeToNBT(tagCompound); + tagList.appendTag(tagCompound); + } + } + + nbt.setTag("Items", tagList); + } + + // Visible inventory + if (structure.visibleInventory != null) + { + NBTTagList tagList = new NBTTagList(); + + for (int slotCount = 0; slotCount < structure.visibleInventory.length; slotCount++) + { + if (slotCount > 0) + { + slotCount++; + } + + if (getStackInSlot(slotCount) != null) + { + NBTTagCompound tagCompound = new NBTTagCompound(); + tagCompound.setByte("Slot", (byte) slotCount); + getStackInSlot(slotCount).writeToNBT(tagCompound); + tagList.appendTag(tagCompound); + } + } + + nbt.setTag("VisibleItems", tagList); + } + + structure.wroteInventory = true; + + /** + * Save the input sides. + */ + NBTTagList tagList = new NBTTagList(); + Iterator it = this.inputSides.iterator(); + + while (it.hasNext()) + { + ForgeDirection dir = it.next(); + + if (dir != ForgeDirection.UNKNOWN) + { + NBTTagCompound tagCompound = new NBTTagCompound(); + tagCompound.setByte("side", (byte) dir.ordinal()); + tagList.appendTag(tagCompound); + } + } + + nbt.setTag("inputSides", tagList); + } + } + + public void update() + { + if (!worldObj.isRemote && (structure == null || !structure.didTick)) + { + new BatteryUpdateProtocol(this).updateBatteries(); + + if (structure != null) + { + structure.didTick = true; + } + } + } + + @Override + public float receiveElectricity(ElectricityPack receive, boolean doAdd) + { + float amount = receive.getWatts(); + float added = 0; + + for (ItemStack itemStack : structure.inventory) + { + if (itemStack.getItem() instanceof IElectricalItem) + { + IElectricalItem battery = (IElectricalItem) itemStack.getItem(); + + float needed = amount - added; + float itemAdd = Math.min(battery.getElectricityCapacity(itemStack) - battery.getElectricityStored(itemStack), needed); + + if (doAdd) + { + battery.setElectricity(itemStack, battery.getElectricityStored(itemStack) + itemAdd); + } + + added += itemAdd; + + if (amount == added) + { + break; + } + } + } + + return added; + } + + @Override + public ElectricityPack provideElectricity(ElectricityPack pack, boolean doRemove) + { + float amount = pack.getWatts(); + + List inverse = ListUtil.inverse(structure.inventory); + + float removed = 0; + for (ItemStack itemStack : inverse) + { + if (itemStack.getItem() instanceof IElectricalItem) + { + IElectricalItem battery = (IElectricalItem) itemStack.getItem(); + + float needed = amount - removed; + float itemRemove = Math.min(battery.getElectricityStored(itemStack), needed); + + if (doRemove) + { + battery.setElectricity(itemStack, battery.getElectricityStored(itemStack) - itemRemove); + } + + removed += itemRemove; + + if (amount == removed) + { + break; + } + } + } + + return ElectricityPack.getFromWatts(removed, this.getVoltage()); + } + + @Override + public float getMaxEnergyStored() + { + if (!this.worldObj.isRemote) + { + float max = 0; + + for (ItemStack itemStack : this.structure.inventory) + { + if (itemStack != null) + { + if (itemStack.getItem() instanceof IElectricalItem) + { + max += ((IElectricalItem) itemStack.getItem()).getElectricityCapacity(itemStack); + } + } + } + + return max; + } + else + { + return this.clientMaxEnergy; + } + } + + @Override + public float getEnergyStored() + { + if (!this.worldObj.isRemote) + { + float energy = 0; + + for (ItemStack itemStack : this.structure.inventory) + { + if (itemStack != null) + { + if (itemStack.getItem() instanceof IElectricalItem) + { + energy += ((IElectricalItem) itemStack.getItem()).getElectricityStored(itemStack); + } + } + } + + return energy; + } + else + { + return clientEnergy; + } + } + + @Override + public void onReceivePacket(ByteArrayDataInput data, EntityPlayer player) + { + structure.isMultiblock = data.readBoolean(); + + clientEnergy = data.readFloat(); + clientCells = data.readInt(); + clientMaxEnergy = data.readFloat(); + + structure.height = data.readInt(); + structure.length = data.readInt(); + structure.width = data.readInt(); + } + + @Override + public ArrayList getPacketData(int type) + { + ArrayList data = new ArrayList(); + data.add(structure.isMultiblock); + + data.add(getEnergyStored()); + data.add(structure.inventory.size()); + data.add(getMaxEnergyStored()); + + data.add(structure.height); + data.add(structure.length); + data.add(structure.width); + + return data; + } + + @Override + public int getSizeInventory() + { + return 4; + } + + @Override + public ItemStack getStackInSlot(int i) + { + if (i == 0) + { + return structure.visibleInventory[0]; + } + else if (i == 1) + { + if (!worldObj.isRemote) + { + return ListUtil.getTop(structure.inventory); + } + else + { + return structure.tempStack; + } + } + else + { + return structure.visibleInventory[i - 1]; + } + } + + @Override + public ItemStack decrStackSize(int slotID, int amount) + { + if (getStackInSlot(slotID) != null) + { + ItemStack tempStack; + + if (getStackInSlot(slotID).stackSize <= amount) + { + tempStack = getStackInSlot(slotID); + setInventorySlotContents(slotID, null); + return tempStack; + } + else + { + tempStack = getStackInSlot(slotID).splitStack(amount); + + if (getStackInSlot(slotID).stackSize == 0) + { + setInventorySlotContents(slotID, null); + } + + return tempStack; + } + } + else + { + return null; + } + } + + @Override + public ItemStack getStackInSlotOnClosing(int i) + { + return getStackInSlot(i); + } + + @Override + public void setInventorySlotContents(int i, ItemStack itemstack) + { + if (i == 0) + { + structure.visibleInventory[0] = itemstack; + } + else if (i == 1) + { + if (itemstack == null) + { + if (!worldObj.isRemote) + { + structure.inventory.remove(ListUtil.getTop(structure.inventory)); + } + else + { + structure.tempStack = null; + } + } + else + { + if (worldObj.isRemote) + { + structure.tempStack = itemstack; + } + } + } + else + { + structure.visibleInventory[i - 1] = itemstack; + } + } + + @Override + public String getInvName() + { + return "Battery"; + } + + @Override + public boolean isInvNameLocalized() + { + return false; + } + + @Override + public int getInventoryStackLimit() + { + return 64; + } + + @Override + public boolean isUseableByPlayer(EntityPlayer entityplayer) + { + return true; + } + + @Override + public void openChest() + { + } + + @Override + public void closeChest() + { + } + + @Override + public boolean isItemValidForSlot(int i, ItemStack itemsSack) + { + return itemsSack.getItem() instanceof IElectricalItem; + } + + @Override + public float getRequest(ForgeDirection direction) + { + if (this.getInputDirections().contains(direction)) + { + return Math.min(this.getMaxEnergyStored() - this.getEnergyStored(), this.getTransferThreshhold()); + } + return 0; + } + + @Override + public float getProvide(ForgeDirection direction) + { + if (this.getOutputDirections().contains(direction)) + { + return Math.min(this.getEnergyStored(), this.getTransferThreshhold()); + } + + return 0; + } + + @Override + public EnumSet getInputDirections() + { + return this.inputSides; + } + + @Override + public EnumSet getOutputDirections() + { + return EnumSet.complementOf(this.inputSides); + } + + /** + * Toggles the input/output sides of the battery. + */ + public boolean toggleSide(ForgeDirection orientation) + { + if (this.inputSides.contains(orientation)) + { + this.inputSides.remove(orientation); + return false; + } + else + { + this.inputSides.add(orientation); + return true; + } + } +}