@Override
  public boolean onRandomLeafTick(
      ITree tree, World world, int biomeId, int x, int y, int z, boolean isDestroyed) {

    if (world.rand.nextFloat() >= tree.getGenome().getSappiness() * tree.getGenome().getYield()) {
      return false;
    }

    IButterfly spawn =
        PluginLepidopterology.butterflyInterface
            .getIndividualTemplates()
            .get(
                world.rand.nextInt(
                    PluginLepidopterology.butterflyInterface.getIndividualTemplates().size()));
    if (world.rand.nextFloat() >= spawn.getGenome().getPrimary().getRarity() * 0.5f) {
      return false;
    }

    if (world.countEntities(EntityButterfly.class) > PluginLepidopterology.spawnConstraint) {
      return false;
    }

    if (!spawn.canSpawn(world, x, y, z)) {
      return false;
    }

    if (world.isAirBlock(x - 1, y, z)) {
      attemptButterflySpawn(world, spawn, x - 1, y, z);
    } else if (world.isAirBlock(x + 1, y, z)) {
      attemptButterflySpawn(world, spawn, x + 1, y, z);
    } else if (world.isAirBlock(x, y, z - 1)) {
      attemptButterflySpawn(world, spawn, x, y, z - 1);
    } else if (world.isAirBlock(x, y, z + 1)) {
      attemptButterflySpawn(world, spawn, x, y, z + 1);
    }

    return false;
  }
  protected boolean hasSufficientSaplings(
      ITreeGenome genome, World world, int xPos, int yPos, int zPos, int expectedGirth) {

    if (expectedGirth == 1) return true;

    int offset = (expectedGirth - 1) / 2;

    for (int x = xPos - offset; x < xPos - offset + expectedGirth; x++)
      for (int z = zPos - offset; z < zPos - offset + expectedGirth; z++) {

        if (world.isAirBlock(x, yPos, z)) return false;

        TileEntity tile = world.getTileEntity(x, yPos, z);
        if (!(tile instanceof TileSapling)) return false;

        ITree tree = ((TileSapling) tile).getTree();
        if (tree == null
            || !tree.getGenome().getPrimary().getUID().equals(genome.getPrimary().getUID()))
          return false;
      }

    return true;
  }