/** * 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 org.freecolandroid.xml.stream.XMLStreamConstants; import org.freecolandroid.xml.stream.XMLStreamException; import org.freecolandroid.xml.stream.XMLStreamReader; import org.freecolandroid.xml.stream.XMLStreamWriter; /** * Encapsulates data common to all instances of a particular kind of * {@link Building}, such as the number of workplaces, and the types * of goods it produces and consumes. */ public final class BuildingType extends BuildableType implements Comparable<BuildingType> { private int level = 1; private int workPlaces = 3; private int basicProduction = 3; private int minSkill = UNDEFINED; private int maxSkill = INFINITY; private int upkeep = 0; private int priority = Consumer.BUILDING_PRIORITY; private GoodsType consumes, produces; private Modifier productionModifier = null; private BuildingType upgradesFrom; private BuildingType upgradesTo; /** * Creates a new <code>BuildingType</code> instance. * * @param id a <code>String</code> value * @param specification a <code>Specification</code> value */ public BuildingType(String id, Specification specification) { super(id, specification); setModifierIndex(Modifier.BUILDING_PRODUCTION_INDEX); } /** * Returns the BuildingType this BuildingType upgrades from. * * @return a <code>BuildingType</code> value */ public BuildingType getUpgradesFrom() { return upgradesFrom; } /** * Returns the BuildingType this BuildingType upgrades to. * * @return a <code>BuildingType</code> value */ public BuildingType getUpgradesTo() { return upgradesTo; } /** * Returns the first level of this BuildingType. * * @return a <code>BuildingType</code> value */ public BuildingType getFirstLevel() { BuildingType buildingType = this; while (buildingType.getUpgradesFrom() != null) { buildingType = buildingType.getUpgradesFrom(); } return buildingType; } /** * Returns the number of workplaces, that is the maximum number of * Units that can work in this BuildingType. * * @return an <code>int</code> value */ public int getWorkPlaces() { return workPlaces; } /** * Returns the production of a single Unit in this BuildingType * before any modifiers are applied. * * @return an <code>int</code> value */ public int getBasicProduction() { return basicProduction; } /** * Returns the type of goods consumed by this BuildingType. * * @return an <code>GoodsType</code> value */ public GoodsType getConsumedGoodsType() { return consumes; } /** * Returns the type of goods produced by this BuildingType. * * @return an <code>GoodsType</code> value */ public GoodsType getProducedGoodsType() { return produces; } /** * Returns the level of this BuildingType. * * @return an <code>int</code> value */ public int getLevel() { return level; } /** * Returns the amount of gold necessary to maintain a Building of * this type for one turn. * * @return an <code>int</code> value */ public int getUpkeep() { return upkeep; } /** * The consumption priority of a Building of this type. The higher * the priority, the earlier will the Consumer be allowed to * consume the goods it requires. * * @return an <code>int</code> value */ public int getPriority() { return priority; } /** * Describe <code>getType</code> method here. * * @return a <code>FreeColGameObjectType</code> value */ public FreeColGameObjectType getType() { return this; } /** * Describe <code>getProductionModifier</code> method here. * * @return a <code>Modifier</code> value */ public Modifier getProductionModifier() { return productionModifier; } /** * Compares this BuildingType to another. BuildingTypes are sorted * according to the order in which they are defined in the * specification. * * @param other a <code>BuildingType</code> value * @return an <code>int</code> value */ public int compareTo(BuildingType other) { return getIndex() - other.getIndex(); } /** * Returns true if the given UnitType could be added to a Building * of this type. * * @param unitType an <code>UnitType</code> value * @return a <code>boolean</code> value */ public boolean canAdd(UnitType unitType) { return workPlaces > 0 && unitType.hasSkill() && unitType.getSkill() >= minSkill && unitType.getSkill() <= maxSkill; } /** * Is this building type automatically built in any colony? * * @return a <code>boolean</code> value */ public boolean isAutomaticBuild() { return !needsGoodsToBuild() && getUpgradesFrom() == null; } /** * Get the index for the given Modifier. * * @param modifier a <code>Modifier</code> value * @return an <code>int</code> value */ @Override public final int getModifierIndex(Modifier modifier) { if (produces != null && produces.getId().equals(modifier.getId())) { return Modifier.AUTO_PRODUCTION_INDEX; } else { return getModifierIndex(); } } /** * Makes an XML-representation of this object. * * @param out The output stream. * @throws XMLStreamException if there are any problems writing to the * stream. */ public 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); if (upgradesFrom != null) { out.writeAttribute("upgradesFrom", upgradesFrom.getId()); } out.writeAttribute("workplaces", Integer.toString(workPlaces)); out.writeAttribute("basicProduction", Integer.toString(basicProduction)); if (minSkill > UNDEFINED) { out.writeAttribute("minSkill", Integer.toString(minSkill)); } if (maxSkill < INFINITY) { out.writeAttribute("maxSkill", Integer.toString(maxSkill)); } if (upkeep > 0) { out.writeAttribute("upkeep", Integer.toString(upkeep)); } if (priority != Consumer.BUILDING_PRIORITY) { out.writeAttribute("priority", Integer.toString(priority)); } if (consumes != null) { out.writeAttribute("consumes", consumes.getId()); } if (produces != null) { out.writeAttribute("produces", produces.getId()); } } /** * 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); String extendString = in.getAttributeValue(null, "extends"); BuildingType parent = (extendString == null) ? this : getSpecification().getBuildingType(extendString); String upgradeString = in.getAttributeValue(null, "upgradesFrom"); if (upgradeString == null) { level = 1; } else { upgradesFrom = getSpecification().getBuildingType(upgradeString); upgradesFrom.upgradesTo = this; level = upgradesFrom.level + 1; } setPopulationRequired(getAttribute(in, "required-population", parent.getPopulationRequired())); workPlaces = getAttribute(in, "workplaces", parent.workPlaces); basicProduction = getAttribute(in, "basicProduction", parent.basicProduction); consumes = getSpecification().getType(in, "consumes", GoodsType.class, parent.consumes); produces = getSpecification().getType(in, "produces", GoodsType.class, parent.produces); if (produces != null && basicProduction > 0) { productionModifier = new Modifier(produces.getId(), this, basicProduction, Modifier.Type.ADDITIVE); } minSkill = getAttribute(in, "minSkill", parent.minSkill); maxSkill = getAttribute(in, "maxSkill", parent.maxSkill); priority = getAttribute(in, "priority", parent.priority); upkeep = getAttribute(in, "upkeep", parent.upkeep); if (parent != this) { getFeatureContainer().add(parent.getFeatureContainer()); if (parent.isAbstractType()) { getFeatureContainer().replaceSource(parent, this); } } } /** * Reads the children 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 readChildren(XMLStreamReader in) throws XMLStreamException { while (in.nextTag() != XMLStreamConstants.END_ELEMENT) { readChild(in); } } /** * Compatibility hack, called from the specification reader when * it is finishing up. * @compat 0.9.x */ public void fixup09x() { try { if (hasAbility(Ability.AUTO_PRODUCTION)) { if (!hasAbility(Ability.AVOID_EXCESS_PRODUCTION)) { // old-style auto-production Ability ability = new Ability(Ability.AVOID_EXCESS_PRODUCTION); addAbility(ability); getFeatureContainer().removeModifiers("model.goods.horses"); float value = ("model.building.country".equals(getId())) ? 50 : 25; Modifier modifier = new Modifier("model.modifier.breedingDivisor", this, value, Modifier.Type.ADDITIVE); addModifier(modifier); getSpecification().addModifier(modifier); modifier = new Modifier("model.modifier.breedingFactor", this, 2, Modifier.Type.ADDITIVE); addModifier(modifier); getSpecification().addModifier(modifier); } if (getModifierSet("model.modifier.consumeOnlySurplusProduction").isEmpty()) { Modifier modifier = new Modifier("model.modifier.consumeOnlySurplusProduction", this, 0.5f, Modifier.Type.MULTIPLICATIVE); addModifier(modifier); getSpecification().addModifier(modifier); } } } catch(Exception e) { // no such ability, we don't care } } /** * Gets the tag name of the root element representing this object. * This method should be overwritten by any sub-class, preferably * with the name of the class with the first letter in lower case. * * @return "building-type". */ public static String getXMLElementTagName() { return "building-type"; } }