Package org.randomgd.bukkit.workers.info

Source Code of org.randomgd.bukkit.workers.info.BlacksmithInfo

package org.randomgd.bukkit.workers.info;

import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import org.bukkit.ChatColor;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.Chest;
import org.bukkit.block.Furnace;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.FurnaceInventory;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.randomgd.bukkit.workers.ToolUsage;
import org.randomgd.bukkit.workers.WorkerHandler;
import org.randomgd.bukkit.workers.util.ChestHandler;
import org.randomgd.bukkit.workers.util.Configuration;
import org.randomgd.bukkit.workers.util.GeneralInformation;

/**
* Information about blacksmith activity.
*/
public class BlacksmithInfo extends ScannerInfo {

  /**
   * Unique Identifier.
   */
  private static final long serialVersionUID = -1090800876410748785L;

  /**
   * General info displayed to the player.
   */
  private static final String GENERAL_INFO = ChatColor.GRAY
      + "I'm a blacksmith.";

  /**
   * Information displayed if the villager is a miner.
   */
  private static final String MINER_INFO = ChatColor.GRAY + "I'm a miner.";

  /**
   * Information displayed if the villager is a miner and can mine.
   */
  private static final String BASIC_MINING_INFO = ChatColor.GRAY
      + "I can mine.";

  /**
   * Information displayed if the villager is a miner but can't mine.
   */
  private static final String NO_MINING_INFO = ChatColor.GRAY
      + "But I can't mine anything !";

  /**
   * Information displayed in the villager is a miner that can mine hard
   * blocks.
   */
  private static final String HARD_STUFF_MINING_INFO = ChatColor.GRAY
      + "I can break hard stuff.";

  private transient Set<Furnace> furnaces;

  private transient Set<Chest> chest;

  private transient List<Location> miningColumn;

  /**
   * Amount of stone axe usage left.
   */
  private int stonePickaxe;

  /**
   * Amount of iron axe usage left.
   */
  private int ironPickaxe;

  /**
   * Mining authorization.
   */
  private boolean canMine;

  /**
   * Last mining date (for cooldown).
   */
  private transient long lastMining;

  /**
   * current cooldown.
   */
  private transient long currentCooldown;

  /**
   * Mining cooldown configuration.
   */
  private transient int miningCooldown;

  /**
   * Maximum mining depth.
   */
  private transient int miningDepth;

  /**
   * Constructor.
   */
  public BlacksmithInfo() {
    stonePickaxe = 0;
    ironPickaxe = 0;
    canMine = false;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void printInfoToPlayer(Player player) {
    if (!canMine) {
      player.sendMessage(GENERAL_INFO);
    } else {
      player.sendMessage(MINER_INFO);
      if (stonePickaxe + ironPickaxe == 0) {
        player.sendMessage(NO_MINING_INFO);
      } else {
        if (ironPickaxe > 0) {
          player.sendMessage(HARD_STUFF_MINING_INFO);
        } else {
          player.sendMessage(BASIC_MINING_INFO);
        }
      }
    }
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public boolean give(Material material, Player player) {
    boolean result = false;
    switch (material) {
    case STICK: {
      printInfoToPlayer(player);
      break;
    }
    case GOLD_PICKAXE: {
      if (player.hasPermission("usefulvillagers.jobassign.miner")) {
        // Set the blacksmith to miner sub-task (from / to).
        String message;
        canMine = !canMine;

        if (canMine) {
          message = ChatColor.GRAY + "Let's mine !";
        } else {
          message = ChatColor.GRAY + "Ok, I stop mining now.";
        }
        player.sendMessage(message);
      } else {
        // TODO Change NO_*_PERMISSION_MESSAGE location to a neutral
        // class to avoid coupling.
        player.sendMessage(WorkerHandler.NO_TASK_PERMISSION_MESSAGE);
      }
      break;
    }
    case STONE_PICKAXE: {
      if (canMine) {
        stonePickaxe += ToolUsage.STONE.getUsage();
        result = true;
      }
      break;
    }
    case IRON_PICKAXE: {
      if (canMine) {
        ironPickaxe += ToolUsage.IRON.getUsage();
        result = true;
      }
      break;
    }
    default:
      break;
    }
    return result;
  }

  /**
   * Perform mine work based on scanned area.
   *
   * @param world
   *            Hosting world.
   */
  private void doMineWork(World world) {
    long now = System.currentTimeMillis();

    // Manage cooldown.
    if (currentCooldown > 0) {
      long elapsed = now - lastMining;
      if (elapsed > currentCooldown) {
        currentCooldown = 0;
      }
    }

    if ((currentCooldown < 1) && !miningColumn.isEmpty()
        && (stonePickaxe + ironPickaxe > 0)) {
      // Let's mine !
      // Get the column with the highest level.
      Location toMine = miningColumn.get(0);
      int height = 0;
      for (Location i : miningColumn) {
        int blockX = i.getBlockX();
        int blockZ = i.getBlockZ();
        int blockY = i.getBlockY() - 1;
        int depth = 1;

        while ((blockY > 0) && (depth < miningDepth)) {
          ++depth;
          Block blk = world.getBlockAt(blockX, blockY, blockZ);
          if (blk.isEmpty()) {
            --blockY;
          } else {
            // Is it a block to mine ?
            Material bType = blk.getType();
            if (GeneralInformation.MINABLE.contains(bType)) {
              // Interesting.
              if ((blockY > height)
                  && (ironPickaxe > 0 || !GeneralInformation.HARDSTUFF
                      .contains(bType))) {
                height = blockY;
                toMine = i;
              }
            }
            break;
          }
        }
      }

      // Do the mining duty
      int blockX = toMine.getBlockX();
      int blockZ = toMine.getBlockZ();
      Block toBreak = world.getBlockAt(blockX, height, blockZ);
      Material breakType = toBreak.getType();
      boolean mineIt = shallMine(breakType);
      if (mineIt) {
        mineBlock(now, toBreak);
      }
    }
  }

  /**
   * Mine a block and use surroundings to store the result.
   *
   * @param now
   *            Mine time.
   * @param toBreak
   *            Block to mine.
   */
  private void mineBlock(long now, Block toBreak) {
    lastMining = now;
    // TODO Cooldown table per material ?
    currentCooldown = miningCooldown;
    Collection<ItemStack> result = toBreak.getDrops();
    for (ItemStack i : result) {
      Material rType = i.getType();
      int amount = i.getAmount();
      ChestHandler.deposit(rType, amount, chest);
      // ## As we've checked that there's at least one free
      // slot, we're sure that nothing left.
      toBreak.setType(Material.AIR);
    }
  }

  /**
   * Depending on the material type and the available tools, decide if the
   * villager can mine or not.
   *
   * @param type
   *            Material type to mine.
   * @return <code>true</code> if the villager is able to mine this.
   */
  private boolean shallMine(Material type) {
    boolean mineIt = false;
    if (GeneralInformation.MINABLE.contains(type)) {
      if (GeneralInformation.HARDSTUFF.contains(type)) {
        if (ironPickaxe > 0) {
          --ironPickaxe;
          mineIt = true;
        }
      } else {
        if (stonePickaxe > 0) {
          --stonePickaxe;
          mineIt = true;
        } else if (ironPickaxe > 0) {
          --ironPickaxe;
          mineIt = true;
        }
      }

    }
    return mineIt;
  }

  /**
   * Check tool state. If there's chests near, try to get some tool.
   */
  private void checkPickaxes() {
    // Get pickaxes in chest if needed.
    if (stonePickaxe == 0) {
      int pickaxe = ChestHandler.get(Material.STONE_PICKAXE, 1, chest);
      if (pickaxe > 0) {
        stonePickaxe += ToolUsage.STONE.getUsage();
      }
    }

    if (ironPickaxe == 0) {
      // ## Code duplication is BAD ! Hopefully, it's beta ...
      int pickaxe = ChestHandler.get(Material.IRON_PICKAXE, 1, chest);
      if (pickaxe > 0) {
        ironPickaxe += ToolUsage.IRON.getUsage();
      }
    }
  }

  /**
   * Perform furnace work (get result, refuel).
   */
  private void doFurnaceWork() {
    // First, clear furnaces.
    cleanFurnaces();

    // Then, look for furnace that need fuel or awaiting jobs.
    for (Furnace i : furnaces) {
      FurnaceInventory fInv = i.getInventory();
      ItemStack job = fInv.getSmelting();
      if ((job != null) && (job.getAmount() > 0)) {
        // If we're here, then there's obviously no fuel left.

        // First, refill with the same material.
        Material material = job.getType();
        if (Material.LOG.equals(material)) {
          // TODO Log management. For now, just skip this. There's
          // metadata to take into account.
          continue;
        }
        int amount = job.getAmount();
        int toRetrieve = job.getMaxStackSize() - amount;
        if (toRetrieve > 0) {
          amount = storeJob(fInv, material, toRetrieve, amount);
        }

        storeNewFuel(fInv, amount);
      } else {
        checkFurnace(fInv);
      }
    }
  }

  /**
   * Check a furnace for new/current job.
   *
   * @param inventory
   *            Furnace inventory.
   */
  private void checkFurnace(FurnaceInventory inventory) {
    // The furnace is ready for a new job.
    Material nextJob = null;
    int maxToGet = 0;
    for (Chest j : chest) {
      Inventory cInv = j.getInventory();
      int size = cInv.getSize();
      for (int k = 0; k < size; ++k) {
        ItemStack stack = cInv.getItem(k);
        if (stack != null) {
          int amount = stack.getAmount();
          if (amount > 0) {
            Material material = stack.getType();
            if (GeneralInformation.SMELTABLE.contains(material)) {
              // Here we go. Stack this.
              nextJob = material;
              maxToGet = stack.getMaxStackSize();
              break;
            }
          }
        }
      }
      if (nextJob != null) {
        break;
      }
    }
    startFurnaceJob(inventory, nextJob, maxToGet);
  }

  /**
   * Start a new furnace job.
   *
   * @param inventory
   *            Furnace inventory.
   * @param type
   *            Type of material to smelt.
   * @param toTransfer
   *            Amount of material to transfer in furnace.
   */
  private void startFurnaceJob(FurnaceInventory inventory, Material type,
      int toTransfer) {
    if (type != null) {
      // We've got a new job.
      int amount = storeJob(inventory, type, toTransfer, 0);
      // Got some fuel ?
      ItemStack fuel = inventory.getFuel();
      if ((fuel == null) || (fuel.getAmount() < 1)) {
        storeNewFuel(inventory, amount);
      } // No fuel addition.
    }
  }

  /**
   * Clear surrounding furnaces.
   */
  private void cleanFurnaces() {
    for (Furnace i : furnaces) {
      FurnaceInventory fInv = i.getInventory();
      ItemStack result = fInv.getResult();
      if ((result != null) && (result.getAmount() != 0)) {
        // There's something to store.
        Material material = result.getType();
        int toStore = result.getAmount();
        for (Chest j : chest) {
          toStore = ChestHandler.deposit(material, toStore, j);
          if (toStore == 0) {
            break;
          }
        }
        result.setAmount(toStore);
        fInv.setResult(result);
      }
    }
  }

  /**
   * Store stuff for chest to furnace smelting slot.
   *
   * @param fInv
   *            Furnace inventory.
   * @param material
   *            Material to store.
   * @param toRetrieve
   *            Amount to retrieve.
   * @param amount
   *            Amount already in the furnace.
   * @return Transfered amount of stuff.
   */
  private int storeJob(FurnaceInventory fInv, Material material,
      int toRetrieve, int amount) {
    int result = amount;
    int retrieved = ChestHandler.get(material, toRetrieve, chest);
    result += retrieved;
    fInv.setSmelting(new ItemStack(material, result));
    return result;
  }

  /**
   * Store fuel found from chest to furnace.
   *
   * @param fInv
   *            Furnace inventory.
   * @param stored
   *            Stored material amount.
   */
  private void storeNewFuel(FurnaceInventory fInv, int stored) {
    int toRetrieve = stored / 8;
    int retrieved = ChestHandler.get(Material.COAL, toRetrieve, chest);
    if (retrieved != 0) {
      // ## TODO: check about fuel type (coal OR charcoal).
      ItemStack fuel = new ItemStack(Material.COAL, retrieved);
      fInv.setFuel(fuel);
    }
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void setConfiguration(Configuration cnf) {
    // Nothing special.
    // Room based stuff will occur later.
    super.setConfiguration(cnf);
    furnaces = new HashSet<Furnace>();
    chest = new HashSet<Chest>();
    miningColumn = new LinkedList<Location>();
    lastMining = System.currentTimeMillis();
    currentCooldown = 0;
    Configuration.Blacksmith blk = cnf.getBlacksmithConfiguration();
    miningCooldown = blk.getMiningCooldown();
    miningDepth = blk.getMiningDepth();
  }

  /**
   * {@inheritDoc}
   */
  @Override
  protected int extractVerticalAbove(Configuration cnf) {
    Configuration.Blacksmith blk = cnf.getBlacksmithConfiguration();
    return blk.getVerticalAbove();
  }

  /**
   * {@inheritDoc}
   */
  @Override
  protected int extractVerticalBelow(Configuration cnf) {
    Configuration.Blacksmith blk = cnf.getBlacksmithConfiguration();
    return blk.getVerticalBelow();
  }

  /**
   * {@inheritDoc}
   */
  @Override
  protected int extractHorizontalScan(Configuration cnf) {
    Configuration.Blacksmith blk = cnf.getBlacksmithConfiguration();
    return blk.getHorizontalRange();
  }

  /**
   * {@inheritDoc}
   */
  @Override
  protected void scan(Block block, World world, int xA, int yA, int zA) {
    Material material = block.getType();
    switch (material) {
    case FURNACE: {
      Furnace furnace = (Furnace) block.getState();
      furnaces.add(furnace);
      break;
    }
    case CHEST: {
      Chest ch = (Chest) block.getState();
      chest.add(ch);
      break;
    }
    case IRON_BLOCK: {
      if (canMine) {
        // Mining column !
        miningColumn.add(block.getLocation());
      }
      break;
    }
    default:
      break;
    }
  }

  /**
   * {@inheritDoc}
   */
  @Override
  protected void preScan(World world) {
    // Let's find a furnace AND a chest.
    // The blacksmith (for a change) does not store a thing.
    // It will transfer stuff from chest to furnace and from furnace to
    // chest.
    furnaces.clear();
    chest.clear();
    miningColumn.clear();
  }

  /**
   * {@inheritDoc}
   */
  @Override
  protected void postScan(World world, Entity entity) {
    boolean hasOneSlotFree = false;

    if (!chest.isEmpty()) {
      // Look for at least one free slot.
      hasOneSlotFree = ChestHandler.hasFreeSlot(chest);
      if (!furnaces.isEmpty()) {
        doFurnaceWork();
      }
      if (hasOneSlotFree && canMine) {
        checkPickaxes();
        doMineWork(world);
      }
    }
  }
}
TOP

Related Classes of org.randomgd.bukkit.workers.info.BlacksmithInfo

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.