package com.hbm.tileentity.machine; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import com.hbm.blocks.ModBlocks; import com.hbm.blocks.machine.BlockHadronCoil; import com.hbm.blocks.machine.BlockHadronPlating; import com.hbm.interfaces.IConsumer; import com.hbm.packet.AuxParticlePacketNT; import com.hbm.packet.PacketDispatcher; import com.hbm.tileentity.TileEntityMachineBase; import com.hbm.tileentity.machine.TileEntityHadronDiode.DiodeConfig; import cpw.mods.fml.common.network.NetworkRegistry.TargetPoint; import net.minecraft.block.Block; import net.minecraft.block.material.Material; import net.minecraft.entity.effect.EntityLightningBolt; import net.minecraft.init.Blocks; import net.minecraft.item.Item; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.tileentity.TileEntity; import net.minecraftforge.common.util.ForgeDirection; public class TileEntityHadron extends TileEntityMachineBase implements IConsumer { public long power; public static final long maxPower = 1000000000; public boolean isOn = false; public boolean analysisOnly = true; public TileEntityHadron() { super(5); } @Override public String getName() { return "container.hadron"; } @Override public void updateEntity() { if(!worldObj.isRemote) { drawPower(); if(particles.isEmpty() && slots[0] != null) { ForgeDirection dir = ForgeDirection.getOrientation(this.getBlockMetadata()); particles.add(new Particle(slots[0].getItem(), dir, xCoord, yCoord, zCoord)); this.decrStackSize(0, 1); } if(!particles.isEmpty()) updateParticles(); for(Particle p : particlesToRemove) { particles.remove(p); } NBTTagCompound data = new NBTTagCompound(); data.setBoolean("isOn", isOn); data.setLong("power", power); data.setBoolean("analysis", analysisOnly); this.networkPack(data, 50); } } @Override public void networkUnpack(NBTTagCompound data) { this.isOn = data.getBoolean("isOn"); this.power = data.getLong("power"); this.analysisOnly = data.getBoolean("analysis"); } @Override public void handleButtonPacket(int value, int meta) { if(meta == 0) this.isOn = !this.isOn; if(meta == 1) this.analysisOnly = !this.analysisOnly; } private void drawPower() { for(ForgeDirection dir : getRandomDirs()) { if(power == maxPower) return; int x = xCoord + dir.offsetX * 2; int y = yCoord + dir.offsetY * 2; int z = zCoord + dir.offsetZ * 2; TileEntity te = worldObj.getTileEntity(x, y, z); if(te instanceof TileEntityHadronPower) { TileEntityHadronPower plug = (TileEntityHadronPower)te; long toDraw = Math.min(maxPower - power, plug.getPower()); this.setPower(power + toDraw); plug.setPower(plug.getPower() - toDraw); } } } private void finishParticle(Particle p, boolean analysisOnly) { particlesToRemove.add(p); worldObj.playSoundEffect(p.posX, p.posY, p.posZ, "random.orb", 10, 1); } static final int maxParticles = 1; List particles = new ArrayList(); List particlesToRemove = new ArrayList(); private void updateParticles() { for(Particle particle : particles) { particle.update(); } } @Override public void setPower(long i) { power = i; this.markDirty(); } @Override public long getPower() { return power; } @Override public long getMaxPower() { return maxPower; } public class Particle { //Starting values Item item; ForgeDirection dir; int posX; int posY; int posZ; //Progressing values int momentum; int charge; int analysis; static final int maxCharge = 10; boolean isCheckExempt = false; boolean expired = false; public Particle(Item item, ForgeDirection dir, int posX, int posY, int posZ) { this.item = item; this.dir = dir; this.posX = posX; this.posY = posY; this.posZ = posZ; this.charge = maxCharge; this.momentum = 0; } public void expire() { if(expired) return; this.expired = true; particlesToRemove.add(this); worldObj.newExplosion(null, posX + 0.5, posY + 0.5, posZ + 0.5, 10, false, false); System.out.println("Last dir: " + dir.name()); System.out.println("Last pos: " + posX + " " + posY + " " + posZ); Thread.currentThread().dumpStack(); } public boolean isExpired() { return this.expired; } public void update() { if(expired) //just in case return; this.charge--; changeDirection(this); makeSteppy(this); checkSegment(this); isCheckExempt = false; //clearing up the exemption we might have held from the previous turn, AFTER stepping if(charge <= 0) this.expire(); } } /** * Moves the particle and does all the checks required to do so * Handles diode entering behavior and whatnot * @param p */ public void makeSteppy(Particle p) { ForgeDirection dir = p.dir; p.posX += dir.offsetX; p.posY += dir.offsetY; p.posZ += dir.offsetZ; int x = p.posX; int y = p.posY; int z = p.posZ; Block block = worldObj.getBlock(x, y, z); TileEntity te = worldObj.getTileEntity(x, y, z); if(te instanceof TileEntityHadron) { if(p.analysis != 3) p.expire(); else this.finishParticle(p, false); return; } if(block.getMaterial() != Material.air && block != ModBlocks.hadron_diode) p.expire(); if(coilValue(worldObj.getBlock(x + dir.offsetX, y + dir.offsetY, z + dir.offsetZ)) > 0) p.isCheckExempt = true; } /** * All the checks done *after* the particle moves one tile * @param p */ public void checkSegment(Particle p) { ForgeDirection dir = p.dir; int x = p.posX; int y = p.posY; int z = p.posZ; //we make a set of axis where the directional axis is 0 and the normals are 1 //that allows us to easily iterate through a rectangle that is normal to our moving direction int dX = 1 - Math.abs(dir.offsetX); int dY = 1 - Math.abs(dir.offsetY); int dZ = 1 - Math.abs(dir.offsetZ); //whether the particle has entered an analysis chamber //-> all coils have to be air //-> all platings have to be analysis chamber walls boolean analysis = true; for(int a = x - dX * 2; a <= x + dX * 2; a++) { for(int b = y - dY * 2; b <= y + dY * 2; b++) { for(int c = z - dZ * 2; c <= z + dZ * 2;c++) { Block block = worldObj.getBlock(a, b, c); /** ignore the center for now */ if(a == x && b == y && c == z) { //we are either in a diode or the core - no analysis for you now if(block.getMaterial() != Material.air) analysis = false; continue; } int ix = Math.abs(x - a); int iy = Math.abs(y - b); int iz = Math.abs(z - c); /** check coils, all abs deltas are 1 or less */ if(ix <= 1 && iy <= 1 && iz <= 1) { //are we exempt from the coil examination? nice, skip checks only for inner magnets, not the corners! if(p.isCheckExempt && ix + iy + iz == 1) { continue; } //coil is air, analysis can remain true if(block.getMaterial() == Material.air && analysis) { continue; } //not air -> not an analysis chamber analysis = false; int coilVal = coilValue(block); //not a valid coil: kablam! if(coilVal == 0) { p.expire(); } else { p.momentum += coilVal; } continue; } /** now we check the plating, sum of all local positions being 3 or less gives us the outer plating without corners */ if(ix + iy + iz <= 3) { //if the plating is for the analysis chamber, continue no matter what if(isAnalysis(block)) continue; //no analysis chamber -> turn off analysis and proceed analysis = false; //a plating? good, continue if(isPlating(block)) continue; TileEntity te = worldObj.getTileEntity(a, b, c); //power plugs are also ok, might as well succ some energy when passing if(te instanceof TileEntityHadronPower) { TileEntityHadronPower plug = (TileEntityHadronPower)te; int req = p.maxCharge - p.charge; //how many "charge points" the particle needs to be fully charged long bit = plug.maxPower / p.maxCharge; //how much HE one "charge point" is int times = (int) (plug.getPower() / bit); //how many charges the plug has to offer int total = Math.min(req, times); //whichever is less, the charges in the plug or the required charges p.charge += total; plug.setPower(plug.getPower() - total * bit); continue; } //Are we exempt from checking the plating? skip all the plating blocks where branches could be if(p.isCheckExempt && ix + iy + iz == 2) { continue; } System.out.println("Was exempt: " + p.isCheckExempt); worldObj.setBlock(a, b, c, Blocks.dirt); p.expire(); } } } } if(analysis) { p.analysis++; //if the analysis chamber is too big, destroy if(p.analysis > 3) p.expire(); if(p.analysis == 2) { NBTTagCompound data = new NBTTagCompound(); data.setString("type", "hadron"); PacketDispatcher.wrapper.sendToAllAround(new AuxParticlePacketNT(data, p.posX + 0.5, p.posY + 0.5, p.posZ + 0.5), new TargetPoint(worldObj.provider.dimensionId, p.posX + 0.5, p.posY + 0.5, p.posZ + 0.5, 25)); } //if operating in line accelerator mode, halt after 2 blocks and staart the reading if(this.analysisOnly && p.analysis == 2) { this.finishParticle(p, true); } } else { //if the analysis stops despite being short of 3 steps in the analysis chamber, destroy if(p.analysis > 0 && p.analysis < 3) p.expire(); } } /** * Checks whether we can go forward or if we might want to do a turn * Handles the better part of the diode behavior * @param p */ public void changeDirection(Particle p) { ForgeDirection dir = p.dir; int x = p.posX; int y = p.posY; int z = p.posZ; int nx = x + dir.offsetX; int ny = y + dir.offsetY; int nz = z + dir.offsetZ; Block next = worldObj.getBlock(nx, ny, nz); TileEntity te = worldObj.getTileEntity(nx, ny, nz); //the next block appears to be a diode, let's see if we can enter if(te instanceof TileEntityHadronDiode) { TileEntityHadronDiode diode = (TileEntityHadronDiode)te; if(diode.getConfig(p.dir.getOpposite().ordinal()) != DiodeConfig.IN) { //it appears as if we have slammed into the side of a diode, ouch p.expire(); } //there's a diode ahead, turn off checks so we can make the curve p.isCheckExempt = true; //the *next* block is a diode, we are not in it yet, which means no turning and no check exemption. too bad kiddo. return; } //instead of the next TE, we are looking at the current one - the diode (maybe) te = worldObj.getTileEntity(x, y, z); //if we are currently in a diode, we might want to consider changing dirs if(te instanceof TileEntityHadronDiode) { //since we are *in* a diode, we might want to call the warrant officer for //an exemption for the coil check, because curves NEED holes to turn into, and //checking for coils in spaces where there cannot be coils is quite not-good p.isCheckExempt = true; TileEntityHadronDiode diode = (TileEntityHadronDiode)te; //the direction in which we were going anyway is an output, so we will keep going if(diode.getConfig(dir.ordinal()) == DiodeConfig.OUT) { return; //well then, iterate through some random directions and hope a valid output shows up } else { List dirs = getRandomDirs(); for(ForgeDirection d : dirs) { if(d == dir || d == dir.getOpposite()) continue; //looks like we can leave! if(diode.getConfig(d.ordinal()) == DiodeConfig.OUT) { //set the direction and leave this hellhole p.dir = d; return; } } } } //next step is air or the core, proceed if(next.getMaterial() == Material.air || next == ModBlocks.hadron_core) return; //so, the next block is most certainly a wall. not good. perhaps we could try turning? if(coilValue(next) > 0) { ForgeDirection validDir = ForgeDirection.UNKNOWN; List dirs = getRandomDirs(); System.out.println("Starting as " + dir.name()); //let's look at every direction we could go in for(ForgeDirection d : dirs) { if(d == dir || d == dir.getOpposite()) continue; System.out.println("Trying " + d.name()); //there is air! we can pass! if(worldObj.getBlock(x + d.offsetX, y + d.offsetY, z + d.offsetZ).getMaterial() == Material.air) { if(validDir == ForgeDirection.UNKNOWN) { validDir = d; System.out.println("yes"); //it seems like there are two or more possible ways, which is not allowed without a diode //sorry kid, nothing personal } else { System.out.println("what"); p.expire(); return; } } } //set the new direction p.dir = validDir; p.isCheckExempt = true; return; } p.expire(); } /** * Dear god please grant me the gift of death and end my eternal torment * @return */ private List getRandomDirs() { List rands = Arrays.asList(new Integer[] {0, 1, 2, 3, 4, 5} ); Collections.shuffle(rands); List dirs = new ArrayList(); for(Integer i : rands) { dirs.add(ForgeDirection.getOrientation(i)); } return dirs; } public int coilValue(Block b) { if(b instanceof BlockHadronCoil) return ((BlockHadronCoil)b).factor; return 0; } public boolean isPlating(Block b) { return b instanceof BlockHadronPlating || b instanceof BlockHadronCoil || b == ModBlocks.hadron_plating_glass || b == ModBlocks.hadron_analysis_glass || b == ModBlocks.hadron_access; } public boolean isAnalysis(Block b) { return b == ModBlocks.hadron_analysis || b == ModBlocks.hadron_analysis_glass; } }