/**
* Copyright (C) 2002-2012 The FreeCol Team
*
* This file is part of FreeCol.
*
* FreeCol is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* FreeCol is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with FreeCol. If not, see <http://www.gnu.org/licenses/>.
*/
package net.sf.freecol.common.model;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.freecolandroid.xml.stream.XMLStreamException;
import org.freecolandroid.xml.stream.XMLStreamReader;
import org.freecolandroid.xml.stream.XMLStreamWriter;
import net.sf.freecol.common.option.OptionGroup;
import net.sf.freecol.common.option.StringOption;
import net.sf.freecol.common.util.RandomChoice;
public final class TileType extends FreeColGameObjectType {
private boolean forest;
private boolean water;
private boolean canSettle;
private boolean elevation;
private int basicMoveCost;
private int basicWorkTurns;
public static enum RangeType { HUMIDITY, TEMPERATURE, ALTITUDE }
private int[] humidity = new int[2];
private int[] temperature = new int[2];
private int[] altitude = new int[2];
private List<RandomChoice<ResourceType>> resourceType =
new ArrayList<RandomChoice<ResourceType>>();
/**
* Whether this TileType is connected to Europe.
*/
private boolean connected;
/**
* The primary goods produced by this tile type. In the original
* game, this is always food or null (in the case of the arctic).
*/
private AbstractGoods primaryGoods = null;
/**
* The secondary goods produced by this tile type. In the original
* game, this is never food, but may be null (in the case of the
* arctic).
*/
private AbstractGoods secondaryGoods = null;
/**
* A list of AbstractGoods produced by this TileType when it is
* not the colony center tile.
*/
private List<AbstractGoods> production = new ArrayList<AbstractGoods>();
private Map<String, AbstractGoods> primaryGoodsMap =
new HashMap<String, AbstractGoods>();
private Map<String, AbstractGoods> secondaryGoodsMap =
new HashMap<String, AbstractGoods>();
private Map<String, Map<GoodsType, AbstractGoods>> productionMap =
new HashMap<String, Map<GoodsType, AbstractGoods>>();
// ------------------------------------------------------------ constructor
public TileType(String id, Specification specification) {
super(id, specification);
}
// ------------------------------------------------------------ retrieval methods
/**
* Returns the index of this FreeColGameObjectType. The index
* imposes a total ordering consistent with equals on each class
* extending FreeColGameObjectType, but this ordering is nothing
* but the order in which the objects of the respective class were
* defined. It is guaranteed to remain stable only for a
* particular revision of a particular specification.
*
* @return an <code>int</code> value
*/
@Override
public int getIndex() {
return super.getIndex();
}
public boolean isForested() {
return forest;
}
public boolean isWater() {
return water;
}
/**
* Get the <code>Connected</code> value.
*
* @return a <code>boolean</code> value
*/
public boolean isConnected() {
return connected;
}
/**
* Is this tile an elevation.
*
* @return <tt>true</tt> if and only if the tile is an elevation, <tt>false</tt> otherwise.
*/
public boolean isElevation() {
return elevation;
}
public boolean canSettle() {
return canSettle;
}
/**
* Returns true if this TileType supports the given TileImprovementType.
*
* @param improvement a <code>TileImprovementType</code> value
* @return a <code>boolean</code> value
*/
public boolean canHaveImprovement(TileImprovementType improvement) {
return (improvement != null && improvement.isTileTypeAllowed(this));
}
public int getBasicMoveCost() {
return basicMoveCost;
}
public int getBasicWorkTurns() {
return basicWorkTurns;
}
public Set<Modifier> getDefenceBonus() {
return getModifierSet(Modifier.DEFENCE);
}
/**
* Returns the amount of goods of given GoodsType this TileType
* can produce. This method applies the production bonus to
* <code>0f</code>. Thus, it will always return <code>0</code>
* unless an additive modifier is present. This is intentional.
*
* @param goodsType a <code>GoodsType</code> value
* @param unitType an <code>UnitType</code> value
* @return an <code>int</code> value
* @see #getProductionBonus(GoodsType)
*/
public int getProductionOf(GoodsType goodsType, UnitType unitType) {
return (int) getFeatureContainer().applyModifier(0f, goodsType.getId(), unitType);
/*
Set<Modifier> result = featureContainer.getModifierSet(goodsType.getId(), unitType);
if (unitType != null && !result.isEmpty()) {
result.addAll(unitType.getFeatureContainer().getModifierSet(goodsType.getId()));
}
return (int) featureContainer.applyModifierSet(0f, null, result);
*/
}
/**
* Returns the production bonus for the given GoodsType.
*
* @param goodsType a <code>GoodsType</code> value
* @return a <code>Modifier</code> value
*/
public Set<Modifier> getProductionBonus(GoodsType goodsType) {
return getFeatureContainer().getModifierSet(goodsType.getId());
}
/**
* Get the <code>PrimaryGoods</code> value.
*
* @return an <code>AbstractGoods</code> value
*/
public AbstractGoods getPrimaryGoods() {
return primaryGoods;
}
/**
* Get the <code>PrimaryGoods</code> value at the tileProduction level
* with the ID given.
*
* @return an <code>AbstractGoods</code> value
*/
public AbstractGoods getPrimaryGoods(String tileProduction) {
AbstractGoods result = primaryGoodsMap.get(tileProduction);
if (result == null) {
result = primaryGoodsMap.get(null);
}
return result;
}
/**
* Returns true if the given <code>GoodsType</code> is the primary
* goods type of this TileType.
*
* @param type a <code>GoodsType</code> value
* @return a <code>boolean</code> value
*/
public boolean isPrimaryGoodsType(GoodsType type) {
return (primaryGoods != null && primaryGoods.getType() == type);
}
/**
* Set the <code>PrimaryGoods</code> value.
*
* @param newPrimaryGoods The new PrimaryGoods value.
*/
public void setPrimaryGoods(final AbstractGoods newPrimaryGoods) {
this.primaryGoods = newPrimaryGoods;
}
/**
* Get the <code>SecondaryGoods</code> value.
*
* @return an <code>AbstractGoods</code> value
*/
public AbstractGoods getSecondaryGoods() {
return secondaryGoods;
}
/**
* Get the <code>SecondaryGoods</code> value at the tileProduction level
* with the ID given.
*
* @return an <code>AbstractGoods</code> value
*/
public AbstractGoods getSecondaryGoods(String tileProduction) {
AbstractGoods result = secondaryGoodsMap.get(tileProduction);
if (result == null) {
result = secondaryGoodsMap.get(null);
}
return result;
}
/**
* Set the <code>SecondaryGoods</code> value.
*
* @param newSecondaryGoods The new SecondaryGoods value.
*/
public void setSecondaryGoods(final AbstractGoods newSecondaryGoods) {
this.secondaryGoods = newSecondaryGoods;
}
/**
* Returns true if the given <code>GoodsType</code> is the secondary
* goods type of this TileType.
*
* @param type a <code>GoodsType</code> value
* @return a <code>boolean</code> value
*/
public boolean isSecondaryGoodsType(GoodsType type) {
return (secondaryGoods != null && secondaryGoods.getType() == type);
}
/**
* Returns a list of all types of AbstractGoods produced by this
* TileType when it is not the colony center tile.
*
* @return a <code>List<AbstractGoods></code> value
*/
public List<AbstractGoods> getProduction() {
return production;
}
/**
* Returns a list of all types of AbstractGoods produced by this
* TileType when it is not the colony center tile.
*
* @param tileProduction
* @return a <code>List<AbstractGoods></code> value
*/
public List<AbstractGoods> getProduction(String tileProduction) {
Map<GoodsType, AbstractGoods> result = new HashMap<GoodsType, AbstractGoods>();
Map<GoodsType, AbstractGoods> defaultMap = productionMap.get(null);
Map<GoodsType, AbstractGoods> difficultyMap = productionMap.get(tileProduction);
if (defaultMap != null) {
result.putAll(defaultMap);
}
if (difficultyMap != null) {
result.putAll(difficultyMap);
}
return new ArrayList<AbstractGoods>(result.values());
}
public List<RandomChoice<ResourceType>> getWeightedResources() {
return resourceType;
}
public List<ResourceType> getResourceTypeList() {
List<ResourceType> result = new ArrayList<ResourceType>();
for (RandomChoice<ResourceType> resource : resourceType) {
result.add(resource.getObject());
}
return result;
}
/**
* Can this <code>TileType</code> contain a specified <code>ResourceType</code>?
*
* @param resourceType a <code>ResourceType</code> to test
* @return Whether this <code>TileType</code> contains the specified <code>ResourceType</code>
*/
public boolean canHaveResourceType(ResourceType resourceType) {
return getResourceTypeList().contains(resourceType);
}
public boolean withinRange(RangeType rangeType, int value) {
switch (rangeType) {
case HUMIDITY:
return (humidity[0] <= value && value <= humidity[1]);
case TEMPERATURE:
return (temperature[0] <= value && value <= temperature[1]);
case ALTITUDE:
return (altitude[0] <= value && value <= altitude[1]);
default:
return false;
}
}
/**
* Applies the difficulty level to this TileType.
*
* @param difficultyLevel difficulty level to apply
*/
@Override
public void applyDifficultyLevel(OptionGroup difficultyLevel) {
String tileProduction = ((StringOption) difficultyLevel.getOption("model.option.tileProduction"))
.getValue();
primaryGoods = getPrimaryGoods(tileProduction);
secondaryGoods = getSecondaryGoods(tileProduction);
production = getProduction(tileProduction);
// remove old modifiers
for (GoodsType goodsType : getSpecification().getGoodsTypeList()) {
getFeatureContainer().removeModifiers(goodsType.getId());
}
// add new modifiers
for (AbstractGoods goods : production) {
addModifier(new Modifier(goods.getType().getId(), this, goods.getAmount(),
Modifier.Type.ADDITIVE));
}
}
/**
* Makes an XML-representation of this object.
*
* @param out The output stream.
* @throws XMLStreamException if there are any problems writing to the
* stream.
*/
protected void toXMLImpl(XMLStreamWriter out) throws XMLStreamException {
super.toXML(out, getXMLElementTagName());
}
/**
* Write the attributes of this object to a stream.
*
* @param out The target stream.
* @throws XMLStreamException if there are any problems writing to
* the stream.
*/
@Override
protected void writeAttributes(XMLStreamWriter out)
throws XMLStreamException {
super.writeAttributes(out);
out.writeAttribute("basic-move-cost", Integer.toString(basicMoveCost));
out.writeAttribute("basic-work-turns", Integer.toString(basicWorkTurns));
out.writeAttribute("is-forest", Boolean.toString(forest));
out.writeAttribute("is-water", Boolean.toString(water));
out.writeAttribute("is-elevation", Boolean.toString(elevation));
out.writeAttribute("is-connected", Boolean.toString(connected));
out.writeAttribute("can-settle", Boolean.toString(canSettle));
}
/**
* Write the children of this object to a stream.
*
* @param out The target stream.
* @throws XMLStreamException if there are any problems writing to
* the stream.
*/
@Override
protected void writeChildren(XMLStreamWriter out)
throws XMLStreamException {
super.writeChildren(out);
out.writeStartElement("gen");
out.writeAttribute("humidityMin", Integer.toString(humidity[0]));
out.writeAttribute("humidityMax", Integer.toString(humidity[1]));
out.writeAttribute("temperatureMin", Integer.toString(temperature[0]));
out.writeAttribute("temperatureMax", Integer.toString(temperature[1]));
out.writeAttribute("altitudeMin", Integer.toString(altitude[0]));
out.writeAttribute("altitudeMax", Integer.toString(altitude[1]));
out.writeEndElement();
for (Map.Entry<String, AbstractGoods> entry
: primaryGoodsMap.entrySet()) {
out.writeStartElement("primary-production");
out.writeAttribute("goods-type",
entry.getValue().getType().getId());
out.writeAttribute(VALUE_TAG,
Integer.toString(entry.getValue().getAmount()));
if (entry.getKey() != null) {
out.writeAttribute("tile-production", entry.getKey());
}
out.writeEndElement();
}
for (Map.Entry<String, AbstractGoods> entry
: secondaryGoodsMap.entrySet()) {
out.writeStartElement("secondary-production");
out.writeAttribute("goods-type",
entry.getValue().getType().getId());
out.writeAttribute(VALUE_TAG,
Integer.toString(entry.getValue().getAmount()));
if (entry.getKey() != null) {
out.writeAttribute("tile-production", entry.getKey());
}
out.writeEndElement();
}
for (Map.Entry<String, Map<GoodsType, AbstractGoods>> entry
: productionMap.entrySet()) {
for (AbstractGoods goods : entry.getValue().values()) {
out.writeStartElement("production");
out.writeAttribute("goods-type", goods.getType().getId());
out.writeAttribute(VALUE_TAG,
Integer.toString(goods.getAmount()));
if (entry.getKey() != null) {
out.writeAttribute("tile-production", entry.getKey());
}
out.writeEndElement();
}
}
for (RandomChoice<ResourceType> choice : resourceType) {
out.writeStartElement("resource");
out.writeAttribute("type", choice.getObject().getId());
out.writeAttribute("probability",
Integer.toString(choice.getProbability()));
out.writeEndElement();
}
}
/**
* Reads the attributes of this object from an XML stream.
*
* @param in The XML input stream.
* @throws XMLStreamException if a problem was encountered
* during parsing.
*/
@Override
protected void readAttributes(XMLStreamReader in)
throws XMLStreamException {
super.readAttributes(in);
basicMoveCost = Integer.parseInt(in.getAttributeValue(null,
"basic-move-cost"));
basicWorkTurns = Integer.parseInt(in.getAttributeValue(null,
"basic-work-turns"));
forest = getAttribute(in, "is-forest", false);
water = getAttribute(in, "is-water", false);
elevation = getAttribute(in, "is-elevation", false);
canSettle = getAttribute(in, "can-settle", !water);
connected = getAttribute(in, "is-connected", false);
}
/**
* Reads a child object.
*
* @param in The XML stream to read.
* @exception XMLStreamException if an error occurs
*/
@Override
protected void readChild(XMLStreamReader in) throws XMLStreamException {
String childName = in.getLocalName();
if ("gen".equals(childName)) {
humidity[0] = getAttribute(in, "humidityMin", 0);
humidity[1] = getAttribute(in, "humidityMax", 100);
temperature[0] = getAttribute(in, "temperatureMin", -20);
temperature[1] = getAttribute(in, "temperatureMax", 40);
altitude[0] = getAttribute(in, "altitudeMin", 0);
altitude[1] = getAttribute(in, "altitudeMax", 0);
in.nextTag(); // close this element
} else if ("production".equals(childName)
|| "primary-production".equals(childName)
|| "secondary-production".equals(childName)) {
GoodsType type = getSpecification().getGoodsType(in.getAttributeValue(null, "goods-type"));
int amount = Integer.parseInt(in.getAttributeValue(null, VALUE_TAG));
AbstractGoods goods = new AbstractGoods(type, amount);
String tileProduction = in.getAttributeValue(null, "tile-production");
if ("primary-production".equals(childName)) {
primaryGoodsMap.put(tileProduction, goods);
} else if ("secondary-production".equals(childName)) {
secondaryGoodsMap.put(tileProduction, goods);
} else {
Map<GoodsType, AbstractGoods> oldValue = productionMap.get(tileProduction);
if (oldValue == null) {
oldValue = new HashMap<GoodsType, AbstractGoods>();
productionMap.put(tileProduction, oldValue);
}
oldValue.put(type, goods);
}
in.nextTag(); // close this element
} else if ("resource".equals(childName)) {
ResourceType type = getSpecification().getResourceType(in.getAttributeValue(null, "type"));
int probability = getAttribute(in, "probability", 100);
resourceType.add(new RandomChoice<ResourceType>(type, probability));
in.nextTag(); // close this element
} else {
super.readChild(in);
}
}
/**
* Returns the tag name of the root element representing this object.
*
* @return "tile-type".
*/
public static String getXMLElementTagName() {
return "tile-type";
}
}