Java tutorial
package com.builtbroken.assemblyline.content.inserter; import com.builtbroken.assemblyline.AssemblyLine; import com.builtbroken.assemblyline.api.IInserterAccess; import com.builtbroken.assemblyline.content.parts.ALParts; import com.builtbroken.jlib.data.vector.IPos3D; import com.builtbroken.mc.api.automation.IAutomatedCrafter; import com.builtbroken.mc.api.automation.IAutomation; import com.builtbroken.mc.api.tile.multiblock.IMultiTile; import com.builtbroken.mc.api.tile.multiblock.IMultiTileHost; import com.builtbroken.mc.api.tile.node.ITileNodeHost; import com.builtbroken.mc.api.tile.provider.IInventoryProvider; import com.builtbroken.mc.core.Engine; import com.builtbroken.mc.core.network.IPacketIDReceiver; import com.builtbroken.mc.core.registry.implement.IRecipeContainer; import com.builtbroken.mc.framework.multiblock.EnumMultiblock; import com.builtbroken.mc.framework.multiblock.MultiBlockHelper; import com.builtbroken.mc.imp.transform.region.Cube; import com.builtbroken.mc.imp.transform.rotation.EulerAngle; import com.builtbroken.mc.imp.transform.vector.Location; import com.builtbroken.mc.imp.transform.vector.Pos; import com.builtbroken.mc.lib.helper.BlockUtility; import com.builtbroken.mc.lib.helper.recipe.OreNames; import com.builtbroken.mc.prefab.inventory.InventoryUtility; import com.builtbroken.mc.prefab.tile.Tile; import com.builtbroken.mc.prefab.tile.TileModuleMachine; import com.builtbroken.mc.prefab.tile.module.TileModuleInventory; import cpw.mods.fml.common.network.ByteBufUtils; import io.netty.buffer.ByteBuf; import net.minecraft.block.material.Material; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.Items; import net.minecraft.inventory.IInventory; import net.minecraft.item.ItemStack; import net.minecraft.item.crafting.IRecipe; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.AxisAlignedBB; import net.minecraft.util.ChatComponentText; import net.minecraftforge.common.util.ForgeDirection; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Robotic arm that inserts stuff into boxes * * @see <a href="https://github.com/BuiltBrokenModding/VoltzEngine/blob/development/license.md">License</a> for what you can and can't do with the code. * Created by Dark(DarkGuardsman, Robert) on 11/9/2016. */ public class TileInsertArm extends TileModuleMachine implements IAutomation, IMultiTileHost, IPacketIDReceiver, IRecipeContainer { public static final int MAX_SPEED_UPGRADES = 18; /** Speed at which the arm rotates in degrees per tick */ public static final float DEFAULT_SPEED = 1f; /** Multi-block cache */ public static final HashMap<IPos3D, String> tileMapCache = new HashMap(); static { tileMapCache.put(new Pos(0, 1, 0), EnumMultiblock.TILE.getTileName()); } //Facing directions is output direction /** Rotation of the base of the arm */ protected EulerAngle rotation = new EulerAngle(0, 0); /** Stack size to pickup/insert into machines */ protected int insertAmount = 1; /** Are we destroying the structure */ private boolean _destroyingStructure = false; protected static final Cube blockBounds = new Cube(0, 0, 0, 1, .3, 1); public TileInsertArm() { super("tileInsertArm", Material.iron); this.hardness = 1; this.resistance = 1; this.renderTileEntity = true; } @Override protected IInventory createInventory() { return new TileModuleInventory(this, 2); } @Override public void genRecipes(final List<IRecipe> recipes) { recipes.add(newShapedRecipe(AssemblyLine.blockInserter, "HW", "AW", "CW", 'H', ALParts.ROBOTIC_HAND.toStack(), 'A', ALParts.ROBOTIC_ARM_ASSEMBLY.toStack(), 'C', ALParts.ROBOTIC_BASE.toStack(), 'W', OreNames.WIRE_COPPER)); } @Override public Tile newTile() { return new TileInsertArm(); } @Override public void firstTick() { super.firstTick(); MultiBlockHelper.buildMultiBlock(oldWorld(), this, true); facing = ForgeDirection.getOrientation(getMetadata()); markRender(); } @Override public void update() { super.update(); if (isServer()) { if (hasPower()) { boolean facingInput = isFacingInput(); boolean facingOutput = isFacingOutput(); boolean hasItem = getHeldItem() != null; if (facingInput && !hasItem) { takeItem(); } else if (facingOutput && hasItem) { insertItem(); } else { updateRotation(); } } else { dropItem(); } } } @Override protected void setRotationOnPlacement(EntityLivingBase entityLiving, ItemStack itemStack) { //Set facing direction ForgeDirection facing = ForgeDirection .getOrientation(BlockUtility.determineRotation(entityLiving.rotationYaw)); this.setFacing(facing.getOpposite()); //Set rotation rotation.setYaw(getRotation(facing)); } @Override protected boolean onPlayerRightClickWrench(EntityPlayer player, int side, Pos hit) { if (isServer()) { if (getDirection() == ForgeDirection.NORTH) { setFacing(ForgeDirection.EAST); } else if (getDirection() == ForgeDirection.EAST) { setFacing(ForgeDirection.SOUTH); } else if (getDirection() == ForgeDirection.SOUTH) { setFacing(ForgeDirection.WEST); } else { setFacing(ForgeDirection.NORTH); } if (isServer()) { player.addChatComponentMessage( new ChatComponentText("Rotation set to " + getDirection().toString().toLowerCase())); } sendDescPacket(); } return true; } @Override protected boolean onPlayerRightClick(EntityPlayer player, int side, Pos hit) { if (player.getHeldItem() != null) { if (Engine.runningAsDev && player.getHeldItem().getItem() == Items.stick) { if (isServer()) { player.addChatComponentMessage( new ChatComponentText("Output: " + getDirection() + " R: " + rotation)); } return true; } else if (player.getHeldItem().getItem() == Items.redstone) { if (isServer()) { ItemStack stack = getStackInSlot(1); if (stack == null) { stack = new ItemStack(Items.redstone); setInventorySlotContents(1, stack); player.addChatComponentMessage(new ChatComponentText("Upgrades: " + getSpeedUpdates())); player.inventory.decrStackSize(player.inventory.currentItem, 1); player.inventoryContainer.detectAndSendChanges(); } else if (stack.stackSize < MAX_SPEED_UPGRADES) { stack.stackSize += 1; setInventorySlotContents(1, stack); player.addChatComponentMessage(new ChatComponentText("Upgrades: " + getSpeedUpdates())); player.inventory.decrStackSize(player.inventory.currentItem, 1); player.inventoryContainer.detectAndSendChanges(); } else { player.addChatComponentMessage(new ChatComponentText( "Max updates of " + MAX_SPEED_UPGRADES + " has been reached")); } } return true; } } return false; } /** * Updates the rotation */ protected void updateRotation() { int desiredRotation = getRotation(getHeldItem() == null ? getDirection().getOpposite() : getDirection()); rotation.moveYaw(desiredRotation, DEFAULT_SPEED + DEFAULT_SPEED * getSpeedUpdates(), 1); rotation.clampTo360(); sendDescPacket(); } /** * Called to take an item from the input direction */ protected void takeItem() { if (getHeldItem() == null) { Object input = findInput(); if (input instanceof IInserterAccess) { setHeldItem( ((IInserterAccess) input).takeInserterItem(rotation, getDirection(), insertAmount, true)); } else if (input instanceof IAutomatedCrafter) { final int[] slots = ((IAutomatedCrafter) input).getCraftingOutputSlots(this, getDirection()); for (int index = 0; index < slots.length; index++) { final ItemStack stack = ((IAutomatedCrafter) input).getInventory().getStackInSlot(slots[index]); if (stack != null) { if (((IAutomatedCrafter) input).canRemove(stack, slots[index], getDirection())) { final ItemStack take = stack.copy(); take.stackSize = 1; setHeldItem(take); stack.stackSize--; if (stack.stackSize <= 0) { ((IAutomatedCrafter) input).getInventory().setInventorySlotContents(slots[index], null); } else { ((IAutomatedCrafter) input).getInventory().setInventorySlotContents(slots[index], stack); } } } } } else { IInventory inventory = toInventory(input); if (inventory != null) { setHeldItem(InventoryUtility.takeTopItemFromInventory(inventory, getDirection().ordinal(), insertAmount)); } } } } /** * Called to inser the item into the output inventory */ protected void insertItem() { if (getHeldItem() != null) { Object output = findOutput(); if (output instanceof IInserterAccess) { setHeldItem(((IInserterAccess) output).giveInserterItem(rotation, getDirection().getOpposite(), getHeldItem(), true)); } else if (output instanceof IAutomatedCrafter) { int[] slots = ((IAutomatedCrafter) output).getCraftingInputSlots(this, getDirection().getOpposite()); ItemStack heldItem = getHeldItem(); final ForgeDirection side = getDirection(); for (int index = 0; index < slots.length; index++) { final int slot = slots[index]; //TODO add rendered error responses when items cant be stored (Ex. invalid filter, no room) if (((IAutomatedCrafter) output).canStore(heldItem, slot, side)) { heldItem = ((IAutomatedCrafter) output).insertRequiredItem(heldItem, slot, this, side); if (heldItem == null || heldItem.stackSize <= 0) { setHeldItem(null); break; } else { setHeldItem(heldItem); } } } } else { IInventory inventory = toInventory(output); if (inventory != null) { setHeldItem(InventoryUtility.putStackInInventory(inventory, getHeldItem(), getDirection().getOpposite().ordinal(), false)); } } } } private IInventory toInventory(Object object) { if (object instanceof IInventory) { return (IInventory) object; } else if (object instanceof IInventoryProvider) { return ((IInventoryProvider) object).getInventory(); } return null; } /** * Called to drop the item */ protected void dropItem() { //TODO drop where the hand actually is Location location = toLocation().add(rotation.toPos()); InventoryUtility.dropItemStack(location, getHeldItem()); setHeldItem(null); } /** * Checks if the machine has power to work * * @return true if it has enough for one more tick */ protected boolean hasPower() { return true; } /** * Checks if the arm is within an acceptable rotation to * access the input tile. * * @return true if yes */ protected boolean isFacingInput() { return isFacing(getDirection().getOpposite()); } /** * Checks if the arm is within an acceptable rotation to * access the output tile. * * @return true if yes */ protected boolean isFacingOutput() { return isFacing(getDirection()); } /** * Checks if the arm is within an acceptable rotation * to the facing direction. * * @param dir - direction to face * @return true if yes */ protected boolean isFacing(ForgeDirection dir) { return rotation.isYawWithin(getRotation(dir), 0.001); } /** * Gets the rotation for the direction * * @param dir - direction to face * @return rotation value i 90 degree slices */ protected int getRotation(ForgeDirection dir) { switch (dir) { case SOUTH: return 180; case EAST: return -90; case WEST: return 90; default: return 0; } } /** * Gets the held item * * @return item or null if none */ protected ItemStack getHeldItem() { return getStackInSlot(0); } /** * Sets the held item * * @param stack - stack, can be null */ protected void setHeldItem(ItemStack stack) { this.setInventorySlotContents(0, stack); } /** * Number of installed speed upgrades * * @return # upgrades */ protected int getSpeedUpdates() { ItemStack stack = getStackInSlot(1); return stack != null ? stack.stackSize : 0; } /** * Finds the input tile * * @return the tile */ protected Object findInput() { Location location = toLocation().add(getDirection().getOpposite()); Object tile = location.getTileEntity(); if (tile instanceof IMultiTile) { tile = (TileEntity) ((IMultiTile) tile).getHost(); } if (tile instanceof ITileNodeHost) { tile = ((ITileNodeHost) tile).getTileNode(); } if (tile == null) { List entities = getEntitiesNearHand(location); for (Object entity : entities) { if (entity instanceof IInventory) { return entity; } } } return tile; } /** * Finds the output tile in the facing direction * * @return the tile */ protected Object findOutput() { Location location = toLocation().add(getDirection()); Object tile = location.getTileEntity(); if (tile instanceof IMultiTile) { tile = (TileEntity) ((IMultiTile) tile).getHost(); } if (tile instanceof ITileNodeHost) { tile = ((ITileNodeHost) tile).getTileNode(); } if (tile == null) { List entities = getEntitiesNearHand(location); for (Object entity : entities) { if (entity instanceof IInventory) { return entity; } } } return tile; } protected List getEntitiesNearHand(Location location) { AxisAlignedBB bounds = AxisAlignedBB.getBoundingBox(location.x(), location.y(), location.z(), location.x() + 1, location.y() + 1, location.z() + 1); return worldObj.getEntitiesWithinAABB(Entity.class, bounds); } @Override public void writeDescPacket(ByteBuf buf) { super.writeDescPacket(buf); rotation.writeBytes(buf); buf.writeBoolean(getHeldItem() != null); if (getHeldItem() != null) { ByteBufUtils.writeItemStack(buf, getHeldItem()); } } @Override public void readFromNBT(NBTTagCompound nbt) { super.readFromNBT(nbt); rotation.readFromNBT(nbt.getCompoundTag("rotation")); } @Override public void writeToNBT(NBTTagCompound nbt) { super.writeToNBT(nbt); nbt.setTag("rotation", rotation.writeNBT(new NBTTagCompound())); } @Override public void onInventoryChanged(int slot, ItemStack prev, ItemStack item) { if (oldWorld() != null) { sendDescPacket(); } } @Override public void onMultiTileAdded(IMultiTile tileMulti) { if (tileMulti instanceof TileEntity) { if (tileMapCache.containsKey(new Pos((TileEntity) this).sub(new Pos((TileEntity) tileMulti)))) { tileMulti.setHost(this); } } } @Override public boolean onMultiTileBroken(IMultiTile tileMulti, Object source, boolean harvest) { if (!_destroyingStructure && tileMulti instanceof TileEntity) { Pos pos = new Pos((TileEntity) tileMulti).sub(new Pos((TileEntity) this)); if (tileMapCache.containsKey(pos)) { MultiBlockHelper.destroyMultiBlockStructure(this, harvest, false, true); return true; } } return false; } @Override public boolean canPlaceBlockOnSide(ForgeDirection side) { return side == ForgeDirection.UP; } @Override public boolean removeByPlayer(EntityPlayer player, boolean willHarvest) { MultiBlockHelper.destroyMultiBlockStructure(this, false, false, false); if (willHarvest && getHeldItem() != null) { InventoryUtility.dropItemStack(toLocation(), getHeldItem()); setInventorySlotContents(0, null); } return super.removeByPlayer(player, willHarvest); } @Override public void onTileInvalidate(IMultiTile tileMulti) { } @Override public boolean onMultiTileActivated(IMultiTile tile, EntityPlayer player, int side, float xHit, float yHit, float zHit) { return this.onPlayerRightClick(player, side, new Pos(xHit, yHit, zHit)); } @Override public void onMultiTileClicked(IMultiTile tile, EntityPlayer player) { } @Override public HashMap<IPos3D, String> getLayoutOfMultiBlock() { HashMap<IPos3D, String> map = new HashMap(); Pos center = new Pos((TileEntity) this); for (Map.Entry<IPos3D, String> entry : tileMapCache.entrySet()) { map.put(center.add(entry.getKey()), entry.getValue()); } return map; } @Override public Cube getBlockBounds() { return blockBounds; } @Override public Cube getSelectBounds() { return blockBounds; } @Override public boolean useMetaForFacing() { return true; } }