package pneumaticCraft.common.tileentity; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Random; import java.util.Set; import java.util.UUID; import net.minecraft.block.Block; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.item.EntityBoat; import net.minecraft.entity.item.EntityExpBottle; import net.minecraft.entity.item.EntityItem; import net.minecraft.entity.item.EntityMinecartChest; import net.minecraft.entity.item.EntityMinecartEmpty; import net.minecraft.entity.item.EntityMinecartFurnace; import net.minecraft.entity.item.EntityMinecartHopper; import net.minecraft.entity.item.EntityMinecartTNT; import net.minecraft.entity.item.EntityTNTPrimed; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.entity.projectile.EntityArrow; import net.minecraft.entity.projectile.EntityEgg; import net.minecraft.entity.projectile.EntityFireball; import net.minecraft.entity.projectile.EntityPotion; import net.minecraft.entity.projectile.EntitySnowball; import net.minecraft.init.Blocks; import net.minecraft.init.Items; import net.minecraft.inventory.IInventory; import net.minecraft.inventory.ISidedInventory; import net.minecraft.item.Item; import net.minecraft.item.ItemMonsterPlacer; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.AxisAlignedBB; import net.minecraft.world.ChunkPosition; import net.minecraftforge.common.util.ForgeDirection; import org.apache.commons.lang3.tuple.Pair; import pneumaticCraft.api.tileentity.IAirHandler; import pneumaticCraft.common.block.Blockss; import pneumaticCraft.common.item.ItemMachineUpgrade; import pneumaticCraft.common.item.Itemss; import pneumaticCraft.common.network.DescSynced; import pneumaticCraft.common.network.GuiSynced; import pneumaticCraft.common.network.LazySynced; import pneumaticCraft.common.network.NetworkHandler; import pneumaticCraft.common.network.PacketPlaySound; import pneumaticCraft.common.network.PacketSetEntityMotion; import pneumaticCraft.common.network.PacketSpawnParticle; import pneumaticCraft.common.thirdparty.computercraft.LuaConstant; import pneumaticCraft.common.thirdparty.computercraft.LuaMethod; import pneumaticCraft.common.util.IOHelper; import pneumaticCraft.common.util.PneumaticCraftUtils; import pneumaticCraft.lib.PneumaticValues; import pneumaticCraft.lib.Sounds; import pneumaticCraft.lib.TileEntityConstants; import cpw.mods.fml.common.network.NetworkRegistry.TargetPoint; public class TileEntityAirCannon extends TileEntityPneumaticBase implements ISidedInventory, IInventory, IMinWorkingPressure, IRedstoneControl{ private ItemStack[] inventory; private final Random rand = new Random(); @DescSynced @LazySynced public float rotationAngle; @DescSynced @LazySynced public float heightAngle; @DescSynced public float targetRotationAngle; @DescSynced public float targetHeightAngle; @GuiSynced public boolean doneTurning = false; private boolean redstonePowered = false; @GuiSynced public int gpsX; @GuiSynced public int gpsY; @GuiSynced public int gpsZ; @GuiSynced public boolean coordWithinReach; @GuiSynced public int redstoneMode; private int oldRangeUpgrades; private boolean externalControl;//used in the CC API, to disallow the Cannon to update its angles when things like range upgrades / GPS Tool have changed. private boolean entityUpgradeInserted, dispenserUpgradeInserted; private final List<EntityItem> trackedItems = new ArrayList<EntityItem>();//Items that are being checked to be hoppering into inventories. private Set<UUID> trackedItemIds; private ChunkPosition lastInsertingInventory; //Last coordinate where the item went into the inventory (as a result of the Block Tracker upgrade). private ForgeDirection lastInsertingInventorySide; @GuiSynced public boolean insertingInventoryHasSpace = true; private final int INVENTORY_SIZE = 6; public final int CANNON_SLOT = 0; public final int GPS_SLOT = 1; public final int UPGRADE_SLOT_1 = 2; public final int UPGRADE_SLOT_2 = 3; public final int UPGRADE_SLOT_3 = 4; public final int UPGRADE_SLOT_4 = 5; public TileEntityAirCannon(){ super(PneumaticValues.DANGER_PRESSURE_AIR_CANNON, PneumaticValues.MAX_PRESSURE_AIR_CANNON, PneumaticValues.VOLUME_AIR_CANNON); inventory = new ItemStack[INVENTORY_SIZE]; setUpgradeSlots(new int[]{UPGRADE_SLOT_1, UPGRADE_SLOT_2, UPGRADE_SLOT_3, UPGRADE_SLOT_4}); } @Override public void updateEntity(){ // GPS Tool read if(inventory[1] != null && inventory[1].getItem() == Itemss.GPSTool && !externalControl) { if(inventory[1].stackTagCompound != null) { NBTTagCompound gpsTag = inventory[1].stackTagCompound; int destinationX = gpsTag.getInteger("x"); int destinationY = gpsTag.getInteger("y"); int destinationZ = gpsTag.getInteger("z"); if(destinationX != gpsX || destinationY != gpsY || destinationZ != gpsZ) { gpsX = destinationX; gpsY = destinationY; gpsZ = destinationZ; updateDestination(); } } } int curRangeUpgrades = Math.min(8, getUpgrades(ItemMachineUpgrade.UPGRADE_RANGE, getUpgradeSlots())); if(curRangeUpgrades != oldRangeUpgrades) { oldRangeUpgrades = curRangeUpgrades; if(!externalControl) updateDestination(); } if(worldObj.getTotalWorldTime() % 40 == 0) { boolean isDispenserUpgradeInserted = getUpgrades(ItemMachineUpgrade.UPGRADE_DISPENSER_DAMAGE) > 0; boolean isEntityTrackerUpgradeInserted = getUpgrades(ItemMachineUpgrade.UPGRADE_ENTITY_TRACKER) > 0; if(dispenserUpgradeInserted != isDispenserUpgradeInserted || entityUpgradeInserted != isEntityTrackerUpgradeInserted) { dispenserUpgradeInserted = isDispenserUpgradeInserted; entityUpgradeInserted = isEntityTrackerUpgradeInserted; updateDestination(); } } // update angles doneTurning = true; float speedMultiplier = getSpeedMultiplierFromUpgrades(getUpgradeSlots()); if(rotationAngle < targetRotationAngle) { if(rotationAngle < targetRotationAngle - TileEntityConstants.CANNON_SLOW_ANGLE) { rotationAngle += TileEntityConstants.CANNON_TURN_HIGH_SPEED * speedMultiplier; } else { rotationAngle += TileEntityConstants.CANNON_TURN_LOW_SPEED * speedMultiplier; } if(rotationAngle > targetRotationAngle) rotationAngle = targetRotationAngle; doneTurning = false; } if(rotationAngle > targetRotationAngle) { if(rotationAngle > targetRotationAngle + TileEntityConstants.CANNON_SLOW_ANGLE) { rotationAngle -= TileEntityConstants.CANNON_TURN_HIGH_SPEED * speedMultiplier; } else { rotationAngle -= TileEntityConstants.CANNON_TURN_LOW_SPEED * speedMultiplier; } if(rotationAngle < targetRotationAngle) rotationAngle = targetRotationAngle; doneTurning = false; } if(heightAngle < targetHeightAngle) { if(heightAngle < targetHeightAngle - TileEntityConstants.CANNON_SLOW_ANGLE) { heightAngle += TileEntityConstants.CANNON_TURN_HIGH_SPEED * speedMultiplier; } else { heightAngle += TileEntityConstants.CANNON_TURN_LOW_SPEED * speedMultiplier; } if(heightAngle > targetHeightAngle) heightAngle = targetHeightAngle; doneTurning = false; } if(heightAngle > targetHeightAngle) { if(heightAngle > targetHeightAngle + TileEntityConstants.CANNON_SLOW_ANGLE) { heightAngle -= TileEntityConstants.CANNON_TURN_HIGH_SPEED * speedMultiplier; } else { heightAngle -= TileEntityConstants.CANNON_TURN_LOW_SPEED * speedMultiplier; } if(heightAngle < targetHeightAngle) heightAngle = targetHeightAngle; doneTurning = false; } updateTrackedItems(); super.updateEntity(); } private void updateTrackedItems(){ if(trackedItemIds != null) { trackedItems.clear(); for(Entity entity : (List<Entity>)worldObj.loadedEntityList) { if(trackedItemIds.contains(entity.getUniqueID()) && entity instanceof EntityItem) { trackedItems.add((EntityItem)entity); } } trackedItemIds = null; } Iterator<EntityItem> iterator = trackedItems.iterator(); while(iterator.hasNext()) { EntityItem item = iterator.next(); if(item.worldObj != worldObj || item.isDead) { iterator.remove(); } else { Map<ChunkPosition, ForgeDirection> positions = new HashMap<ChunkPosition, ForgeDirection>(); double range = 0.2; for(ForgeDirection d : ForgeDirection.VALID_DIRECTIONS) { double posX = item.posX + d.offsetX * range; double posY = item.posY + d.offsetY * range; double posZ = item.posZ + d.offsetZ * range; positions.put(new ChunkPosition((int)Math.floor(posX), (int)Math.floor(posY), (int)Math.floor(posZ)), d.getOpposite()); } for(Entry<ChunkPosition, ForgeDirection> entry : positions.entrySet()) { ChunkPosition pos = entry.getKey(); TileEntity te = worldObj.getTileEntity(pos.chunkPosX, pos.chunkPosY, pos.chunkPosZ); IInventory inv = IOHelper.getInventoryForTE(te); ItemStack remainder = IOHelper.insert(inv, item.getEntityItem(), entry.getValue().ordinal(), false); if(remainder != null) { item.setEntityItemStack(remainder); } else { item.setDead(); iterator.remove(); lastInsertingInventory = new ChunkPosition(te.xCoord, te.yCoord, te.zCoord); lastInsertingInventorySide = entry.getValue(); break; } } } } } // ANGLE METHODS ------------------------------------------------- private void updateDestination(){ doneTurning = false; // take dispenser upgrade in account double payloadFrictionY = 0.98D;// this value will differ when a // dispenser upgrade is inserted. double payloadFrictionX = 0.98D; double payloadGravity = 0.04D; if(getUpgrades(ItemMachineUpgrade.UPGRADE_ENTITY_TRACKER) > 0) { payloadFrictionY = 0.98D; payloadFrictionX = 0.91D; payloadGravity = 0.08D; } else if(getUpgrades(ItemMachineUpgrade.UPGRADE_DISPENSER_DAMAGE, getUpgradeSlots()) > 0 && inventory[0] != null) {// if // there // is // a // dispenser // upgrade // inserted. Item item = inventory[0].getItem(); if(item == Items.potionitem || item == Items.experience_bottle || item == Items.egg || item == Items.snowball) {// EntityThrowable payloadFrictionY = 0.99D; payloadGravity = 0.03D; } else if(item == Items.arrow) { payloadFrictionY = 0.99D; payloadGravity = 0.05D; } else if(item == Items.minecart || item == Items.chest_minecart || item == Items.hopper_minecart || item == Items.tnt_minecart || item == Items.furnace_minecart) { payloadFrictionY = 0.95D; } // else if(itemID == Item.fireballCharge.itemID){ // payloadGravity = 0.0D; // } // family items (throwable) which only differ in gravity. if(item == Items.potionitem) payloadGravity = 0.05D; else if(item == Items.experience_bottle) payloadGravity = 0.07D; payloadFrictionX = payloadFrictionY; // items which have different frictions for each axis. if(item == Items.boat) { payloadFrictionX = 0.99D; payloadFrictionY = 0.95D; } if(item == Items.spawn_egg) { payloadFrictionY = 0.98D; payloadFrictionX = 0.91D; payloadGravity = 0.08D; } } // calculate the heading. double deltaX = gpsX - xCoord; double deltaZ = gpsZ - zCoord; float calculatedRotationAngle; if(deltaX >= 0 && deltaZ < 0) { calculatedRotationAngle = (float)(Math.atan(Math.abs(deltaX / deltaZ)) / Math.PI * 180D); } else if(deltaX >= 0 && deltaZ >= 0) { calculatedRotationAngle = (float)(Math.atan(Math.abs(deltaZ / deltaX)) / Math.PI * 180D) + 90; } else if(deltaX < 0 && deltaZ >= 0) { calculatedRotationAngle = (float)(Math.atan(Math.abs(deltaX / deltaZ)) / Math.PI * 180D) + 180; } else { calculatedRotationAngle = (float)(Math.atan(Math.abs(deltaZ / deltaX)) / Math.PI * 180D) + 270; } // calculate the height angle. double distance = Math.sqrt(deltaX * deltaX + deltaZ * deltaZ); double deltaY = gpsY - yCoord; float calculatedHeightAngle = calculateBestHeightAngle(distance, deltaY, getForce(), payloadGravity, payloadFrictionX, payloadFrictionY); setTargetAngles(calculatedRotationAngle, calculatedHeightAngle); } private float calculateBestHeightAngle(double distance, double deltaY, float force, double payloadGravity, double payloadFrictionX, double payloadFrictionY){ double bestAngle = 0; double bestDistance = Float.MAX_VALUE; if(payloadGravity == 0D) { return 90F - (float)(Math.atan(deltaY / distance) * 180F / Math.PI); } for(double i = Math.PI * 0.25D; i < Math.PI * 0.50D; i += 0.001D) { double motionX = Math.cos(i) * force;// calculate the x component of // the vector double motionY = Math.sin(i) * force;// calculate the y component of // the vector double posX = 0; double posY = 0; while(posY > deltaY || motionY > 0) { // simulate movement, until we // reach the y-level required posX += motionX; posY += motionY; motionY -= payloadGravity;// gravity motionX *= payloadFrictionX;// friction motionY *= payloadFrictionY;// friction } double distanceToTarget = Math.abs(distance - posX);// take the // distance if(distanceToTarget < bestDistance) {// and return the best angle. bestDistance = distanceToTarget; bestAngle = i; } } coordWithinReach = bestDistance < 1.5D; return 90F - (float)(bestAngle * 180D / Math.PI); } public synchronized void setTargetAngles(float rotationAngle, float heightAngle){ targetRotationAngle = rotationAngle; targetHeightAngle = heightAngle; if(!worldObj.isRemote) scheduleDescriptionPacket(); } // this function calculates with the parsed in X and Z angles and the force // the needed, and outputs the X, Y and Z velocities. public double[] getVelocityVector(float angleX, float angleZ, float force){ double[] velocities = new double[3]; velocities[0] = Math.sin((double)angleZ / 180 * Math.PI); velocities[1] = Math.cos((double)angleX / 180 * Math.PI); velocities[2] = Math.cos((double)angleZ / 180 * Math.PI) * -1; velocities[0] *= Math.sin((double)angleX / 180 * Math.PI); velocities[2] *= Math.sin((double)angleX / 180 * Math.PI); // calculate the total velocity vector, in relation. double vectorTotal = velocities[0] * velocities[0] + velocities[1] * velocities[1] + velocities[2] * velocities[2]; vectorTotal = force / vectorTotal; // calculate the relation between the // forces to be shot, and the // calculated vector (the scale). for(int i = 0; i < 3; i++) { velocities[i] *= vectorTotal; // scale up the velocities // System.out.println("velocities " + i + " = " + velocities[i]); } return velocities; } public boolean hasCoordinate(){ return gpsX != 0 || gpsY != 0 || gpsZ != 0; } // PNEUMATIC METHODS ----------------------------------------- @Override protected void disperseAir(){ super.disperseAir(); List<Pair<ForgeDirection, IAirHandler>> teList = getConnectedPneumatics(); if(teList.size() == 0) airLeak(ForgeDirection.getOrientation(getBlockMetadata())); } @Override public boolean isConnectedTo(ForgeDirection side){ return ForgeDirection.getOrientation(worldObj.getBlockMetadata(xCoord, yCoord, zCoord)) == side; } public float getForce(){ return 2F + oldRangeUpgrades; } // INVENTORY METHODS- && NBT // ------------------------------------------------------------ /** * Returns the number of slots in the inventory. */ @Override public int getSizeInventory(){ return inventory.length; } /** * Returns the stack in slot i */ @Override public ItemStack getStackInSlot(int slot){ return inventory[slot]; } @Override public ItemStack decrStackSize(int slot, int amount){ ItemStack itemStack = getStackInSlot(slot); if(itemStack != null) { if(itemStack.stackSize <= amount) { setInventorySlotContents(slot, null); } else { itemStack = itemStack.splitStack(amount); if(itemStack.stackSize == 0) { setInventorySlotContents(slot, null); } } } return itemStack; } @Override public ItemStack getStackInSlotOnClosing(int slot){ ItemStack itemStack = getStackInSlot(slot); if(itemStack != null) { setInventorySlotContents(slot, null); } return itemStack; } @Override public void setInventorySlotContents(int slot, ItemStack itemStack){ inventory[slot] = itemStack; if(itemStack != null && itemStack.stackSize > getInventoryStackLimit()) { itemStack.stackSize = getInventoryStackLimit(); } } @Override public String getInventoryName(){ return Blockss.airCannon.getUnlocalizedName(); } @Override public int getInventoryStackLimit(){ return 64; } @Override public void readFromNBT(NBTTagCompound tag){ super.readFromNBT(tag); redstonePowered = tag.getBoolean("redstonePowered"); targetRotationAngle = tag.getFloat("targetRotationAngle"); targetHeightAngle = tag.getFloat("targetHeightAngle"); rotationAngle = tag.getFloat("rotationAngle"); heightAngle = tag.getFloat("heightAngle"); gpsX = tag.getInteger("gpsX"); gpsY = tag.getInteger("gpsY"); gpsZ = tag.getInteger("gpsZ"); if(tag.hasKey("fireOnRightAngle")) { redstoneMode = tag.getBoolean("fireOnRightAngle") ? 0 : 1; //TODO remove legacy } else { redstoneMode = tag.getByte("redstoneMode"); } coordWithinReach = tag.getBoolean("targetWithinReach"); // Read in the ItemStacks in the inventory from NBT NBTTagList tagList = tag.getTagList("Items", 10); inventory = new ItemStack[getSizeInventory()]; for(int i = 0; i < tagList.tagCount(); ++i) { NBTTagCompound tagCompound = tagList.getCompoundTagAt(i); byte slot = tagCompound.getByte("Slot"); if(slot >= 0 && slot < inventory.length) { inventory[slot] = ItemStack.loadItemStackFromNBT(tagCompound); } } trackedItemIds = new HashSet<UUID>(); tagList = tag.getTagList("trackedItems", 10); for(int i = 0; i < tagList.tagCount(); i++) { NBTTagCompound t = tagList.getCompoundTagAt(i); trackedItemIds.add(new UUID(t.getLong("UUIDMost"), t.getLong("UUIDLeast"))); } if(tag.hasKey("inventoryX")) { lastInsertingInventory = new ChunkPosition(tag.getInteger("inventoryX"), tag.getInteger("inventoryY"), tag.getInteger("inventoryZ")); lastInsertingInventorySide = ForgeDirection.getOrientation(tag.getByte("inventorySide")); } else { lastInsertingInventory = null; lastInsertingInventorySide = null; } } @Override public void writeToNBT(NBTTagCompound tag){ super.writeToNBT(tag); tag.setBoolean("redstonePowered", redstonePowered); tag.setFloat("targetRotationAngle", targetRotationAngle); tag.setFloat("targetHeightAngle", targetHeightAngle); tag.setFloat("rotationAngle", rotationAngle); tag.setFloat("heightAngle", heightAngle); tag.setInteger("gpsX", gpsX); tag.setInteger("gpsY", gpsY); tag.setInteger("gpsZ", gpsZ); tag.setByte("redstoneMode", (byte)redstoneMode); tag.setBoolean("targetWithinReach", coordWithinReach); // Write the ItemStacks in the inventory to NBT NBTTagList tagList = new NBTTagList(); for(int currentIndex = 0; currentIndex < inventory.length; ++currentIndex) { if(inventory[currentIndex] != null) { NBTTagCompound tagCompound = new NBTTagCompound(); tagCompound.setByte("Slot", (byte)currentIndex); inventory[currentIndex].writeToNBT(tagCompound); tagList.appendTag(tagCompound); } } tag.setTag("Items", tagList); tagList = new NBTTagList(); for(EntityItem entity : trackedItems) { UUID uuid = entity.getUniqueID(); NBTTagCompound t = new NBTTagCompound(); t.setLong("UUIDMost", uuid.getMostSignificantBits()); t.setLong("UUIDLeast", uuid.getLeastSignificantBits()); tagList.appendTag(t); } tag.setTag("trackedItems", tagList); if(lastInsertingInventory != null) { tag.setInteger("inventoryX", lastInsertingInventory.chunkPosX); tag.setInteger("inventoryY", lastInsertingInventory.chunkPosY); tag.setInteger("inventoryZ", lastInsertingInventory.chunkPosZ); tag.setByte("inventorySide", (byte)lastInsertingInventorySide.ordinal()); } } @Override public boolean isItemValidForSlot(int i, ItemStack itemstack){ if(i == GPS_SLOT && itemstack != null && itemstack.getItem() != Itemss.GPSTool) return false; if(i > GPS_SLOT && i <= UPGRADE_SLOT_4 && itemstack != null && itemstack.getItem() != Itemss.machineUpgrade) return false; return true; } @Override public int[] getAccessibleSlotsFromSide(int var1){ return new int[]{1, 2, 3, 4, 5, 0}; } @Override public boolean canInsertItem(int slot, ItemStack itemstack, int side){ return true; } @Override public boolean canExtractItem(int slot, ItemStack itemstack, int side){ return true; } @Override public boolean isUseableByPlayer(EntityPlayer var1){ return isGuiUseableByPlayer(var1); } // REDSTONE BEHAVIOUR // ------------------------------------------------------------ @Override public void handleGUIButtonPress(int buttonID, EntityPlayer player){ if(buttonID == 0) { if(++redstoneMode > 2) redstoneMode = 0; if(redstoneMode == 2 && getUpgrades(ItemMachineUpgrade.UPGRADE_BLOCK_TRACKER) == 0) redstoneMode = 0; } } public void onNeighbourBlockChange(int x, int y, int z, Block block){ if(!block.isAir(worldObj, x, y, z) && worldObj.isBlockIndirectlyGettingPowered(x, y, z) && !redstonePowered && (redstoneMode != 0 || doneTurning) && (redstoneMode != 2 || inventoryCanCarry())) { fire(); redstonePowered = true; } else if(!worldObj.isBlockIndirectlyGettingPowered(x, y, z) && redstonePowered) { redstonePowered = false; } } private boolean inventoryCanCarry(){ insertingInventoryHasSpace = true; if(lastInsertingInventory == null) return true; if(inventory[0] == null) return true; TileEntity te = worldObj.getTileEntity(lastInsertingInventory.chunkPosX, lastInsertingInventory.chunkPosY, lastInsertingInventory.chunkPosZ); IInventory inv = IOHelper.getInventoryForTE(te); if(inv != null) { ItemStack remainder = IOHelper.insert(inv, inventory[0].copy(), lastInsertingInventorySide.ordinal(), true); insertingInventoryHasSpace = remainder == null; return insertingInventoryHasSpace; } else { lastInsertingInventory = null; lastInsertingInventorySide = null; return true; } } private synchronized boolean fire(){ Entity itemShot = getCloseEntityIfUpgraded(); if(getPressure(ForgeDirection.UNKNOWN) >= PneumaticValues.MIN_PRESSURE_AIR_CANNON && (itemShot != null || inventory[0] != null)) { double[] velocity = getVelocityVector(heightAngle, rotationAngle, getForce()); addAir((int)(-500 * getForce()), ForgeDirection.UNKNOWN); boolean shootingInventory = false; if(itemShot == null) { shootingInventory = true; itemShot = getPayloadEntity(); if(itemShot instanceof EntityItem) { inventory[0] = null; if(getUpgrades(ItemMachineUpgrade.UPGRADE_BLOCK_TRACKER) > 0) { trackedItems.add((EntityItem)itemShot); } } else { inventory[0].stackSize--; if(inventory[0].stackSize <= 0) inventory[0] = null; } } else if(itemShot instanceof EntityPlayer) { EntityPlayerMP entityplayermp = (EntityPlayerMP)itemShot; if(entityplayermp.playerNetServerHandler.func_147362_b().isChannelOpen()) { entityplayermp.setPositionAndUpdate(xCoord + 0.5D, yCoord + 1.8D, zCoord + 0.5D); } } if(itemShot.isRiding()) { itemShot.mountEntity((Entity)null); } itemShot.setPosition(xCoord + 0.5D, yCoord + 1.8D, zCoord + 0.5D); NetworkHandler.sendToAllAround(new PacketSetEntityMotion(itemShot, velocity[0], velocity[1], velocity[2]), new TargetPoint(worldObj.provider.dimensionId, xCoord, yCoord, zCoord, 64)); if(itemShot instanceof EntityFireball) { velocity[0] *= 0.05D; velocity[1] *= 0.05D; velocity[2] *= 0.05D; } itemShot.motionX = velocity[0]; itemShot.motionY = velocity[1]; itemShot.motionZ = velocity[2]; itemShot.onGround = false; itemShot.isCollided = false; itemShot.isCollidedHorizontally = false; itemShot.isCollidedVertically = false; if(itemShot instanceof EntityLivingBase) ((EntityLivingBase)itemShot).setJumping(true); if(shootingInventory && !worldObj.isRemote) worldObj.spawnEntityInWorld(itemShot); for(int i = 0; i < 10; i++) { double velX = velocity[0] * 0.4D + (rand.nextGaussian() - 0.5D) * 0.05D; double velY = velocity[1] * 0.4D + (rand.nextGaussian() - 0.5D) * 0.05D; double velZ = velocity[2] * 0.4D + (rand.nextGaussian() - 0.5D) * 0.05D; NetworkHandler.sendToAllAround(new PacketSpawnParticle("largesmoke", xCoord + 0.5D, yCoord + 0.7D, zCoord + 0.5D, velX, velY, velZ), worldObj); } NetworkHandler.sendToAllAround(new PacketPlaySound(Sounds.CANNON_SOUND, xCoord, yCoord, zCoord, 1.0F, rand.nextFloat() / 4F + 0.75F, true), worldObj); return true; } else { return false; } } // warning: no null-check for inventory slot 0 private Entity getPayloadEntity(){ if(getUpgrades(ItemMachineUpgrade.UPGRADE_DISPENSER_DAMAGE, getUpgradeSlots()) > 0) { Item item = inventory[0].getItem(); if(item == Item.getItemFromBlock(Blocks.tnt)) { EntityTNTPrimed tnt = new EntityTNTPrimed(worldObj); tnt.fuse = 80; return tnt; } else if(item == Items.experience_bottle) return new EntityExpBottle(worldObj); else if(item == Items.potionitem) { EntityPotion potion = new EntityPotion(worldObj); potion.setPotionDamage(inventory[0].getItemDamage()); return potion; } else if(item == Items.arrow) return new EntityArrow(worldObj); else if(item == Items.egg) return new EntityEgg(worldObj); // else if(itemID == Item.fireballCharge) return new // EntitySmallFireball(worldObj); else if(item == Items.snowball) return new EntitySnowball(worldObj); else if(item == Items.spawn_egg) return ItemMonsterPlacer.spawnCreature(worldObj, inventory[0].getItemDamage(), 0, 0, 0); else if(item == Items.minecart) return new EntityMinecartEmpty(worldObj); else if(item == Items.chest_minecart) return new EntityMinecartChest(worldObj); else if(item == Items.furnace_minecart) return new EntityMinecartFurnace(worldObj); else if(item == Items.hopper_minecart) return new EntityMinecartHopper(worldObj); else if(item == Items.tnt_minecart) return new EntityMinecartTNT(worldObj); else if(item == Items.boat) return new EntityBoat(worldObj); } EntityItem item = new EntityItem(worldObj); item.setEntityItemStack(inventory[0].copy()); item.age = 4800; // 1200 ticks left to live, = 60s. item.lifespan += Math.min(getUpgrades(ItemMachineUpgrade.UPGRADE_ITEM_LIFE, getUpgradeSlots()) * 600, 4800); // add // 30s // for // each // life // upgrade, // to // the // max // of // 5 // min. return item; } private Entity getCloseEntityIfUpgraded(){ int entityUpgrades = getUpgrades(ItemMachineUpgrade.UPGRADE_ENTITY_TRACKER); if(entityUpgrades > 0) { entityUpgrades = Math.min(entityUpgrades, 5); List<Entity> entities = worldObj.getEntitiesWithinAABB(EntityLivingBase.class, AxisAlignedBB.getBoundingBox(xCoord - entityUpgrades, yCoord - entityUpgrades, zCoord - entityUpgrades, xCoord + 1 + entityUpgrades, yCoord + 1 + entityUpgrades, zCoord + 1 + entityUpgrades)); Entity closestEntity = null; for(Entity entity : entities) { if(closestEntity == null || PneumaticCraftUtils.distBetween(closestEntity.posX, closestEntity.posY, closestEntity.posZ, xCoord + 0.5, yCoord + 0.5, zCoord + 0.5) > PneumaticCraftUtils.distBetween(entity.posX, entity.posY, entity.posZ, xCoord + 0.5, yCoord + 0.5, zCoord + 0.5)) { closestEntity = entity; } } return closestEntity; } return null; } @Override public boolean hasCustomInventoryName(){ return false; } @Override public void openInventory(){} @Override public void closeInventory(){} /* * COMPUTERCRAFT API */ @Override public String getType(){ return "airCannon"; } @Override protected void addLuaMethods(){ super.addLuaMethods(); luaMethods.add(new LuaConstant("getMinWorkingPressure", PneumaticValues.MIN_PRESSURE_AIR_CANNON)); luaMethods.add(new LuaMethod("setTargetLocation"){ @Override public Object[] call(Object[] args) throws Exception{ if(args.length == 3) { gpsX = ((Double)args[0]).intValue(); gpsY = ((Double)args[1]).intValue(); gpsZ = ((Double)args[2]).intValue(); updateDestination(); return new Object[]{coordWithinReach}; } else { throw new IllegalArgumentException("setTargetLocation requires 3 parameters (x,y,z)"); } } }); luaMethods.add(new LuaMethod("fire"){ @Override public Object[] call(Object[] args) throws Exception{ if(args.length == 0) { return new Object[]{fire()};//returns true if the fire succeeded. } else { throw new IllegalArgumentException("fire doesn't take any arguments!"); } } }); luaMethods.add(new LuaMethod("isDoneTurning"){ @Override public Object[] call(Object[] args) throws Exception{ if(args.length == 0) { return new Object[]{doneTurning}; } else { throw new IllegalArgumentException("isDoneTurning doesn't take any arguments!"); } } }); luaMethods.add(new LuaMethod("setRotationAngle"){ @Override public Object[] call(Object[] args) throws Exception{ if(args.length == 1) { setTargetAngles(((Double)args[0]).floatValue(), targetHeightAngle); return null; } else { throw new IllegalArgumentException("setRotationAngle does take one argument!"); } } }); luaMethods.add(new LuaMethod("setHeightAngle"){ @Override public Object[] call(Object[] args) throws Exception{ if(args.length == 1) { setTargetAngles(targetRotationAngle, 90 - ((Double)args[0]).floatValue()); return null; } else { throw new IllegalArgumentException("setHeightAngle does take one argument!"); } } }); luaMethods.add(new LuaMethod("setExternalControl"){ @Override public Object[] call(Object[] args) throws Exception{ if(args.length == 1) { externalControl = (Boolean)args[0]; return null; } else { throw new IllegalArgumentException("setExternalControl does take one argument!"); } } }); } @Override public float getMinWorkingPressure(){ return PneumaticValues.MIN_PRESSURE_AIR_CANNON; } @Override public int getRedstoneMode(){ return redstoneMode; } }