/**
* 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.server.ai;
import java.util.Comparator;
import java.util.logging.Logger;
import net.sf.freecol.common.model.Colony;
import net.sf.freecol.common.model.Europe;
import net.sf.freecol.common.model.Goods;
import net.sf.freecol.common.model.GoodsContainer;
import net.sf.freecol.common.model.GoodsType;
import net.sf.freecol.common.model.Locatable;
import net.sf.freecol.common.model.Location;
import net.sf.freecol.common.model.Tile;
import net.sf.freecol.server.ai.mission.TransportMission;
import org.freecolandroid.xml.stream.XMLStreamException;
import org.freecolandroid.xml.stream.XMLStreamReader;
import org.freecolandroid.xml.stream.XMLStreamWriter;
import org.w3c.dom.Element;
/**
* Objects of this class contains AI-information for a single {@link Goods}.
*/
public class AIGoods extends AIObject implements Transportable {
private static final Logger logger = Logger.getLogger(AIGoods.class.getName());
private static final Comparator<AIGoods> aiGoodsPriorityComparator
= new Comparator<AIGoods>() {
public int compare(AIGoods g1, AIGoods g2) {
return g2.getTransportPriority() - g1.getTransportPriority();
}
};
public static final int IMPORTANT_DELIVERY = 110;
public static final int FULL_DELIVERY = 100;
/**
* The priority of tools intended for a Colony with none stored
* at the present (and with no special needs).
*/
public static final int TOOLS_FOR_COLONY_PRIORITY = 10;
/**
* The extra priority value added to the base value of
* {@link #TOOLS_FOR_COLONY_PRIORITY}
* for each ColonyTile needing a terrain improvement.
*/
public static final int TOOLS_FOR_IMPROVEMENT = 10;
/**
* The extra priority value added to the base value of
* {@link #TOOLS_FOR_COLONY_PRIORITY}
* if a Pioneer is lacking tools
*/
public static final int TOOLS_FOR_PIONEER = 90;
/**
* The extra priority value added to the base value of
* {@link #TOOLS_FOR_COLONY_PRIORITY}
* if a building is lacking tools. The number of tools
* is also added to the total amount.
*/
public static final int TOOLS_FOR_BUILDING = 100;
private Goods goods;
private Location destination;
private int transportPriority;
private AIUnit transport = null;
/**
* Creates a new <code>AIGoods</code>.
*
* @param aiMain The main AI-object.
* @param location The location of the goods.
* @param type The type of goods.
* @param amount The amount of goods.
* @param destination The destination of the goods. This is the
* <code>Location</code> to which the goods should be transported.
*/
public AIGoods(AIMain aiMain, Location location, GoodsType type, int amount, Location destination) {
super(aiMain, getXMLElementTagName() + ":" + aiMain.getNextID());
goods = new Goods(aiMain.getGame(), location, type, amount);
this.destination = destination;
}
/**
* Creates a new <code>AIGoods</code>.
*
* @param aiMain The main AI-object.
* @param element An <code>Element</code> containing an
* XML-representation of this object.
*/
public AIGoods(AIMain aiMain, Element element) {
super(aiMain, element.getAttribute(ID_ATTRIBUTE));
readFromXMLElement(element);
}
/**
* Creates a new <code>AIGoods</code>.
*
* @param aiMain The main AI-object.
* @param in The input stream containing the XML.
* @throws XMLStreamException if a problem was encountered
* during parsing.
*/
public AIGoods(AIMain aiMain, XMLStreamReader in) throws XMLStreamException {
super(aiMain, in.getAttributeValue(null, ID_ATTRIBUTE));
readFromXML(in);
}
/**
* Creates a new <code>AIGoods</code>.
*
* @param aiMain The main AI-object.
* @param id The unique ID of this object.
*/
public AIGoods(AIMain aiMain, String id) {
super(aiMain, id);
uninitialized = true;
}
/**
* Gets a comparator that sorts by priority.
*
* @return A priority comparator.
*/
public static Comparator<AIGoods> getAIGoodsPriorityComparator() {
return aiGoodsPriorityComparator;
}
/**
* Aborts the given <code>Wish</code>.
*
* @param w The <code>Wish</code> to be aborted.
*/
public void abortWish(Wish w) {
if (destination == w.getDestination()) {
destination = null;
}
if (w.getTransportable() == this) {
w.dispose();
}
}
/**
* Returns the source for this <code>Transportable</code>.
* This is normally the location of the
* {@link #getTransportLocatable locatable}.
*
* @return The source for this <code>Transportable</code>.
*/
public Location getTransportSource() {
return goods.getLocation();
}
/**
* Returns the destination for this <code>Transportable</code>.
* This can either be the target {@link Tile} of the transport
* or the target for the entire <code>Transportable</code>'s
* mission. The target for the tansport is determined by
* {@link TransportMission} in the latter case.
*
* @return The destination for this <code>Transportable</code>.
*/
public Location getTransportDestination() {
return destination;
}
/**
* Gets the <code>Locatable</code> which should be transported.
* @return The <code>Locatable</code>.
*/
public Locatable getTransportLocatable() {
return getGoods();
}
/**
* Gets the priority of transporting this <code>Transportable</code>
* to it's destination.
*
* @return The priority of the transport.
*/
public int getTransportPriority() {
if (goods.getAmount() <= GoodsContainer.CARGO_SIZE) {
return goods.getAmount();
} else {
return transportPriority;
}
}
/**
* Increases the transport priority of this <code>Transportable</code>.
* This method gets called every turn the <code>Transportable</code>
* have not been put on a carrier's transport list.
*/
public void increaseTransportPriority() {
transportPriority++;
}
/**
* Gets the carrier responsible for transporting this
* <code>Transportable</code>.
*
* @return The <code>AIUnit</code> which has this
* <code>Transportable</code> in it's transport list. This
* <code>Transportable</code> has not been scheduled for
* transport if this value is <code>null</code>.
*
*/
public AIUnit getTransport() {
return transport;
}
/**
* Disposes this object.
*/
public void dispose() {
setTransport(null);
if (destination != null) {
if (destination instanceof Colony) {
(getAIMain().getAIColony((Colony) destination)).removeAIGoods(this);
} else if (destination instanceof Europe) {
// Nothing to remove.
} else {
logger.warning("Unknown type of destination: " + destination);
}
}
super.dispose();
}
/**
* Sets the carrier responsible for transporting this
* <code>Transportable</code>.
*
* @param transport The <code>AIUnit</code> which has this
* <code>Transportable</code> in it's transport list. This
* <code>Transportable</code> has not been scheduled for
* transport if this value is <code>null</code>.
*
*/
public void setTransport(AIUnit transport) {
if (this.transport == transport) return;
AIUnit oldTransport = this.transport;
this.transport = transport;
if (oldTransport != null) {
// Remove from old carrier:
if (oldTransport.getMission() != null
&& oldTransport.getMission() instanceof TransportMission) {
TransportMission tm = (TransportMission) oldTransport.getMission();
if (tm.isOnTransportList(this)) {
tm.removeFromTransportList(this);
}
}
}
if (transport != null
&& transport.getMission() instanceof TransportMission
&& !((TransportMission) transport.getMission()).isOnTransportList(this)) {
// Add to new carrier:
((TransportMission) transport.getMission()).addToTransportList(this);
}
}
/**
* Sets the priority of getting the goods to the {@link
* #getTransportDestination}.
*
* @param transportPriority The priority.
*/
public void setTransportPriority(int transportPriority) {
this.transportPriority = transportPriority;
}
/**
* Gets the goods this <code>AIGoods</code> is controlling.
*
* @return The <code>Goods</code>.
*/
public Goods getGoods() {
return goods;
}
/**
* Sets the goods this <code>AIGoods</code> is controlling.
*
* @param goods The <code>Goods</code>.
*/
public void setGoods(Goods goods) {
if (goods == null) {
throw new NullPointerException();
}
this.goods = goods;
}
/**
* Writes this object to an XML stream.
*
* @param out The target stream.
* @throws XMLStreamException if there are any problems writing
* to the stream.
*/
protected void toXMLImpl(XMLStreamWriter out) throws XMLStreamException {
if (goods == null) return; // Workaround for BR#3456180
out.writeStartElement(getXMLElementTagName());
out.writeAttribute(ID_ATTRIBUTE, getId());
if (destination != null) {
out.writeAttribute("destination", destination.getId());
}
out.writeAttribute("transportPriority", Integer.toString(transportPriority));
if (transport != null) {
if (getAIMain().getAIObject(transport.getId()) == null) {
logger.warning("broken reference to transport");
} else if (transport.getMission() != null
&& transport.getMission() instanceof TransportMission
&& !((TransportMission) transport.getMission()).isOnTransportList(this)) {
logger.warning("We should not be on the transport list.");
} else {
out.writeAttribute("transport", transport.getId());
}
}
goods.toXML(out);
out.writeEndElement();
}
/**
* Reads information for this object from an XML stream.
*
* @param in The input stream with the XML.
* @throws XMLStreamException if there are any problems reading
* from the stream.
*/
protected void readFromXMLImpl(XMLStreamReader in)
throws XMLStreamException {
setId(in.getAttributeValue(null, ID_ATTRIBUTE));
final String destinationStr = in.getAttributeValue(null, "destination");
if (destinationStr != null) {
destination = (Location) getAIMain().getFreeColGameObject(destinationStr);
if (destination == null) {
logger.warning("Could not find destination: " + destinationStr);
}
} else {
destination = null;
}
transportPriority = Integer.parseInt(in.getAttributeValue(null, "transportPriority"));
final String transportStr = in.getAttributeValue(null, "transport");
if (transportStr != null) {
transport = (AIUnit) getAIMain().getAIObject(transportStr);
if (transport == null) {
transport = new AIUnit(getAIMain(), transportStr);
}
} else {
transport = null;
}
in.nextTag();
if (goods != null) {
goods.readFromXML(in);
} else {
goods = new Goods(getAIMain().getGame(), in);
}
in.nextTag();
}
/**
* Returns a <code>String</code>-representation of this object.
*
* @return A <code>String</code> representing this object for
* debugging purposes.
*/
@Override
public String toString() {
return "AIGoods@" + hashCode() + "[ " + goods
+ " /" + transportPriority + "]";
}
/**
* Returns the tag name of the root element representing this object.
*
* @return "aiGoods"
*/
public static String getXMLElementTagName() {
return "aiGoods";
}
}