/**
   * Calculates how much of the given goods type this settlement wants and should retain.
   *
   * @param type The <code>GoodsType</code>.
   * @return The amount of goods wanted.
   */
  protected int getWantedGoodsAmount(GoodsType type) {
    if (getUnitCount() <= 0) return 0;

    final Specification spec = getSpecification();
    final UnitType unitType = getFirstUnit().getType();
    final Role militaryRole =
        Role.getAvailableRoles(getOwner(), unitType, spec.getMilitaryRoles()).get(0);

    if (type.isMilitaryGoods()) {
      // Retain enough goods to fully arm.
      int need = 0;
      for (Unit u : ownedUnits) {
        if (u.getRole() == militaryRole) continue;
        List<AbstractGoods> required = u.getGoodsDifference(militaryRole, 1);
        need += AbstractGoods.getCount(type, required);
      }
      return need;
    }

    int consumption = getConsumptionOf(type);
    if (type == spec.getPrimaryFoodType()) {
      // Food is perishable, do not try to retain that much
      return Math.max(40, consumption * 3);
    }
    if (type.isTradeGoods() || type.isNewWorldLuxuryType() || type.isRefined()) {
      // Aim for 10 years supply, resupply is doubtful
      return Math.max(80, consumption * 20);
    }
    // Just keep some around
    return 2 * getUnitCount();
  }
 /** {@inheritDoc} */
 @Override
 public boolean add(Locatable locatable) {
   boolean result = super.add(locatable);
   if (result && locatable instanceof Unit) {
     Unit indian = (Unit) locatable;
     if (indian.getHomeIndianSettlement() == null) {
       // Adopt homeless Indians
       indian.setHomeIndianSettlement(this);
     }
   }
   return result;
 }
 /** {@inheritDoc} */
 @Override
 public Unit getDefendingUnit(Unit attacker) {
   Unit defender = null;
   float defencePower = -1.0f;
   for (Unit nextUnit : getUnitList()) {
     float unitPower = attacker.getGame().getCombatModel().getDefencePower(attacker, nextUnit);
     if (Unit.betterDefender(defender, defencePower, nextUnit, unitPower)) {
       defender = nextUnit;
       defencePower = unitPower;
     }
   }
   return defender;
 }
  /**
   * Gets the goods this settlement is willing to sell.
   *
   * @param limit The maximum number of goods required.
   * @param unit The <code>Unit</code> that is trading.
   * @return A list of goods to sell.
   */
  public List<Goods> getSellGoods(int limit, Unit unit) {
    List<Goods> result = new ArrayList<>();
    List<Goods> settlementGoods = getCompactGoods();
    Collections.sort(settlementGoods, exportGoodsComparator);

    int count = 0;
    for (Goods goods : settlementGoods) {
      if (!willSell(goods.getType())) continue;
      int amount = goods.getAmount();
      int retain = getWantedGoodsAmount(goods.getType());
      if (retain >= amount) continue;
      amount -= retain;
      if (amount > GoodsContainer.CARGO_SIZE) {
        amount = GoodsContainer.CARGO_SIZE;
      }
      if (unit != null) {
        amount =
            Math.round(
                applyModifiers(
                    (float) amount,
                    getGame().getTurn(),
                    unit.getModifiers(Modifier.TRADE_VOLUME_PENALTY)));
      }
      if (amount < TRADE_MINIMUM_SIZE) continue;
      result.add(new Goods(getGame(), this, goods.getType(), amount));
      count++;
      if (count >= limit) break;
    }
    return result;
  }
  /** {@inheritDoc} */
  @Override
  protected void readChildren(FreeColXMLReader xr) throws XMLStreamException {
    // Clear containers.
    contactLevels.clear();
    alarm.clear();
    missionary = null;
    ownedUnits.clear();

    super.readChildren(xr);

    // @compat 0.10.1
    for (Unit u : getUnitList()) {
      if (u.getLocation() != this) {
        u.setLocationNoUpdate(this);
        logger.warning("Fixing unit location" + " from " + u.getLocation() + " to " + this.getId());
      }
    }
    // end @compat
  }
  /** {@inheritDoc} */
  @Override
  protected void readChild(FreeColXMLReader xr) throws XMLStreamException {
    final Game game = getGame();
    final String tag = xr.getLocalName();

    if (ALARM_TAG.equals(tag)) {
      Player player = xr.findFreeColGameObject(game, PLAYER_TAG, Player.class, (Player) null, true);
      // @compat 0.10.5
      if (getName() != null) {
        // Alarm used to imply contact, but only set contacted if
        // we also have a valid name for the settlement.
        setContacted(player);
      }
      // end @compat
      alarm.put(player, new Tension(xr.getAttribute(VALUE_TAG, 0)));
      xr.closeTag(ALARM_TAG);

    } else if (CONTACT_LEVEL_TAG.equals(tag)) {
      ContactLevel cl = xr.getAttribute(LEVEL_TAG, ContactLevel.class, ContactLevel.UNCONTACTED);
      Player player = xr.findFreeColGameObject(game, PLAYER_TAG, Player.class, (Player) null, true);
      contactLevels.put(player, cl);
      xr.closeTag(CONTACT_LEVEL_TAG);

      // @compat 0.10.5
    } else if (IS_VISITED_TAG.equals(tag)) {
      Player player = xr.findFreeColGameObject(game, PLAYER_TAG, Player.class, (Player) null, true);
      setScouted(player);
      xr.closeTag(IS_VISITED_TAG);
      // end @compat

    } else if (MISSIONARY_TAG.equals(tag)) {
      xr.nextTag();
      missionary = xr.readFreeColGameObject(game, Unit.class);
      missionary.setLocationNoUpdate(this);
      xr.closeTag(MISSIONARY_TAG);

      // @compat 0.10.1
    } else if (OLD_UNITS_TAG.equals(tag)) {
      while (xr.nextTag() != XMLStreamConstants.END_ELEMENT) {
        super.readChild(xr);
      }
      // end @compat

    } else if (OWNED_UNITS_TAG.equals(tag)) {
      Unit unit = xr.makeFreeColGameObject(game, ID_ATTRIBUTE_TAG, Unit.class, true);
      addOwnedUnit(unit);
      xr.closeTag(OWNED_UNITS_TAG);

    } else {
      super.readChild(xr);
    }
  }
 /**
  * Is a unit permitted to make contact with this settlement? The unit must be from a nation that
  * has already made contact, or in the first instance, must be arriving by land, with the
  * exception of trading ships.
  *
  * @param unit The <code>Unit</code> that proposes to contact this settlement.
  * @return True if the settlement accepts such contact.
  */
 public boolean allowContact(Unit unit) {
   return unit.getOwner().hasContacted(owner) || !unit.isNaval() || unit.hasGoodsCargo();
 }
  /** {@inheritDoc} */
  @Override
  protected void writeChildren(FreeColXMLWriter xw) throws XMLStreamException {
    super.writeChildren(xw);

    if (missionary != null) {
      xw.writeStartElement(MISSIONARY_TAG);

      missionary.toXML(xw);

      xw.writeEndElement();
    }

    if (xw.validFor(getOwner())) {

      for (Player p : getSortedCopy(contactLevels.keySet())) {
        xw.writeStartElement(CONTACT_LEVEL_TAG);

        xw.writeAttribute(LEVEL_TAG, contactLevels.get(p));

        xw.writeAttribute(PLAYER_TAG, p);

        xw.writeEndElement();
      }

      for (Player p : getSortedCopy(alarm.keySet())) {
        xw.writeStartElement(ALARM_TAG);

        xw.writeAttribute(PLAYER_TAG, p);

        xw.writeAttribute(VALUE_TAG, alarm.get(p).getValue());

        xw.writeEndElement();
      }

      for (Unit unit : getSortedCopy(ownedUnits)) {
        xw.writeStartElement(OWNED_UNITS_TAG);

        xw.writeAttribute(ID_ATTRIBUTE_TAG, unit);

        xw.writeEndElement();
      }

    } else {
      Player client = xw.getClientPlayer();

      ContactLevel cl = contactLevels.get(client);
      if (cl != null) {
        xw.writeStartElement(CONTACT_LEVEL_TAG);

        xw.writeAttribute(LEVEL_TAG, cl);

        xw.writeAttribute(PLAYER_TAG, client);

        xw.writeEndElement();
      }

      Tension alarm = getAlarm(client);
      if (alarm != null) {
        xw.writeStartElement(ALARM_TAG);

        xw.writeAttribute(PLAYER_TAG, client);

        xw.writeAttribute(VALUE_TAG, alarm.getValue());

        xw.writeEndElement();
      }
    }
  }