@Override
  public void undoFromTemplate() {

    if (this.nextWallBuilt == null) {
      for (BlockCoord coord : wallBlocks.keySet()) {
        WallBlock wb = wallBlocks.get(coord);
        ItemManager.setTypeId(coord.getBlock(), wb.getOldId());
        ItemManager.setData(coord.getBlock(), wb.getOldData());
        try {
          wb.delete();
        } catch (SQLException e) {
          e.printStackTrace();
        }
      }

      // Remove this wall chunk.
      ChunkCoord coord = new ChunkCoord(this.getCorner());
      CivGlobal.removeWallChunk(this, coord);
    } else {
      try {
        this.nextWallBuilt.processUndo();
      } catch (CivException e) {
        e.printStackTrace();
      }
    }
  }
  @Override
  public void process() {
    double rate = this.getDouble("value");
    int duration = Integer.valueOf(this.getString("duration"));

    CivGlobal.getSessionDB()
        .add(
            getKey(this.getParentTown()),
            rate + ":" + duration,
            this.getParentTown().getCiv().getId(),
            this.getParentTown().getId(),
            0);
    DecimalFormat df = new DecimalFormat();

    if (rate > 1.0) {
      sendMessage(
          "Our production rate has increased by "
              + df.format((rate - 1.0) * 100)
              + "% due to an unforseen event!");
    } else {
      sendMessage(
          "Our production rate has decreased by "
              + df.format((1.0 - rate) * 100)
              + "% due to an unforseen event!");
    }
  }
  private void validateBlockLocation(Player player, Location loc) throws CivException {
    Block b = loc.getBlock();

    if (ItemManager.getId(b) == CivData.CHEST) {
      throw new CivException("Cannot build here, would destroy chest.");
    }

    TownChunk tc = CivGlobal.getTownChunk(b.getLocation());

    if (tc != null
        && !tc.perms.hasPermission(PlotPermissions.Type.DESTROY, CivGlobal.getResident(player))) {
      // Make sure we have permission to destroy any block in this area.
      throw new CivException(
          "Cannot build here, you need DESTROY permissions to the block at "
              + b.getX()
              + ","
              + b.getY()
              + ","
              + b.getZ());
    }

    BlockCoord coord = new BlockCoord(b);
    // not building a trade outpost, prevent protected blocks from being destroyed.
    if (CivGlobal.getProtectedBlock(coord) != null) {
      throw new CivException("Cannot build here, protected blocks in the way.");
    }

    if (CivGlobal.getStructureBlock(coord) != null) {
      throw new CivException("Cannot build here, structure blocks in the way at " + coord);
    }

    if (CivGlobal.getFarmChunk(new ChunkCoord(coord.getLocation())) != null) {
      throw new CivException("Cannot build here, in the same chunk as a farm improvement.");
    }

    if (loc.getBlockY() >= Wall.MAX_HEIGHT) {
      throw new CivException("Cannot build here, wall is too high.");
    }

    if (loc.getBlockY() <= 1) {
      throw new CivException("Cannot build here, too close to bedrock.");
    }

    BlockCoord bcoord = new BlockCoord(loc);
    for (int y = 0; y < 256; y++) {
      bcoord.setY(y);
      StructureBlock sb = CivGlobal.getStructureBlock(bcoord);
      if (sb != null) {
        throw new CivException(
            "Cannot build here, this wall segment overlaps with a structure block belonging to a "
                + sb.getOwner().getName()
                + " structure.");
      }
    }
  }
  @Override
  public void delete() throws SQLException {
    if (this.wallBlocks != null) {
      for (WallBlock wb : this.wallBlocks.values()) {
        wb.delete();
      }
    }

    if (wallChunks != null) {
      for (ChunkCoord coord : wallChunks) {
        CivGlobal.removeWallChunk(this, coord);
      }
    }

    super.delete();
  }
  private boolean isValidWall() {
    for (WallBlock block : this.wallBlocks.values()) {
      BlockCoord bcoord = new BlockCoord(block.getCoord());

      for (int y = 0; y < 256; y++) {
        bcoord.setY(y);

        StructureBlock sb = CivGlobal.getStructureBlock(bcoord);
        if (sb != null) {
          if (sb.getOwner() != this) {
            return false;
          }
        }
      }
    }

    return true;
  }
  public void onHit() {
    // launchExplodeFirework(loc);

    int radius = (int) yield;
    HashSet<Buildable> structuresHit = new HashSet<Buildable>();

    for (int x = -radius; x < radius; x++) {
      for (int z = -radius; z < radius; z++) {
        for (int y = -radius; y < radius; y++) {

          Block b = loc.getBlock().getRelative(x, y, z);
          if (ItemManager.getId(b) == CivData.BEDROCK) {
            continue;
          }

          if (loc.distance(b.getLocation()) <= yield) {
            bcoord.setFromLocation(b.getLocation());
            StructureBlock sb = CivGlobal.getStructureBlock(bcoord);
            CampBlock cb = CivGlobal.getCampBlock(bcoord);

            if (sb == null && cb == null) {
              explodeBlock(b);
              continue;
            }

            if (sb != null) {

              if (!sb.isDamageable()) {
                continue;
              }

              if (sb.getOwner() instanceof TownHall) {
                TownHall th = (TownHall) sb.getOwner();
                if (th.getControlPoints().containsKey(bcoord)) {
                  continue;
                }
              }

              if (!sb.getOwner().isDestroyed()) {
                if (!structuresHit.contains(sb.getOwner())) {

                  structuresHit.add(sb.getOwner());

                  if (sb.getOwner() instanceof TownHall) {
                    TownHall th = (TownHall) sb.getOwner();

                    if (th.getHitpoints() == 0) {
                      explodeBlock(b);
                    } else {
                      th.onCannonDamage(cannon.getDamage());
                    }
                  } else {
                    Player player = null;
                    try {
                      player = CivGlobal.getPlayer(whoFired);
                    } catch (CivException e) {
                    }

                    if (!sb.getCiv().getDiplomacyManager().atWarWith(whoFired.getCiv())) {
                      if (player != null) {
                        CivMessage.sendError(
                            player,
                            "Cannot damage structures in civilizations we're not at war with.");
                        return;
                      }
                    }

                    sb.getOwner()
                        .onDamage(cannon.getDamage(), b.getWorld(), player, sb.getCoord(), sb);
                    CivMessage.sendCiv(
                        sb.getCiv(),
                        CivColor.Yellow
                            + "Our "
                            + sb.getOwner().getDisplayName()
                            + " at ("
                            + sb.getOwner().getCenterLocation().getX()
                            + ","
                            + sb.getOwner().getCenterLocation().getY()
                            + ","
                            + sb.getOwner().getCenterLocation().getZ()
                            + ")"
                            + " was hit by a cannon! ("
                            + sb.getOwner().getHitpoints()
                            + "/"
                            + sb.getOwner().getMaxHitPoints()
                            + ")");
                  }

                  CivMessage.sendCiv(
                      whoFired.getCiv(),
                      CivColor.LightGreen
                          + "We've hit "
                          + sb.getOwner().getTown().getName()
                          + "'s "
                          + sb.getOwner().getDisplayName()
                          + " with a cannon!"
                          + " ("
                          + sb.getOwner().getHitpoints()
                          + "/"
                          + sb.getOwner().getMaxHitPoints()
                          + ")");
                }
              } else {

                if (!IronCannon.cannonBlocks.containsKey(bcoord)) {
                  explodeBlock(b);
                }
              }
              continue;
            }
          }
        }
      }
    }

    /* Instantly kill any players caught in the blast. */
    LinkedList<Entity> players =
        EntityProximity.getNearbyEntities(null, loc, yield, EntityPlayer.class);
    for (Entity e : players) {
      Player player = (Player) e;
      player.damage(playerDamage);
      if (player.isDead()) {
        CivMessage.global(
            CivColor.LightGray
                + whoFired.getName()
                + " obliterated "
                + player.getName()
                + " with a cannon blast!");
      }
    }
  }
  private int buildWallSegment(
      Player player,
      BlockCoord first,
      BlockCoord second,
      int blockCount,
      HashMap<String, SimpleBlock> simpleBlocks,
      int verticalSegments)
      throws CivException {
    Location locFirst = first.getLocation();
    Location locSecond = second.getLocation();

    Vector dir =
        new Vector(
            locFirst.getX() - locSecond.getX(),
            locFirst.getY() - locSecond.getY(),
            locFirst.getZ() - locSecond.getZ());
    dir.normalize();
    dir.multiply(0.5);
    HashMap<String, SimpleBlock> thisWallBlocks = new HashMap<String, SimpleBlock>();

    this.getTown().lastBuildableBuilt = null;

    getVerticalWallSegment(player, locSecond, thisWallBlocks);
    simpleBlocks.putAll(thisWallBlocks);
    verticalSegments++;

    double distance = locSecond.distance(locFirst);
    BlockCoord lastBlockCoord = new BlockCoord(locSecond);
    BlockCoord currentBlockCoord = new BlockCoord(locSecond);
    while (locSecond.distance(locFirst) > 1.0) {
      locSecond.add(dir);
      ChunkCoord coord = new ChunkCoord(locSecond);
      CivGlobal.addWallChunk(this, coord);

      currentBlockCoord.setFromLocation(locSecond);
      if (lastBlockCoord.equals(currentBlockCoord)) {
        continue;
      } else {
        lastBlockCoord.setFromLocation(locSecond);
      }

      blockCount++;
      if (blockCount > Wall.RECURSION_LIMIT) {
        throw new CivException(
            "ERROR: Building wall blocks exceeded recusion limit! Halted to keep server alive.");
      }

      getVerticalWallSegment(player, locSecond, thisWallBlocks);
      simpleBlocks.putAll(thisWallBlocks);
      verticalSegments++;

      // Distance should always be going down, as a failsave
      // check that it is. Abort if our distance goes up.
      double tmpDist = locSecond.distance(locFirst);
      if (tmpDist > distance) {
        break;
      }
    }

    /* build the last wall segment. */
    if (!wallBlocks.containsKey(new BlockCoord(locFirst))) {
      try {
        getVerticalWallSegment(player, locFirst, thisWallBlocks);
        simpleBlocks.putAll(thisWallBlocks);
        verticalSegments++;
      } catch (CivException e) {
        CivLog.warning("Couldn't build the last wall segment, oh well.");
      }
    }

    for (SimpleBlock sb : simpleBlocks.values()) {
      BlockCoord bcoord = new BlockCoord(sb);
      int old_id = ItemManager.getId(bcoord.getBlock());
      int old_data = ItemManager.getData(bcoord.getBlock());
      if (!wallBlocks.containsKey(bcoord)) {
        try {
          WallBlock wb = new WallBlock(bcoord, this, old_id, old_data, sb.getType(), sb.getData());

          wallBlocks.put(bcoord, wb);
          this.addStructureBlock(bcoord, true);
          wb.save();
        } catch (SQLException e) {
          e.printStackTrace();
        }
      }
    }
    return verticalSegments;
  }
  @Override
  public void onMarkerPlacement(Player player, Location next, ArrayList<Location> locs)
      throws CivException {
    BlockCoord first = new BlockCoord(next);
    BlockCoord second = null;

    CultureChunk cc = CivGlobal.getCultureChunk(next);
    if (cc == null || cc.getTown().getCiv() != this.getTown().getCiv()) {
      throw new CivException("Cannot build here, you need to build inside your culture.");
    }

    if (locs.size() <= 1) {
      CivMessage.send(
          player,
          CivColor.LightGray + "First location placed, place another to start building a wall.");
      return;
    }

    // Validate our locations
    if (locs.get(0).distance(locs.get(1)) > Wall.MAX_SEGMENT) {
      throw new CivException(
          "Can only build a wall in "
              + Wall.MAX_SEGMENT
              + " block segments, pick a closer location");
    }

    second = new BlockCoord(locs.get(0));
    locs.clear();
    MarkerPlacementManager.removeFromPlacementMode(player, false);

    Location secondLoc = second.getLocation();
    // Setting to a new block coord so we can increment in buildWallSegment without changing the
    // corner.
    this.setCorner(new BlockCoord(secondLoc));
    this.setComplete(true);
    this.save();

    // We should now be able to draw a line between these two block points.
    HashMap<String, SimpleBlock> simpleBlocks = new HashMap<String, SimpleBlock>();
    int verticalSegments = this.buildWallSegment(player, first, second, 0, simpleBlocks, 0);

    // Pay the piper
    double cost = verticalSegments * COST_PER_SEGMENT;
    if (!this.getTown().getTreasury().hasEnough(cost)) {

      for (WallBlock wb : this.wallBlocks.values()) {
        try {
          wb.delete();
        } catch (SQLException e) {
          e.printStackTrace();
        }
      }
      this.wallBlocks.clear();

      throw new CivException(
          "Cannot build, not enough coins to pay "
              + cost
              + " coins for wall of length "
              + verticalSegments
              + " blocks.");
    }

    this.getTown().getTreasury().withdraw(cost);

    CivMessage.sendTown(
        this.getTown(),
        CivColor.Yellow + "Paid " + cost + " coins for " + verticalSegments + " wall segments.");

    // build the blocks
    for (SimpleBlock sb : simpleBlocks.values()) {
      BlockCoord bcoord = new BlockCoord(sb);
      ItemManager.setTypeId(bcoord.getBlock(), sb.getType());
      ItemManager.setData(bcoord.getBlock(), sb.getData());
    }

    // Add wall to town and global tables
    this.getTown().addStructure(this);
    CivGlobal.addStructure(this);
    this.getTown().lastBuildableBuilt = this;
  }