package resonantinduction.electrical.levitator; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import net.minecraft.block.Block; import net.minecraft.block.BlockFluid; import net.minecraft.block.BlockLadder; import net.minecraft.block.BlockSnow; import net.minecraft.block.BlockVine; 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.network.packet.Packet; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.AxisAlignedBB; import net.minecraft.world.World; import net.minecraftforge.common.ForgeDirection; import net.minecraftforge.fluids.IFluidBlock; import resonantinduction.core.ResonantInduction; import resonantinduction.core.Settings; import resonantinduction.core.handler.ILinkable; import resonantinduction.electrical.Electrical; import resonantinduction.electrical.tesla.TileTesla; import universalelectricity.api.vector.Vector3; import universalelectricity.api.vector.VectorWorld; import calclavia.lib.network.IPacketReceiver; import calclavia.lib.network.IPacketSender; import calclavia.lib.prefab.tile.TileAdvanced; import calclavia.lib.render.EnumColor; import calclavia.lib.utility.InventoryUtility; import com.google.common.io.ByteArrayDataInput; /** * * @author Calclavia * */ public class TileEMLevitator extends TileAdvanced implements IPacketReceiver, IPacketSender, ILinkable { private int pushDelay; private AxisAlignedBB operationBounds; private AxisAlignedBB suckBounds; /** * true = suck, false = push */ public boolean suck = true; /** * Pathfinding */ private ThreadEMPathfinding thread; private PathfinderEMContractor pathfinder; private Set pathfindingTrackers = new HashSet(); private TileEMLevitator linked; private int lastCalcTime = 0; /** Color of beam */ private int dyeID = TileTesla.DEFAULT_COLOR; private Vector3 tempLinkVector; @Override public void initiate() { super.initiate(); updateBounds(); } @Override public void updateEntity() { super.updateEntity(); pushDelay = Math.max(0, pushDelay - 1); if (tempLinkVector != null) { if (tempLinkVector.getTileEntity(worldObj) instanceof TileEMLevitator) { setLink((TileEMLevitator) tempLinkVector.getTileEntity(worldObj), true); } tempLinkVector = null; } if (canFunction()) { TileEntity inventoryTile = getLatched(); IInventory inventory = (IInventory) inventoryTile; if (!suck && pushDelay == 0) { ItemStack retrieved = InventoryUtility.takeTopItemFromInventory(inventory, getDirection().getOpposite().ordinal()); if (retrieved != null) { EntityItem item = getItemWithPosition(retrieved); if (!worldObj.isRemote) { worldObj.spawnEntityInWorld(item); } pushDelay = Settings.LEVITATOR_PUSH_DELAY; } } else if (suck) { if (suckBounds != null) { if (!worldObj.isRemote) { for (EntityItem item : (List) worldObj.getEntitiesWithinAABB(EntityItem.class, suckBounds)) { ItemStack remains = InventoryUtility.putStackInInventory(inventory, item.getEntityItem(), getDirection().getOpposite().ordinal(), false); if (remains == null) { item.setDead(); } else { item.setEntityItemStack(remains); } } } } } if (thread != null) { PathfinderEMContractor newPath = thread.getPath(); if (newPath != null) { pathfinder = newPath; thread = null; } } final int renderFrequency = ResonantInduction.proxy.isGraphicsFancy() ? 1 + worldObj.rand.nextInt(2) : 10 + worldObj.rand.nextInt(2); final boolean renderBeam = ticks % renderFrequency == 0 && hasLink() && linked.suck != suck; if (hasLink()) { if (!suck) { if (renderBeam) { Electrical.proxy.renderElectricShock(worldObj, new Vector3(this).translate(0.5), new Vector3(this).translate(new Vector3(getDirection())).translate(0.5), EnumColor.DYES[dyeID].toColor(), false); } // Push entity along path. if (pathfinder != null) { List results = this.pathfinder.results; for (int i = 0; i < results.size(); i++) { Vector3 result = results.get(i).clone(); if (TileEMLevitator.canBePath(worldObj, result)) { if (i - 1 >= 0) { Vector3 prevResult = results.get(i - 1).clone(); Vector3 difference = prevResult.clone().difference(result); final ForgeDirection direction = difference.toForgeDirection(); if (renderBeam) { Electrical.proxy.renderElectricShock(worldObj, prevResult.clone().translate(0.5), result.clone().translate(0.5), EnumColor.DYES[dyeID].toColor(), false); } AxisAlignedBB bounds = AxisAlignedBB.getAABBPool().getAABB(result.x, result.y, result.z, result.x + 1, result.y + 1, result.z + 1); List entities = worldObj.getEntitiesWithinAABB(EntityItem.class, bounds); for (EntityItem entityItem : entities) { moveEntity(entityItem, direction, result); } } } else { updatePath(); break; } } } else { updatePath(); } } else { if (renderBeam) { Electrical.proxy.renderElectricShock(worldObj, new Vector3(this).translate(0.5), new Vector3(this).translate(new Vector3(getDirection())).translate(0.5), EnumColor.DYES[dyeID].toColor(), false); } pathfinder = null; Vector3 searchVec = new Vector3(this).modifyPositionFromSide(getDirection()); AxisAlignedBB searchBounds = AxisAlignedBB.getAABBPool().getAABB(searchVec.x, searchVec.y, searchVec.z, searchVec.x + 1, searchVec.y + 1, searchVec.z + 1); if (searchBounds != null) { for (EntityItem entityItem : (List) worldObj.getEntitiesWithinAABB(EntityItem.class, searchBounds)) { if (renderBeam) { Electrical.proxy.renderElectricShock(worldObj, new Vector3(this).translate(0.5), new Vector3(entityItem), EnumColor.DYES[dyeID].toColor(), false); } moveEntity(entityItem, getDirection(), new Vector3(this)); } } } } else if (!hasLink()) { for (EntityItem entityItem : (List) worldObj.getEntitiesWithinAABB(EntityItem.class, operationBounds)) { moveEntity(entityItem, getDirection(), new Vector3(this)); } } if (linked != null && linked.isInvalid()) { linked = null; } lastCalcTime--; } } public static boolean canBePath(World world, Vector3 position) { Block block = Block.blocksList[position.getBlockID(world)]; return block == null || (block instanceof BlockSnow || block instanceof BlockVine || block instanceof BlockLadder || ((block instanceof BlockFluid || block instanceof IFluidBlock) && block.blockID != Block.lavaMoving.blockID && block.blockID != Block.lavaStill.blockID)); } private boolean hasLink() { return linked != null && !linked.isInvalid() && linked.linked == this; } private void moveEntity(EntityItem entityItem, ForgeDirection direction, Vector3 lockVector) { switch (direction) { case DOWN: entityItem.setPosition(lockVector.x + 0.5, entityItem.posY, lockVector.z + 0.5); entityItem.motionX = 0; entityItem.motionZ = 0; if (!suck) { entityItem.motionY = Math.max(-Settings.LEVITATOR_MAX_SPEED, entityItem.motionY - Settings.LEVITATOR_ACCELERATION); } else { entityItem.motionY = Math.min(Settings.LEVITATOR_MAX_SPEED, entityItem.motionY + .04 + Settings.LEVITATOR_ACCELERATION); } break; case UP: entityItem.setPosition(lockVector.x + 0.5, entityItem.posY, lockVector.z + 0.5); entityItem.motionX = 0; entityItem.motionZ = 0; if (!suck) { entityItem.motionY = Math.min(Settings.LEVITATOR_MAX_SPEED, entityItem.motionY + .04 + Settings.LEVITATOR_ACCELERATION); } else { entityItem.motionY = Math.max(-Settings.LEVITATOR_MAX_SPEED, entityItem.motionY - Settings.LEVITATOR_ACCELERATION); } break; case NORTH: entityItem.setPosition(lockVector.x + 0.5, lockVector.y + 0.5, entityItem.posZ); entityItem.motionX = 0; entityItem.motionY = 0; if (!suck) { entityItem.motionZ = Math.max(-Settings.LEVITATOR_MAX_SPEED, entityItem.motionZ - Settings.LEVITATOR_ACCELERATION); } else { entityItem.motionZ = Math.min(Settings.LEVITATOR_MAX_SPEED, entityItem.motionZ + Settings.LEVITATOR_ACCELERATION); } break; case SOUTH: entityItem.setPosition(lockVector.x + 0.5, lockVector.y + 0.5, entityItem.posZ); entityItem.motionX = 0; entityItem.motionY = 0; if (!suck) { entityItem.motionZ = Math.min(Settings.LEVITATOR_MAX_SPEED, entityItem.motionZ + Settings.LEVITATOR_ACCELERATION); } else { entityItem.motionZ = Math.max(-Settings.LEVITATOR_MAX_SPEED, entityItem.motionZ - Settings.LEVITATOR_ACCELERATION); } break; case WEST: entityItem.setPosition(entityItem.posX, lockVector.y + 0.5, lockVector.z + 0.5); entityItem.motionY = 0; entityItem.motionZ = 0; if (!suck) { entityItem.motionX = Math.max(-Settings.LEVITATOR_MAX_SPEED, entityItem.motionX - Settings.LEVITATOR_ACCELERATION); } else { entityItem.motionX = Math.min(Settings.LEVITATOR_MAX_SPEED, entityItem.motionX + Settings.LEVITATOR_ACCELERATION); } break; case EAST: entityItem.setPosition(entityItem.posX, lockVector.y + 0.5, lockVector.z + 0.5); entityItem.motionY = 0; entityItem.motionZ = 0; if (!suck) { entityItem.motionX = Math.min(Settings.LEVITATOR_MAX_SPEED, entityItem.motionX + Settings.LEVITATOR_ACCELERATION); } else { entityItem.motionX = Math.max(-Settings.LEVITATOR_MAX_SPEED, entityItem.motionX - Settings.LEVITATOR_ACCELERATION); } break; default: break; } entityItem.ticksExisted = 1; entityItem.isAirBorne = true; entityItem.delayBeforeCanPickup = 1; entityItem.age = Math.max(entityItem.age - 1, 0); } private EntityItem getItemWithPosition(ItemStack toSend) { EntityItem item = null; switch (getDirection()) { case DOWN: item = new EntityItem(worldObj, xCoord + 0.5, yCoord - 0.2, zCoord + 0.5, toSend); break; case UP: item = new EntityItem(worldObj, xCoord + 0.5, yCoord + 1.2, zCoord + 0.5, toSend); break; case NORTH: item = new EntityItem(worldObj, xCoord + 0.5, yCoord + 0.5, zCoord - 0.2, toSend); break; case SOUTH: item = new EntityItem(worldObj, xCoord + 0.5, yCoord + 0.5, zCoord + 1.2, toSend); break; case WEST: item = new EntityItem(worldObj, xCoord - 0.2, yCoord + 0.5, zCoord + 0.5, toSend); break; case EAST: item = new EntityItem(worldObj, xCoord + 1.2, yCoord + 0.5, zCoord + 0.5, toSend); break; default: break; } item.motionX = 0; item.motionY = 0; item.motionZ = 0; return item; } public void updateBounds() { switch (getDirection()) { case DOWN: operationBounds = AxisAlignedBB.getBoundingBox(xCoord, Math.max(yCoord - Settings.LEVITATOR_MAX_REACH, 1), zCoord, xCoord + 1, yCoord, zCoord + 1); suckBounds = AxisAlignedBB.getBoundingBox(xCoord, yCoord - 0.1, zCoord, xCoord + 1, yCoord, zCoord + 1); break; case UP: operationBounds = AxisAlignedBB.getBoundingBox(xCoord, yCoord + 1, zCoord, xCoord + 1, Math.min(yCoord + 1 + Settings.LEVITATOR_MAX_REACH, 255), zCoord + 1); suckBounds = AxisAlignedBB.getBoundingBox(xCoord, yCoord + 1, zCoord, xCoord + 1, yCoord + 1.1, zCoord + 1); break; case NORTH: operationBounds = AxisAlignedBB.getBoundingBox(xCoord, yCoord, zCoord - Settings.LEVITATOR_MAX_REACH, xCoord + 1, yCoord + 1, zCoord); suckBounds = AxisAlignedBB.getBoundingBox(xCoord, yCoord, zCoord - 0.1, xCoord + 1, yCoord + 1, zCoord); break; case SOUTH: operationBounds = AxisAlignedBB.getBoundingBox(xCoord, yCoord, zCoord + 1, xCoord + 1, yCoord + 1, zCoord + 1 + Settings.LEVITATOR_MAX_REACH); suckBounds = AxisAlignedBB.getBoundingBox(xCoord, yCoord, zCoord + 1, xCoord + 1, yCoord + 1, zCoord + 1.1); break; case WEST: operationBounds = AxisAlignedBB.getBoundingBox(xCoord - Settings.LEVITATOR_MAX_REACH, yCoord, zCoord, xCoord, yCoord + 1, zCoord + 1); suckBounds = AxisAlignedBB.getBoundingBox(xCoord - 0.1, yCoord, zCoord, xCoord, yCoord + 1, zCoord + 1); break; case EAST: operationBounds = AxisAlignedBB.getBoundingBox(xCoord + 1, yCoord, zCoord, xCoord + 1 + Settings.LEVITATOR_MAX_REACH, yCoord + 1, zCoord + 1); suckBounds = AxisAlignedBB.getBoundingBox(xCoord + 1, yCoord, zCoord, xCoord + 1.1, yCoord + 1, zCoord + 1); break; default: break; } } public boolean isLatched() { return getLatched() != null; } public TileEntity getLatched() { ForgeDirection side = getDirection().getOpposite(); TileEntity tile = worldObj.getBlockTileEntity(xCoord + side.offsetX, yCoord + side.offsetY, zCoord + side.offsetZ); if (tile instanceof IInventory) { return tile; } return null; } public void incrementFacing() { int newOrdinal = getDirection().ordinal() < 5 ? getDirection().ordinal() + 1 : 0; setDirection(ForgeDirection.getOrientation(newOrdinal)); } public ForgeDirection getDirection() { return ForgeDirection.getOrientation(this.getBlockMetadata()); } public void setDirection(ForgeDirection side) { this.worldObj.setBlockMetadataWithNotify(this.xCoord, this.yCoord, this.zCoord, side.ordinal(), 3); this.updateBounds(); } @Override public ArrayList getPacketData(int type) { ArrayList data = new ArrayList(); data.add(suck); data.add(dyeID); if (linked != null) { data.add(true); data.add(linked.xCoord); data.add(linked.yCoord); data.add(linked.zCoord); } else { data.add(false); } return data; } @Override public Packet getDescriptionPacket() { return ResonantInduction.PACKET_TILE.getPacket(this, getPacketData(0).toArray()); } @Override public void onReceivePacket(ByteArrayDataInput data, EntityPlayer player, Object... extra) { suck = data.readBoolean(); dyeID = data.readInt(); if (data.readBoolean()) { tempLinkVector = new Vector3(data.readInt(), data.readInt(), data.readInt()); } worldObj.markBlockForRenderUpdate(xCoord, yCoord, zCoord); updateBounds(); } public boolean canFunction() { return isLatched() && !worldObj.isBlockIndirectlyGettingPowered(xCoord, yCoord, zCoord); } @Override public void readFromNBT(NBTTagCompound nbt) { super.readFromNBT(nbt); this.suck = nbt.getBoolean("suck"); this.dyeID = nbt.getInteger("dyeID"); if (nbt.hasKey("link")) { tempLinkVector = new Vector3(nbt.getCompoundTag("link")); } } @Override public void writeToNBT(NBTTagCompound nbt) { super.writeToNBT(nbt); nbt.setBoolean("suck", suck); nbt.setInteger("dyeID", dyeID); if (linked != null) { nbt.setCompoundTag("link", new Vector3(linked).writeToNBT(new NBTTagCompound())); } } /** * Link between two TileEntities, do pathfinding operation. */ public void setLink(TileEMLevitator tileEntity, boolean setOpponent) { if (linked != null && setOpponent) { linked.setLink(null, false); } linked = tileEntity; if (setOpponent) { linked.setLink(this, false); } updatePath(); } public void updatePath() { if (thread == null && linked != null && lastCalcTime <= 0) { pathfinder = null; Vector3 start = new Vector3(this).modifyPositionFromSide(getDirection()); Vector3 target = new Vector3(linked).modifyPositionFromSide(linked.getDirection()); if (start.distance(target) < Settings.MAX_CONTRACTOR_DISTANCE) { if (TileEMLevitator.canBePath(worldObj, start) && TileEMLevitator.canBePath(worldObj, target)) { thread = new ThreadEMPathfinding(new PathfinderEMContractor(worldObj, target), start); thread.start(); lastCalcTime = 40; } } } } public void setDye(int dye) { dyeID = dye; worldObj.markBlockForUpdate(xCoord, yCoord, zCoord); } @Override public boolean onLink(EntityPlayer player, VectorWorld vector) { if (vector != null) { if (vector.getTileEntity(this.worldObj) instanceof TileEMLevitator) { this.setLink((TileEMLevitator) vector.getTileEntity(this.worldObj), true); if (this.worldObj.isRemote) { player.addChatMessage("Linked " + this.getBlockType().getLocalizedName() + " with " + " [" + (int) vector.x + ", " + (int) vector.y + ", " + (int) vector.z + "]"); } return true; } } return false; } }