Monarch.java
| Index Score | ||
|---|---|---|
![]() |
![]() |
net.sf.freecol.common.model |
![]() |
![]() |
FreeCol |
View: Reasons, Metrics, Source Code
These are the metrics that contribute to the Enerjy Score for this file, ranked by impact. So the metrics listed at the top influence the score to a greater extent that the metrics listed at the bottom.
/**
* Copyright (C) 2002-2007 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.EnumMap;
import java.util.List;
import java.util.logging.Logger;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.XMLStreamWriter;
import net.sf.freecol.FreeCol;
import net.sf.freecol.common.model.Player.PlayerType;
import net.sf.freecol.common.model.Player.Stance;
import net.sf.freecol.common.model.Unit.Role;
import net.sf.freecol.common.util.RandomChoice;
import org.w3c.dom.Element;
/**
* This class implements the player's monarch, whose functions prior
* to the revolution include raising taxes, declaring war on other
* European countries, and occasionally providing military support.
*/
public final class Monarch extends FreeColGameObject {
/** The name of this monarch. */
private String name;
/** The player of this monarch. */
private Player player;
private static final Logger logger = Logger.getLogger(Monarch.class.getName());
private static final EquipmentType muskets = FreeCol.getSpecification().getEquipmentType("model.equipment.muskets");
private static final EquipmentType horses = FreeCol.getSpecification().getEquipmentType("model.equipment.horses");
/** Constants describing monarch actions. */
public static enum MonarchAction {
NO_ACTION,
RAISE_TAX,
ADD_TO_REF,
DECLARE_WAR,
SUPPORT_SEA,
SUPPORT_LAND,
OFFER_MERCENARIES,
LOWER_TAX,
NUMBER_OF_ACTIONS,
WAIVE_TAX,
ADD_UNITS }
/** The space required to transport all land units. */
int spaceRequired;
/** The current naval transport capacity. */
int capacity;
/** The number of land units in the REF. */
private List<AbstractUnit> landUnits = new ArrayList<AbstractUnit>();
/** The number of naval units in the REF. */
private List<AbstractUnit> navalUnits = new ArrayList<AbstractUnit>();
/** The minimum price for mercenaries. */
public static final int MINIMUM_PRICE = 100;
/**
* The maximum possible tax rate (given in percentage).
*/
public static final int MAXIMUM_TAX_RATE = 75;
/**
* The minimum tax rate (given in percentage) from where it
* can be lowered
*/
public static final int MINIMUM_TAX_RATE = 20;
/** Whether a frigate has been provided. */
private boolean supportSea = false;
/**
* Constructor.
*
* @param game The <code>Game</code> this <code>Monarch</code>
* should be created in.
* @param player The <code>Player</code> to create the
* <code>Monarch</code> for.
* @param name The name of the <code>Monarch</code>.
*/
public Monarch(Game game, Player player, String name) {
super(game);
if (player == null) {
throw new IllegalStateException("player == null");
}
this.player = player;
this.name = name;
// TODO: make this work with DifficultyLevel
int number = game.getGameOptions().getInteger(GameOptions.DIFFICULTY) * 2 + 3;
for (UnitType unitType : FreeCol.getSpecification().getUnitTypeList()) {
if (unitType.hasAbility("model.ability.refUnit")) {
if (unitType.hasAbility("model.ability.navalUnit")) {
navalUnits.add(new AbstractUnit(unitType, Role.DEFAULT, number));
if (unitType.canCarryUnits()) {
capacity += unitType.getSpace() * number;
}
} else if (unitType.hasAbility("model.ability.canBeEquipped")) {
landUnits.add(new AbstractUnit(unitType, Role.SOLDIER, number));
landUnits.add(new AbstractUnit(unitType, Role.DRAGOON, number));
spaceRequired += unitType.getSpaceTaken() * 2 * number;
} else {
landUnits.add(new AbstractUnit(unitType, Role.DEFAULT, number));
spaceRequired += unitType.getSpaceTaken() * number;
}
}
}
}
/**
* Initiates a new <code>Monarch</code> from an <code>Element</code>
* and registers this <code>Monarch</code> at the specified game.
*
* @param game The <code>Game</code> this object belongs to.
* @param in The input stream containing the XML.
* @throws XMLStreamException if a problem was encountered
* during parsing.
*/
public Monarch(Game game, XMLStreamReader in) throws XMLStreamException {
super(game, in);
readFromXML(in);
}
/**
* Initiates a new <code>Monarch</code> from an <code>Element</code>
* and registers this <code>Monarch</code> at the specified game.
*
* @param game The <code>Game</code> this object belongs to.
* @param e An XML-element that will be used to initialize
* this object.
*/
public Monarch(Game game, Element e) {
super(game, e);
readFromXMLElement(e);
}
/**
* Initiates a new <code>Monarch</code>
* with the given ID. The object should later be
* initialized by calling either
* {@link #readFromXML(XMLStreamReader)} or
* {@link #readFromXMLElement(Element)}.
*
* @param game The <code>Game</code> in which this object belong.
* @param id The unique identifier for this object.
*/
public Monarch(Game game, String id) {
super(game, id);
}
/**
* Returns a monarch action. Not all actions are always
* applicable, and their probability depends on the player's
* difficulty settings. This method can only be called by the
* server.
*
* @return A monarch action.
*/
public MonarchAction getAction() {
/**
* Random numbers can be generated by this method since it is only
* invoked by the endTurn method of the server's InGameController.
*/
List<RandomChoice<MonarchAction>> choices = getActionChoices();
if (choices == null) {
return MonarchAction.NO_ACTION;
} else {
return RandomChoice.getWeightedRandom(getGame().getModelController().getPseudoRandom(),
choices);
}
}
public List<RandomChoice<MonarchAction>> getActionChoices() {
// TODO: make this work with DifficultyLevel
int dx = getGame().getGameOptions().getInteger(GameOptions.DIFFICULTY) + 1; // 1-5
int turn = getGame().getTurn().getNumber();
int grace = (6 - dx) * 10; // 10-50
// nothing happens during the first few turns, nor after the
// revolution
if (turn < grace || player.getPlayerType() != PlayerType.COLONIAL) {
return null;
}
boolean canDeclareWar = false;
boolean atWar = false;
// Benjamin Franklin puts an end to the monarch's interference
if (!player.hasAbility("model.ability.ignoreEuropeanWars")) {
for (Player enemy : getGame().getPlayers()) {
if (!enemy.isEuropean() || enemy.isREF()) {
continue;
}
if (player.hasContacted(enemy)) {
switch (player.getStance(enemy)) {
case WAR:
atWar = true;
break;
case PEACE:
case CEASE_FIRE:
canDeclareWar = true;
break;
}
}
}
}
/** The probabilities of these actions. */
List<RandomChoice<MonarchAction>> choices = new ArrayList<RandomChoice<MonarchAction>>();
// the more time has passed, the less likely the monarch will
// do nothing
choices.add(new RandomChoice<MonarchAction>(MonarchAction.NO_ACTION, Math.max(200 - turn, 100)));
if (player.getTax() < MAXIMUM_TAX_RATE) {
choices.add(new RandomChoice<MonarchAction>(MonarchAction.RAISE_TAX, 10 + dx));
}
choices.add(new RandomChoice<MonarchAction>(MonarchAction.ADD_TO_REF, 10 + dx));
if (canDeclareWar) {
choices.add(new RandomChoice<MonarchAction>(MonarchAction.DECLARE_WAR, 5 + dx));
}
// provide no more than one frigate
if (player.hasBeenAttackedByPrivateers() && !supportSea) {
choices.add(new RandomChoice<MonarchAction>(MonarchAction.SUPPORT_SEA, 6 - dx));
}
if (atWar) {
// disable for the moment
//MonarchAction.SUPPORT_LAND = 6 - dx;
if (player.getGold() > MINIMUM_PRICE) {
choices.add(new RandomChoice<MonarchAction>(MonarchAction.OFFER_MERCENARIES, 6 - dx));
}
}
if (player.getTax() > MINIMUM_TAX_RATE) {
choices.add(new RandomChoice<MonarchAction>(MonarchAction.LOWER_TAX, 10 - dx));
}
return choices;
}
/**
* Returns a Set of all REF units.
*
* @return a Set of all REF units.
*/
public List<AbstractUnit> getREF() {
List<AbstractUnit> result = new ArrayList<AbstractUnit>(landUnits);
result.addAll(navalUnits);
return result;
}
/**
* Returns the new increased tax.
*
* @return The increased tax.
*/
public int getNewTax(MonarchAction taxChange) {
int newTax = 110; // set bigger that 100% to catch errors
int adjustment = 0;
switch(taxChange){
case RAISE_TAX:
int turn = getGame().getTurn().getNumber();
adjustment = (6 - player.getDifficulty().getIndex()) * 10; // 20-60
// later in the game, the taxes will increase by more
int increase = getGame().getModelController().getPseudoRandom().nextInt(5 + turn/adjustment) + 1;
newTax = player.getTax() + increase;
newTax = Math.min(newTax, MAXIMUM_TAX_RATE);
break;
case LOWER_TAX:
adjustment = 10 - player.getDifficulty().getIndex(); // 5-10
int decrease = getGame().getModelController().getPseudoRandom().nextInt(adjustment) + 1;
newTax = player.getTax() - decrease;
newTax = Math.max(newTax, MINIMUM_TAX_RATE);
break;
default:
logger.warning("Wrong tax change type");
return newTax;
}
return newTax;
}
/**
* Returns units available as mercenaries.
*
* @return A troop of mercenaries.
*/
public List<AbstractUnit> getMercenaries() {
List<AbstractUnit> mercenaries = new ArrayList<AbstractUnit>();
List<UnitType> unitTypes = new ArrayList<UnitType>();
for (UnitType unitType : FreeCol.getSpecification().getUnitTypeList()) {
if (unitType.hasAbility("model.ability.mercenaryUnit")) {
unitTypes.add(unitType);
}
}
int gold = player.getGold();
int price = 0;
int limit = unitTypes.size();
UnitType unitType = null;
for (int count = 0; count < limit; count++) {
int index = getGame().getModelController().getPseudoRandom().nextInt(unitTypes.size());
unitType = unitTypes.get(index);
if (unitType.hasAbility("model.ability.canBeEquipped")) {
int newPrice = getPrice(unitType, Role.DRAGOON);
for (int number = 3; number > 0; number--) {
if (price + newPrice * number <= gold) {
mercenaries.add(new AbstractUnit(unitType, Role.DRAGOON, number));
price += newPrice * number;
break;
}
}
newPrice = getPrice(unitType, Role.SOLDIER);
for (int number = 3; number > 0; number--) {
if (price + newPrice * number <= gold) {
mercenaries.add(new AbstractUnit(unitType, Role.SOLDIER, number));
price += newPrice * number;
break;
}
}
} else {
int newPrice = getPrice(unitType, Role.DEFAULT);
for (int number = 3; number > 0; number--) {
if (price + newPrice * number <= gold) {
mercenaries.add(new AbstractUnit(unitType, Role.DEFAULT, number));
price += newPrice * number;
break;
}
}
}
unitTypes.remove(index);
}
if (price == 0 && unitType != null) {
if (unitType.hasAbility("model.ability.canBeEquipped")) {
mercenaries.add(new AbstractUnit(unitType, Role.SOLDIER, 1));
} else {
mercenaries.add(new AbstractUnit(unitType, Role.DEFAULT, 1));
}
}
return mercenaries;
}
/**
* Returns units to be added to the Royal Expeditionary Force.
*
* @return An addition to the Royal Expeditionary Force.
*/
public List<AbstractUnit> addToREF() {
ArrayList<AbstractUnit> result = new ArrayList<AbstractUnit>();
if (capacity < spaceRequired) {
AbstractUnit unit = navalUnits.get(getGame().getModelController().getPseudoRandom().nextInt(navalUnits.size()));
result.add(new AbstractUnit(unit.getUnitType(), unit.getRole(), 1));
} else {
int number = getGame().getModelController().getPseudoRandom().nextInt(3) + 1;
AbstractUnit unit = landUnits.get(getGame().getModelController().getPseudoRandom().nextInt(landUnits.size()));
result.add(new AbstractUnit(unit.getUnitType(), unit.getRole(), number));
}
return result;
}
/**
* Adds units to the Royal Expeditionary Force.
*
* @param units The addition to the Royal Expeditionary Force.
*/
public void addToREF(List<AbstractUnit> units) {
for (AbstractUnit unitToAdd : units) {
UnitType unitType = unitToAdd.getUnitType();
if (unitType.hasAbility("model.ability.navalUnit")) {
for (AbstractUnit refUnit : navalUnits) {
if (refUnit.getUnitType().equals(unitType)) {
refUnit.setNumber(refUnit.getNumber() + unitToAdd.getNumber());
if (unitType.canCarryUnits()) {
capacity += unitType.getSpace() * unitToAdd.getNumber();
}
}
}
} else {
for (AbstractUnit refUnit : landUnits) {
if (refUnit.getUnitType().equals(unitType) &&
refUnit.getRole().equals(unitToAdd.getRole())) {
refUnit.setNumber(refUnit.getNumber() + unitToAdd.getNumber());
spaceRequired += unitType.getSpaceTaken() * unitToAdd.getNumber();
}
}
}
}
}
/**
* Returns the price for the given units.
*
* @param units The units to get a price for.
* @param rebate Whether to grant a rebate.
* @return The price fo the units.
*/
public int getPrice(List<AbstractUnit> units, boolean rebate) {
int price = 0;
for (AbstractUnit unit : units) {
int newPrice = getPrice(unit.getUnitType(), unit.getRole());
price += newPrice * unit.getNumber();
}
if (price > player.getGold() && rebate) {
return player.getGold();
} else {
return price;
}
}
public int getPrice(UnitType unitType, Role role) {
if (unitType.hasPrice()) {
int price = player.getEurope().getUnitPrice(unitType);
if (Role.SOLDIER.equals(role)) {
price += getEquipmentPrice(muskets);
} else if (Role.DRAGOON.equals(role)) {
price += getEquipmentPrice(muskets);
price += getEquipmentPrice(horses);
}
return price / 10 + 25 * player.getDifficulty().getIndex();
} else {
return 1000000;
}
}
private int getEquipmentPrice(EquipmentType equipment) {
int price = 0;
for (AbstractGoods goods : equipment.getGoodsRequired()) {
price += player.getMarket().getBidPrice(goods.getType(), goods.getAmount());
}
return price;
}
/**
* Returns the nation of another player to declare war on.
*
* @return The enemy nation.
*/
public Player declareWar() {
ArrayList<Player> europeanPlayers = new ArrayList<Player>();
for (Player enemy : getGame().getPlayers()) {
if (enemy == player) {
continue;
} else if (!player.hasContacted(enemy)) {
continue;
} else if (!enemy.isEuropean() || enemy.isREF()) {
continue;
}
Stance stance = player.getStance(enemy);
if (stance == Stance.PEACE || stance == Stance.CEASE_FIRE) {
europeanPlayers.add(enemy);
}
}
if (europeanPlayers.size() > 0) {
int randomInt = getGame().getModelController().getPseudoRandom().nextInt(europeanPlayers.size());
Player enemy = europeanPlayers.get(randomInt);
return enemy;
}
return null;
}
/**
* Returns an addition to the colonial forces.
*
* @return An addition to the colonial forces.
*/
/*
public int[] supportLand() {
int[] units = new int[NUMBER_OF_TYPES];
switch (player.getDifficulty()) {
case Player.VERY_EASY:
units[ARTILLERY] = 1;
units[DRAGOON] = 2;
break;
case Player.EASY:
units[DRAGOON] = 2;
units[INFANTRY] = 1;
break;
case Player.MEDIUM:
units[DRAGOON] = 2;
break;
case Player.HARD:
units[DRAGOON] = 1;
units[INFANTRY] = 1;
break;
case Player.VERY_HARD:
units[INFANTRY] = 1;
break;
}
return units;
}
*/
/**
* This method writes an XML-representation of this object to
* the given stream.
*
* <br><br>
*
* Only attributes visible to the given <code>Player</code> will
* be added to that representation if <code>showAll</code> is
* set to <code>false</code>.
*
* @param out The target stream.
* @param player The <code>Player</code> this XML-representation
* should be made for, or <code>null</code> if
* <code>showAll == true</code>.
* @param showAll Only attributes visible to <code>player</code>
* will be added to the representation if <code>showAll</code>
* is set to <i>false</i>.
* @param toSavedGame If <code>true</code> then information that
* is only needed when saving a game is added.
* @throws XMLStreamException if there are any problems writing
* to the stream.
*/
protected void toXMLImpl(XMLStreamWriter out, Player player, boolean showAll, boolean toSavedGame)
throws XMLStreamException {
// Start element:
out.writeStartElement(getXMLElementTagName());
out.writeAttribute("ID", getId());
out.writeAttribute("player", this.player.getId());
out.writeAttribute("name", name);
out.writeAttribute("supportSea", String.valueOf(supportSea));
out.writeStartElement("navalUnits");
for (AbstractUnit unit : navalUnits) {
unit.toXMLImpl(out);
}
out.writeEndElement();
out.writeStartElement("landUnits");
for (AbstractUnit unit : landUnits) {
unit.toXMLImpl(out);
}
out.writeEndElement();
out.writeEndElement();
}
/**
* Initialize this object from an XML-representation of this object.
* @param in The input stream with the XML.
*/
protected void readFromXMLImpl(XMLStreamReader in) throws XMLStreamException {
setId(in.getAttributeValue(null, "ID"));
player = (Player) getGame().getFreeColGameObject(in.getAttributeValue(null, "player"));
if (player == null) {
player = new Player(getGame(), in.getAttributeValue(null, "player"));
}
name = in.getAttributeValue(null, "name");
supportSea = Boolean.valueOf(in.getAttributeValue(null, "supportSea")).booleanValue();
while (in.nextTag() != XMLStreamConstants.END_ELEMENT) {
String childName = in.getLocalName();
if ("navalUnits".equals(childName)) {
while (in.nextTag() != XMLStreamConstants.END_ELEMENT) {
AbstractUnit newUnit = new AbstractUnit(in);
navalUnits.add(newUnit);
}
} else if ("landUnits".equals(childName)) {
while (in.nextTag() != XMLStreamConstants.END_ELEMENT) {
AbstractUnit newUnit = new AbstractUnit(in);
landUnits.add(newUnit);
}
}
}
// sanity check: we should be on the closing tag
if (!in.getLocalName().equals(Monarch.getXMLElementTagName())) {
logger.warning("Error parsing xml: expecting closing tag </" + Monarch.getXMLElementTagName() + "> "+
"found instead: " +in.getLocalName());
}
}
/**
* 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 "monarch".
*/
public static String getXMLElementTagName() {
return "monarch";
}
}
The table below shows all metrics for Monarch.java.




