package zmaster587.advancedRocketry.tile.multiblock; import io.netty.buffer.ByteBuf; import java.util.LinkedList; import java.util.List; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.Blocks; import net.minecraft.inventory.IInventory; import net.minecraft.inventory.ISidedInventory; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraft.network.NetworkManager; import net.minecraft.network.Packet; import net.minecraft.network.play.server.S35PacketUpdateTileEntity; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.AxisAlignedBB; import net.minecraft.world.ChunkCoordIntPair; import net.minecraft.world.WorldServer; import net.minecraftforge.common.DimensionManager; import net.minecraftforge.common.ForgeChunkManager; import net.minecraftforge.common.ForgeChunkManager.Ticket; import net.minecraftforge.common.ForgeChunkManager.Type; import net.minecraftforge.common.util.ForgeDirection; import net.minecraftforge.oredict.OreDictionary; import zmaster587.advancedRocketry.AdvancedRocketry; import zmaster587.advancedRocketry.api.AdvancedRocketryItems; import zmaster587.advancedRocketry.api.Configuration; import zmaster587.advancedRocketry.integration.CompatibilityMgr; import zmaster587.advancedRocketry.inventory.TextureResources; import zmaster587.advancedRocketry.satellite.SatelliteLaser; import zmaster587.advancedRocketry.satellite.SatelliteLaserNoDrill; import zmaster587.advancedRocketry.stations.SpaceObjectManager; import zmaster587.advancedRocketry.world.provider.WorldProviderSpace; import zmaster587.libVulpes.LibVulpes; import zmaster587.libVulpes.api.LibVulpesBlocks; import zmaster587.libVulpes.block.RotatableBlock; import zmaster587.libVulpes.compat.InventoryCompat; import zmaster587.libVulpes.inventory.modules.IButtonInventory; import zmaster587.libVulpes.inventory.modules.IGuiCallback; import zmaster587.libVulpes.inventory.modules.IModularInventory; import zmaster587.libVulpes.inventory.modules.ModuleBase; import zmaster587.libVulpes.inventory.modules.ModuleButton; import zmaster587.libVulpes.inventory.modules.ModuleImage; import zmaster587.libVulpes.inventory.modules.ModuleNumericTextbox; import zmaster587.libVulpes.inventory.modules.ModulePower; import zmaster587.libVulpes.inventory.modules.ModuleSlotArray; import zmaster587.libVulpes.inventory.modules.ModuleText; import zmaster587.libVulpes.inventory.modules.ModuleTextBox; import zmaster587.libVulpes.network.PacketHandler; import zmaster587.libVulpes.network.PacketMachine; import zmaster587.libVulpes.tile.multiblock.TileMultiPowerConsumer; import zmaster587.libVulpes.util.INetworkMachine; import zmaster587.libVulpes.util.MultiInventory; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; public class TileSpaceLaser extends TileMultiPowerConsumer implements ISidedInventory, INetworkMachine, IModularInventory, IGuiCallback, IButtonInventory { private static final int INVSIZE = 9; ItemStack glassPanel; //ItemStack invBuffer[]; SatelliteLaserNoDrill laserSat; protected boolean isRunning, finished; protected IInventory adjInv; private int radius, xCenter, yCenter, numSteps; private ForgeDirection prevDir; public int laserX, laserZ, tickSinceLastOperation; private static final ForgeDirection[] VALID_INVENTORY_DIRECTIONS = { ForgeDirection.NORTH, ForgeDirection.EAST, ForgeDirection.SOUTH, ForgeDirection.WEST}; private static final int POWER_PER_OPERATION = (int) (10000 * Configuration.spaceLaserPowerMult); private ModuleTextBox locationX, locationZ; private ModuleText updateText; MultiInventory inv; Object[][][] structure = new Object[][][]{ { {null, null, null, null, null}, {null, null, LibVulpesBlocks.blockAdvStructureBlock, null, null}, {null, LibVulpesBlocks.blockAdvStructureBlock, LibVulpesBlocks.blockAdvStructureBlock, LibVulpesBlocks.blockAdvStructureBlock, null}, {null, null, LibVulpesBlocks.blockAdvStructureBlock, null, null}, {null, null, null, null, null} }, { {null, null,'c', null, null}, {null, 'P', Blocks.glass, 'P', null}, {'P', LibVulpesBlocks.blockAdvStructureBlock, Blocks.glass, LibVulpesBlocks.blockAdvStructureBlock, 'P'}, {null, 'P', LibVulpesBlocks.blockAdvStructureBlock, 'P', null}, {null, null, 'P', null, null} }, { {null, null, LibVulpesBlocks.blockAdvStructureBlock, null, null}, {null, LibVulpesBlocks.blockAdvStructureBlock, LibVulpesBlocks.blockAdvStructureBlock, LibVulpesBlocks.blockAdvStructureBlock, null}, {LibVulpesBlocks.blockAdvStructureBlock, LibVulpesBlocks.blockAdvStructureBlock, Blocks.glass, LibVulpesBlocks.blockAdvStructureBlock, LibVulpesBlocks.blockAdvStructureBlock}, {null, LibVulpesBlocks.blockAdvStructureBlock, LibVulpesBlocks.blockAdvStructureBlock, LibVulpesBlocks.blockAdvStructureBlock, null}, {null, null, LibVulpesBlocks.blockAdvStructureBlock, null, null} }, { {null, null, 'O', null, null}, {null, LibVulpesBlocks.blockAdvStructureBlock, Blocks.glass, LibVulpesBlocks.blockAdvStructureBlock, null}, {'O', Blocks.glass, Blocks.glass, Blocks.glass, 'O'}, {null, LibVulpesBlocks.blockAdvStructureBlock, Blocks.glass, LibVulpesBlocks.blockAdvStructureBlock, null}, {null, null, 'O', null, null} }, }; public enum MODE{ SINGLE, LINE_X, LINE_Z, SPIRAL }; private MODE mode; Ticket ticket; public TileSpaceLaser() { super(); glassPanel = null; //invBuffer = new ItemStack[INVSIZE]; radius = 0; xCenter = 0; yCenter = 0; numSteps = 0; prevDir = ForgeDirection.UNKNOWN; inv = new MultiInventory(itemOutPorts); tickSinceLastOperation = 0; laserX = 0; laserZ = 0; if(Configuration.laserDrillPlanet) laserSat = new SatelliteLaser(inv); else laserSat = new SatelliteLaserNoDrill(inv); isRunning = false; finished = false; mode = MODE.SINGLE; } //Required so we see the laser @SideOnly(Side.CLIENT) @Override public AxisAlignedBB getRenderBoundingBox() { return AxisAlignedBB.getBoundingBox(this.xCoord -5, this.yCoord - 100, this.zCoord - 5, this.xCoord + 5, this.yCoord +5, this.zCoord + 5); } @Override public Object[][][] getStructure() { return structure; } @Override public String getMachineName() { return getInventoryName(); } /* * ID 10: client changed xcoord in interface * ID 11: client changed ycoord in interface * ID 12: sync whether the machine is running * ID 13: sync Mode * ID 14: jam reset */ @Override public void writeDataToNetwork(ByteBuf out, byte id) { super.writeDataToNetwork(out, id); if(id == 10) out.writeInt(this.laserX); else if(id == 11) out.writeInt(this.laserZ); else if(id == 12) out.writeBoolean(isRunning); else if(id == 13) out.writeInt(mode.ordinal()); } @Override public void readDataFromNetwork(ByteBuf in, byte id, NBTTagCompound nbt) { super.readDataFromNetwork(in, id, nbt); if(id == 10) nbt.setInteger("laserX", in.readInt()); else if(id == 11) nbt.setInteger("laserZ", in.readInt()); else if(id == 12) nbt.setBoolean("isRunning", in.readBoolean()); else if(id == 13) nbt.setInteger("mode", in.readInt()); } @Override public void useNetworkData(EntityPlayer player, Side side, byte id, NBTTagCompound nbt) { super.useNetworkData(player, side, id, nbt); if(id == 10) { this.laserX = nbt.getInteger("laserX"); finished = false; if(mode == MODE.SPIRAL) resetSpiral(); } else if(id == 11) { this.laserZ = nbt.getInteger("laserZ"); finished = false; if(mode == MODE.SPIRAL) resetSpiral(); } else if(id == 12) this.isRunning = nbt.getBoolean("isRunning"); else if(id == 13 && !isRunning()) this.mode = MODE.values()[nbt.getInteger("mode")]; else if(id == 14) this.attempUnjam(); markDirty(); } private void resetSpiral() { radius = 0; prevDir = ForgeDirection.UNKNOWN; xCenter = 0; yCenter = 0; numSteps = 0; } @Override public boolean isRunning() {return isRunning && isComplete();} public boolean isFinished() {return finished;} public MODE getMode() {return mode;} public void incrementMode() { if(mode == MODE.SPIRAL) resetSpiral(); int num = mode.ordinal(); num++; if(num >= MODE.values().length) num = 0; mode = MODE.values()[num]; } public void decrementMode() { if(mode == MODE.SPIRAL) resetSpiral(); int num = mode.ordinal(); num--; if(num < 0) num = MODE.values().length - 1; mode = MODE.values()[num]; } public void setMode(MODE m) {mode = m;}; @Override public boolean canUpdate() {return true;} public void setFinished(boolean value) { finished = value; } private void setRunning(boolean value) { this.isRunning = value; markDirty(); this.worldObj.markBlockForUpdate(xCoord, yCoord, zCoord); } @Override public void updateEntity() { //TODO: drain energy //Freaky jenky crap to make sure the multiblock loads on chunkload etc if(timeAlive == 0 && !worldObj.isRemote) { if(isComplete()) canRender = completeStructure = completeStructure(); timeAlive = 0x1; checkCanRun(); } if(!this.worldObj.isRemote) { tickSinceLastOperation++; if(!isAllowedToRun()) { laserSat.deactivateLaser(); this.setFinished(true); this.setRunning(false); } else if(hasPowerForOperation() && isReadyForOperation() && laserSat.isAlive() && !laserSat.getJammed()) { laserSat.performOperation(); batteries.extractEnergy(POWER_PER_OPERATION, false); tickSinceLastOperation = 0; } } if(laserSat.isFinished()) { setRunning(false); laserSat.deactivateLaser(); if(!laserSat.getJammed()) { if(mode == MODE.SINGLE) finished = true; if(this.worldObj.getBlockPowerInput(this.xCoord, this.yCoord, this.zCoord) != 0) { if(mode == MODE.LINE_X) { this.laserX += 3; } else if(mode == MODE.LINE_Z) { this.laserZ += 3; } else if(mode == MODE.SPIRAL) { numSteps++; if(radius < numSteps) { numSteps = 0; if(prevDir == ForgeDirection.NORTH) prevDir = ForgeDirection.EAST; else if(prevDir == ForgeDirection.EAST){ prevDir = ForgeDirection.SOUTH; radius++; } else if(prevDir == ForgeDirection.SOUTH) prevDir = ForgeDirection.WEST; else { prevDir = ForgeDirection.NORTH; radius++; } } this.laserX += 3*prevDir.offsetX; this.laserZ += 3*prevDir.offsetZ; } } //TODO: unneeded? checkCanRun(); } } } public boolean isReadyForOperation() { if(batteries.getEnergyStored() == 0) return false; return tickSinceLastOperation > (3*this.batteries.getMaxEnergyStored()/(float)this.batteries.getEnergyStored()); } public void onDestroy() { if(laserSat != null) { laserSat.deactivateLaser(); } ForgeChunkManager.releaseTicket(ticket); } @Override public void onChunkUnload() { super.onChunkUnload(); if(laserSat != null) { laserSat.deactivateLaser(); } isRunning = false; } @Override protected void writeNetworkData(NBTTagCompound nbt) { super.writeNetworkData(nbt); nbt.setBoolean("IsRunning", isRunning); } @Override protected void readNetworkData(NBTTagCompound nbt) { super.readNetworkData(nbt); isRunning = nbt.getBoolean("IsRunning"); } @Override public void writeToNBT(NBTTagCompound nbt) { super.writeToNBT(nbt); NBTTagCompound laser = new NBTTagCompound(); laserSat.writeToNBT(laser); nbt.setTag("laser", laser); NBTTagList list = new NBTTagList(); if(glassPanel != null) { NBTTagCompound tag = new NBTTagCompound(); glassPanel.writeToNBT(tag); nbt.setTag("GlassPane", tag); } nbt.setInteger("laserX", laserX); nbt.setInteger("laserZ", laserZ); nbt.setByte("mode", (byte)mode.ordinal()); if(mode == MODE.SPIRAL && prevDir != null) { nbt.setInteger("CenterX", xCenter); nbt.setInteger("CenterY", yCenter); nbt.setInteger("radius", radius); nbt.setInteger("numSteps", numSteps); nbt.setInteger("prevDir", prevDir.ordinal()); } } @Override public void readFromNBT(NBTTagCompound nbt) { super.readFromNBT(nbt); laserSat.readFromNBT(nbt.getCompoundTag("laser")); if(nbt.hasKey("GlassPane")) { NBTTagCompound tag = nbt.getCompoundTag("GlassPane"); glassPanel = ItemStack.loadItemStackFromNBT(tag); } laserX = nbt.getInteger("laserX"); laserZ = nbt.getInteger("laserZ"); mode = MODE.values()[nbt.getByte("mode")]; if(mode == MODE.SPIRAL && nbt.hasKey("prevDir")){ xCenter = nbt.getInteger("CenterX"); yCenter = nbt.getInteger("CenterY"); radius = nbt.getInteger("radius"); numSteps = nbt.getInteger("numSteps"); prevDir = ForgeDirection.values()[nbt.getInteger("prevDir")]; } } /** * Needs to be called when the laser is finished */ public void onLaserFinish() { } /** * Take items from internal inventory * @return */ public void attempUnjam() { if(!laserSat.getJammed()) return; /*IInventory depositInv = getAvalibleInv(ZUtils.getFirstFilledSlot(invBuffer)); //Assign subInv while(depositInv != null) { ZUtils.mergeInventory(invBuffer, depositInv); if(ZUtils.isInvEmpty(invBuffer)) break; depositInv = getAvalibleInv(ZUtils.getFirstFilledSlot(invBuffer)); }*/ //TODO: finish laserSat.setJammed(false); //!ZUtils.isInvEmpty(invBuffer); finished = false; checkCanRun(); } //TODO: buildcraft support /** * Gets the first inventory with an empty slot * @return first available inventory or null */ private Object getAvalibleInv() { ForgeDirection front = RotatableBlock.getFront(this.getBlockMetadata()); for(ForgeDirection f : VALID_INVENTORY_DIRECTIONS) { if(f == front) continue; TileEntity e = this.worldObj.getTileEntity(this.xCoord + f.offsetX, this.yCoord + f.offsetY, this.zCoord + f.offsetZ); if(InventoryCompat.canInjectItems(e)) return (IInventory)e; } return null; } /** * Gets the first inv with a slot able to hold 'itemStack' * @param item item to fit into inventory * @return inv with capablity of holding 'itemStack' */ private Object getAvalibleInv(ItemStack item) { if(item == null) return getAvalibleInv(); ForgeDirection front = RotatableBlock.getFront(this.getBlockMetadata()); for(ForgeDirection f : VALID_INVENTORY_DIRECTIONS) { if(f == front) continue; TileEntity e = this.worldObj.getTileEntity(this.xCoord + f.offsetX, this.yCoord + f.offsetY, this.zCoord + f.offsetZ); if(InventoryCompat.canInjectItems(e, item)) return e; } return null; } private boolean canMachineSeeEarth() { //for(int i = yCoord - 1; i > 0; i--) { // if(worldObj.isBlockNormalCubeDefault(xCoord, i, zCoord,true)) // return false; //} return true; } private boolean isAllowedToRun() { return !(glassPanel == null || batteries.getEnergyStored() == 0 || !(this.worldObj.provider instanceof WorldProviderSpace) || !zmaster587.advancedRocketry.dimension.DimensionManager.getInstance().canTravelTo(((WorldProviderSpace)this.worldObj.provider).getDimensionProperties(xCoord, zCoord).getParentPlanet()) || Configuration.laserBlackListDims.contains(((WorldProviderSpace)this.worldObj.provider).getDimensionProperties(xCoord, zCoord).getParentPlanet())); } /** * Checks to see if the situation for firing the laser exists... and changes the state accordingly */ public void checkCanRun() { //Laser requires lense, redstone power, not be jammed, and be in orbit and energy to function if(!isAllowedToRun() || !worldObj.isBlockIndirectlyGettingPowered(xCoord, yCoord, zCoord)) { if(laserSat.isAlive()) { laserSat.deactivateLaser(); } setRunning(false); } else if(!laserSat.isAlive() && !finished && !laserSat.getJammed() && worldObj.isBlockIndirectlyGettingPowered(xCoord, yCoord, zCoord) && canMachineSeeEarth()) { //Laser will be on at this point int orbitDimId = ((WorldProviderSpace)this.worldObj.provider).getDimensionProperties(xCoord, zCoord).getParentPlanet(); if(orbitDimId == SpaceObjectManager.WARPDIMID) return; WorldServer orbitWorld = DimensionManager.getWorld(orbitDimId); if(orbitWorld == null) { DimensionManager.initDimension(orbitDimId); orbitWorld = DimensionManager.getWorld(orbitDimId); if(orbitWorld == null) return; } if(ticket == null) { ticket = ForgeChunkManager.requestTicket(AdvancedRocketry.instance, this.worldObj, Type.NORMAL); if(ticket != null) ForgeChunkManager.forceChunk(ticket, new ChunkCoordIntPair(this.xCoord / 16 - (this.xCoord < 0 ? 1 : 0), this.zCoord / 16 - (this.zCoord < 0 ? 1 : 0))); } setRunning(laserSat.activateLaser(orbitWorld, laserX, laserZ)); } if(!this.worldObj.isRemote) PacketHandler.sendToNearby(new PacketMachine(this, (byte)12), this.xCoord, this.yCoord, this.zCoord, 128, this.worldObj.provider.dimensionId); } public int getEnergyPercentScaled(int max) { return (int)(max * (batteries.getEnergyStored() / (float)batteries.getMaxEnergyStored()) ); } public boolean hasEnergy() { return batteries.getEnergyStored() != 0; } //InventoryHandling start @Override public int getSizeInventory() { return inv.getSizeInventory(); } @Override public ItemStack getStackInSlot(int i) { if(i == 0) return glassPanel; else { i--; return inv.getStackInSlot(i); } } @Override public ItemStack decrStackSize(int i, int j) { ItemStack ret; if(i == 0) { ret = glassPanel.copy(); glassPanel = null; return ret; } return null; } @Override public ItemStack getStackInSlotOnClosing(int i) { if(i == 0) return glassPanel; return null; } @Override public void setInventorySlotContents(int i, ItemStack itemstack) { //TODO: add gregcipies if(i == 0) glassPanel = itemstack; else { //TileEntity e = this.worldObj.getTileEntity(this.xCoord + f.offsetX, this.yCoord + f.offsetY, this.zCoord + f.offsetZ); if(InventoryCompat.canInjectItems(inv, itemstack)) InventoryCompat.injectItem(inv, itemstack); this.checkCanRun(); } } @Override public String getInventoryName() { return LibVulpes.proxy.getLocalizedString("tile.spaceLaser.name"); } @Override public int getInventoryStackLimit() { return 64; } @Override public boolean isUseableByPlayer(EntityPlayer entityplayer) { return entityplayer.getDistanceSq(this.xCoord, this.yCoord, this.zCoord) <= 64; } @Override public void openInventory() { // TODO Perhaps make sure laser isn't running } @Override public void closeInventory() { // TODO Auto-generated method stub } @Override public int[] getAccessibleSlotsFromSide(int var1) { return new int[] {}; } @Override public boolean canInsertItem(int i, ItemStack itemstack, int j) { return false; } @Override public boolean canExtractItem(int i, ItemStack itemstack, int j) { return false; } @Override public boolean isItemValidForSlot(int i, ItemStack itemstack) { if(i == 0) return CompatibilityMgr.gregtechLoaded ? OreDictionary.getOreName(OreDictionary.getOreID(itemstack)).equals("lenseRuby") : AdvancedRocketryItems.itemLens == itemstack.getItem() ? true : false; return inv.isItemValidForSlot(i, itemstack); } //InventoryHandling end //Redstone Flux start /** * @param simulate true to simulate.. false to drain the power * @return returns whether enough power is stored for the next opertation */ public boolean hasPowerForOperation() { return POWER_PER_OPERATION <= batteries.getEnergyStored(); } //Redstone Flux end public boolean isJammed() { return laserSat.getJammed(); } public void setJammed(boolean b) { laserSat.setJammed(b); } @Override public boolean hasCustomInventoryName() { return false; } @Override public void onModuleUpdated(ModuleBase module) { if(module == locationX) { if(!((ModuleTextBox)module).getText().isEmpty() && !((ModuleTextBox)module).getText().contentEquals("-")) laserX = Integer.parseInt(((ModuleTextBox)module).getText()); PacketHandler.sendToServer(new PacketMachine(this,(byte) 10)); } else if(module == locationZ) { if(!((ModuleTextBox)module).getText().isEmpty() && !((ModuleTextBox)module).getText().contentEquals("-")) laserZ = Integer.parseInt(((ModuleTextBox)module).getText()); PacketHandler.sendToServer(new PacketMachine(this,(byte) 11)); } } @Override public List<ModuleBase> getModules(int id, EntityPlayer player) { List<ModuleBase> modules = new LinkedList<ModuleBase>(); if(worldObj.isRemote) { modules.add(locationX = new ModuleNumericTextbox(this, 113, 31, 50, 10, 16)); modules.add(locationZ = new ModuleNumericTextbox(this, 113, 41, 50, 10, 16)); locationX.setText(String.valueOf(this.laserX)); locationZ.setText(String.valueOf(this.laserZ)); modules.add(updateText = new ModuleText(130, 20, this.getMode().toString(), 0x0b0b0b, true)); modules.add(new ModuleText(103, 33, "X:", 0x0b0b0b)); modules.add(new ModuleText(103, 43, "Z:", 0x0b0b0b)); modules.add(new ModuleImage(8, 16, TextureResources.laserGuiBG)); } modules.add(new ModuleButton(103, 20, 0, "", this, zmaster587.libVulpes.inventory.TextureResources.buttonLeft, 5, 8)); modules.add(new ModuleButton(157, 20, 1, "", this, zmaster587.libVulpes.inventory.TextureResources.buttonRight, 5, 8)); modules.add(new ModuleButton(103, 62, 2, "Reset", this, zmaster587.libVulpes.inventory.TextureResources.buttonBuild, 34, 20)); modules.add(new ModulePower(11, 25, getBatteries())); modules.add(new ModuleSlotArray(56, 54, this, 0, 1)); return modules; } @Override public String getModularInventoryName() { return "tile.spaceLaser.name"; } @Override public boolean canInteractWithContainer(EntityPlayer entity) { return true; } @Override public void onInventoryButtonPressed(int buttonId) { if(buttonId == 0){ this.decrementMode(); updateText.setText(this.getMode().toString()); } else if(buttonId == 1) { this.incrementMode(); updateText.setText(this.getMode().toString()); } else if(buttonId == 2) { PacketHandler.sendToServer(new PacketMachine(this, (byte)14)); return; } else return; if(!this.isRunning()) PacketHandler.sendToServer(new PacketMachine(this, (byte)13)); } }