package pixlepix.auracascade.block.tile; import net.minecraft.block.Block; import net.minecraft.block.state.IBlockState; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraft.network.NetworkManager; import net.minecraft.network.play.server.SPacketUpdateTileEntity; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.EnumFacing; import net.minecraft.util.ITickable; import net.minecraft.util.math.BlockPos; import net.minecraft.world.Explosion; import net.minecraft.world.World; import net.minecraftforge.fml.common.network.NetworkRegistry; import pixlepix.auracascade.AuraCascade; import pixlepix.auracascade.block.AuraBlock; import pixlepix.auracascade.data.PosUtil; import pixlepix.auracascade.network.PacketBurst; import java.util.HashMap; import java.util.LinkedList; public class AuraTile extends TileEntity implements ITickable { public int storage; public HashMap<BlockPos, Integer> burstMap = null; public LinkedList<BlockPos> connected = new LinkedList<BlockPos>(); public boolean hasConnected = false; public int energy = 0; public int initialYValue = -1; public AuraTile() { } @Override public void readFromNBT(NBTTagCompound nbt) { super.readFromNBT(nbt); readCustomNBT(nbt); } @Override public boolean shouldRefresh(World world, BlockPos pos, IBlockState oldState, IBlockState newSate) { return oldState.getBlock() != newSate.getBlock(); } protected void readCustomNBT(NBTTagCompound nbt) { storage = nbt.getInteger("storage"); NBTTagList connectionsNBT = nbt.getTagList("connected", 10); connected = new LinkedList<BlockPos>(); for (int i = 0; i < connectionsNBT.tagCount(); i++) { NBTTagCompound coordNBT = connectionsNBT.getCompoundTagAt(i); int x = coordNBT.getInteger("x"); int y = coordNBT.getInteger("y"); int z = coordNBT.getInteger("z"); BlockPos coord = new BlockPos(x, y, z); connected.add(coord); } hasConnected = nbt.getBoolean("hasConnected"); energy = nbt.getInteger("energy"); initialYValue = nbt.getInteger("initialYValue"); } public boolean connectionBlockedByBlock(BlockPos pos) { Block block = worldObj.getBlockState(pos).getBlock(); return !block.isAir(block.getDefaultState(), worldObj, pos) && (block instanceof AuraBlock || block.isOpaqueCube(block.getDefaultState())); } @Override public NBTTagCompound writeToNBT(NBTTagCompound nbt) { super.writeToNBT(nbt); writeCustomNBT(nbt); return nbt; } protected void writeCustomNBT(NBTTagCompound nbt) { nbt.setInteger("storage", storage); NBTTagList connectionsNBT = new NBTTagList(); for (BlockPos tuple : connected) { NBTTagCompound coordNBT = new NBTTagCompound(); coordNBT.setInteger("x", tuple.getX()); coordNBT.setInteger("y", tuple.getY()); coordNBT.setInteger("z", tuple.getZ()); connectionsNBT.appendTag(coordNBT); } nbt.setTag("connected", connectionsNBT); nbt.setBoolean("hasConnected", hasConnected); nbt.setInteger("energy", energy); nbt.setInteger("initialYValue", initialYValue); } public boolean isOpenPath(BlockPos target) { int dist = (int) Math.sqrt(getPos().distanceSq(target)); EnumFacing direction = PosUtil.directionTo(getPos(), target); if (direction == null) { return false; } for (int i = 1; i < dist; i++) { BlockPos between = getPos().offset(direction, i); if (connectionBlockedByBlock(between)) { return false; } } return true; } public void verifyConnections() { LinkedList<BlockPos> result = new LinkedList<BlockPos>(); for (BlockPos next : connected) { TileEntity tile = worldObj.getTileEntity(next); if (tile instanceof AuraTile && isOpenPath(next)) { if (!((AuraTile) tile).connected.contains(getPos())) { ((AuraTile) tile).connected.add(getPos()); } if (!result.contains(next)) { result.add(next); } } } connected = result; } public void connect(BlockPos pos) { if (worldObj.getTileEntity(pos) instanceof AuraTile && worldObj.getTileEntity(pos) != this && isOpenPath(pos)) { AuraTile otherNode = (AuraTile) worldObj.getTileEntity(pos); otherNode.connected.add(getPos()); this.connected.add(otherNode.getPos()); //This should only happen on initial placement //Not on 'follow ups' if (!hasConnected) { burst(pos, "spell"); } } } public void burst(BlockPos target, String particle, double r, double g, double b) { AuraCascade.proxy.networkWrapper.sendToAllAround(new PacketBurst(getPos(), target, particle, r, g, b), new NetworkRegistry.TargetPoint(worldObj.provider.getDimension(), pos.getX(), pos.getY(), pos.getZ(), 32)); } public void burst(BlockPos target, String particle) { burst(target, particle, 1, 1, 1); } @Override public void update() { if ((!hasConnected || worldObj.getTotalWorldTime() % 200 == 0) && !worldObj.isRemote) { for (int i = 1; i < 16; i++) { for (EnumFacing dir : EnumFacing.VALUES) { connect(getPos().offset(dir, i)); } } //This initial check does *not* see if connections are blocked verifyConnections(); hasConnected = true; } if (initialYValue != pos.getY()) { if (initialYValue == -1 || initialYValue == 0) { initialYValue = pos.getY(); } else { Explosion explosion = new Explosion(worldObj, null, pos.getX(), pos.getY(), pos.getZ(), 2F, true, true); // todo 1.8 isSmoking/isFlaming explosion.doExplosionA(); explosion.doExplosionB(false); worldObj.setBlockToAir(pos); } } if (!worldObj.isRemote) { if (worldObj.getTotalWorldTime() % 20 == 0) { verifyConnections(); energy = 0; burstMap = new HashMap<BlockPos, Integer>(); double totalWeight = 0; for (BlockPos tuple : connected) { if (canTransfer(tuple)) { totalWeight += getWeight(tuple); } } //Add a balance so the node doesn't completley discharge //Based off 'sending color to yourself' totalWeight += this instanceof AuraTileCapacitor ? 0 : 20 * 20; for (BlockPos pos : connected) { double factor = getWeight(pos) / totalWeight; AuraTile other = (AuraTile) worldObj.getTileEntity(pos); if (canTransfer(pos)) { int diff = Math.abs(storage - other.storage); if (diff > 25) { burstMap.put(pos, (int) (storage * factor)); } } } } } if (worldObj.getTotalWorldTime() % 20 == 1) { verifyConnections(); if (burstMap != null) { for (BlockPos tuple : connected) { if (burstMap.containsKey(tuple)) { transferAura(tuple, burstMap.get(tuple)); } } burstMap = null; } markDirty(); worldObj.markBlockRangeForRenderUpdate(pos.getX(), pos.getY(), pos.getX(), pos.getX(), pos.getY(), pos.getZ()); worldObj.notifyBlockOfStateChange(pos, worldObj.getBlockState(pos).getBlock()); worldObj.markAndNotifyBlock(this.pos, this.worldObj.getChunkFromBlockCoords(this.pos),this.blockType.getDefaultState(), this.blockType.getDefaultState(), 2); } } public void transferAura(BlockPos pos, int auraToTransfer) { if (storage >= auraToTransfer) { ((AuraTile) worldObj.getTileEntity(pos)).storage += auraToTransfer; storage -= auraToTransfer; burst(pos, "square"); int power = (int) ((this.pos.getY() - pos.getY()) * auraToTransfer); if (power > 0) { ((AuraTile) worldObj.getTileEntity(pos)).receivePower(power); } } } public void receivePower(int power) { energy += power; } public boolean canTransfer(BlockPos pos) { boolean isLower = pos.getY() < this.pos.getY(); boolean isSame = pos.getY() == this.pos.getY(); return worldObj.getTileEntity(pos) instanceof AuraTile && (isSame || isLower) && !(worldObj.isBlockIndirectlyGettingPowered(this.pos) > 0) && ((AuraTile) worldObj.getTileEntity(pos)).canReceive(getPos()); } public boolean canReceive(BlockPos source) { return true; } public double getWeight(BlockPos pos) { return Math.pow(20 - Math.sqrt(pos.distanceSq(getPos())), 2); } @Override public SPacketUpdateTileEntity getUpdatePacket() { NBTTagCompound nbt = new NBTTagCompound(); writeCustomNBT(nbt); return new SPacketUpdateTileEntity(getPos(), -999, nbt); } @Override public void onDataPacket(NetworkManager net, SPacketUpdateTileEntity pkt) { readCustomNBT(pkt.getNbtCompound()); } }