/**
* 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.Locale;
import java.util.Map;
import org.freecolandroid.xml.stream.XMLStreamConstants;
import org.freecolandroid.xml.stream.XMLStreamException;
import org.freecolandroid.xml.stream.XMLStreamReader;
import org.freecolandroid.xml.stream.XMLStreamWriter;
/**
* Represents one FoundingFather to be contained in a Player object.
* The FoundingFather is able to grant new abilities or bonuses to the
* player, or to cause certain events.
*/
public class FoundingFather extends FreeColGameObjectType {
/**
* The probability of this FoundingFather being offered for selection.
*/
private int[] weight = new int[4];
/**
* The type of this FoundingFather. One of the following constants.
*/
private FoundingFatherType type;
/**
* Players that want to elect this founding father must match all
* scopes.
*/
private List<Scope> scopes = new ArrayList<Scope>();
/**
* Describe events here.
*/
private List<Event> events = new ArrayList<Event>();
/**
* Holds the upgrades of Units caused by this FoundingFather.
*/
private Map<UnitType, UnitType> upgrades;
public static enum FoundingFatherType {
TRADE,
EXPLORATION,
MILITARY,
POLITICAL,
RELIGIOUS
}
/**
* A list of AbstractUnits generated by this FoundingFather.
*/
private List<AbstractUnit> units = null;
public FoundingFather(String id, Specification specification) {
super(id, specification);
setModifierIndex(Modifier.FATHER_PRODUCTION_INDEX);
}
/**
* Return the type of this FoundingFather.
*
* @return The type of this FoundingFather.
*/
public FoundingFatherType getType() {
return type;
}
/**
* Return the type of this FoundingFather.
*
* @param type A new <code>FoundingFatherType</code>.
*/
public void setType(FoundingFatherType type) {
this.type = type;
}
/**
* Return the localized type of this FoundingFather.
*
* @return a <code>String</code> value
*/
public String getTypeKey() {
return getTypeKey(type);
}
/**
* Return the localized type of the given FoundingFather.
*
* @param type an <code>int</code> value
* @return a <code>String</code> value
*/
public static String getTypeKey(FoundingFatherType type) {
return "model.foundingFather." + type.toString().toLowerCase(Locale.US);
}
/**
* Get the weight of this FoundingFather. This is used to select a
* random FoundingFather.
*
* @param age an <code>int</code> value
* @return an <code>int</code> value
*/
public int getWeight(int age) {
switch(age) {
case 1:
return weight[1];
case 2:
return weight[2];
case 3:
default:
return weight[3];
}
}
/**
* Get the <code>Units</code> value.
*
* @return a <code>List<AbstractUnit></code> value
*/
public final List<AbstractUnit> getUnits() {
return units;
}
/**
* Set the <code>Units</code> value.
*
* @param newUnits The new Units value.
*/
public final void setUnits(final List<AbstractUnit> newUnits) {
this.units = newUnits;
}
/**
* Get the <code>Events</code> value.
*
* @return a <code>List<Event></code> value
*/
public final List<Event> getEvents() {
return events;
}
/**
* Set the <code>Events</code> value.
*
* @param newEvents The new Events value.
*/
public final void setEvents(final List<Event> newEvents) {
this.events = newEvents;
}
/**
* Get the <code>Scopes</code> value.
*
* @return a <code>List<Scope></code> value
*/
public final List<Scope> getScopes() {
return scopes;
}
/**
* Returns true if this <code>FoundingFather</code> is available
* to the Player given.
*
* @param player a <code>Player</code> value
* @return a <code>boolean</code> value
*/
public boolean isAvailableTo(Player player) {
if (player.isEuropean()) {
if (scopes == null || scopes.isEmpty()) {
return true;
} else {
for (Scope scope : scopes) {
if (scope.appliesTo(player)) {
return true;
}
}
}
}
return false;
}
/**
* Get the <code>Upgrades</code> value.
*
* @return a <code>Map<UnitType, UnitType></code> value
*/
public final Map<UnitType, UnitType> getUpgrades() {
return upgrades;
}
/**
* Set the <code>Upgrades</code> value.
*
* @param newUpgrades The new Upgrades value.
*/
public final void setUpgrades(final Map<UnitType, UnitType> newUpgrades) {
this.upgrades = newUpgrades;
}
/**
* 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("type", type.toString().toLowerCase(Locale.US));
for (int index = 1; index <= 3; index++) {
out.writeAttribute("weight" + index,
Integer.toString(weight[index]));
}
}
/**
* 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);
if (events != null) {
for (Event event : events) {
event.toXMLImpl(out);
}
}
if (scopes != null) {
for (Scope scope : scopes) {
scope.toXMLImpl(out);
}
}
if (units != null) {
for (AbstractUnit unit : units) {
out.writeStartElement("unit");
out.writeAttribute(ID_ATTRIBUTE_TAG, unit.getId());
//out.writeAttribute("role", unit.getRole().toString().toLowerCase(Locale.US));
//out.writeAttribute("number", String.valueOf(unit.getNumber()));
out.writeEndElement();
}
}
if (upgrades != null) {
for (Map.Entry<UnitType, UnitType> entry : upgrades.entrySet()) {
out.writeStartElement("upgrade");
out.writeAttribute("from-id", entry.getKey().getId());
out.writeAttribute("to-id", entry.getValue().getId());
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);
String typeString = in.getAttributeValue(null,
"type").toUpperCase(Locale.US);
type = Enum.valueOf(FoundingFatherType.class, typeString);
weight[1] = Integer.parseInt(in.getAttributeValue(null, "weight1"));
weight[2] = Integer.parseInt(in.getAttributeValue(null, "weight2"));
weight[3] = Integer.parseInt(in.getAttributeValue(null, "weight3"));
}
/**
* 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);
}
}
/**
* 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 (Event.getXMLElementTagName().equals(childName)) {
Event event = new Event(null, getSpecification());
event.readFromXML(in);
events.add(event);
} else if ("scope".equals(childName)) {
scopes.add(new Scope(in));
} else if ("unit".equals(childName)) {
// AbstractUnit closes element
AbstractUnit unit = new AbstractUnit(in);
if (units == null) {
units = new ArrayList<AbstractUnit>();
}
units.add(unit);
} else if ("upgrade".equals(childName)) {
UnitType fromType = getSpecification()
.getUnitType(in.getAttributeValue(null, "from-id"));
UnitType toType = getSpecification()
.getUnitType(in.getAttributeValue(null, "to-id"));
if (fromType != null && toType != null) {
if (upgrades == null) {
upgrades = new HashMap<UnitType, UnitType>();
}
upgrades.put(fromType, toType);
}
in.nextTag();
} else {
super.readChild(in);
}
}
/**
* Compatibility hack, called from the specification when it is
* finishing up.
* @compat 0.9.x
*/
public void fixup09x() {
try {
// Cortes has changed
if (!getModifierSet("model.modifier.nativeTreasureModifier").isEmpty()) {
addAbility(new Ability("model.ability.plunderNatives"));
}
} catch (Exception e) {
// we don't care
}
}
/**
* Returns the tag name of the root element representing this object.
*
* @return "founding-father".
*/
public static String getXMLElementTagName() {
return "founding-father";
}
}