/*
* MegaMek -
* Copyright (C) 2000,2001,2002,2003,2004,2005 Ben Mazur (bmazur@sev.org)
*
* This program 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.
*
* This program 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.
*/
package megamek.common;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
import megamek.common.actions.ArtilleryAttackAction;
import megamek.common.actions.AttackAction;
import megamek.common.actions.EntityAction;
import megamek.common.event.GameBoardChangeEvent;
import megamek.common.event.GameBoardNewEvent;
import megamek.common.event.GameEndEvent;
import megamek.common.event.GameEntityChangeEvent;
import megamek.common.event.GameEntityNewEvent;
import megamek.common.event.GameEntityNewOffboardEvent;
import megamek.common.event.GameEntityRemoveEvent;
import megamek.common.event.GameEvent;
import megamek.common.event.GameListener;
import megamek.common.event.GameMapQueryEvent;
import megamek.common.event.GameNewActionEvent;
import megamek.common.event.GamePhaseChangeEvent;
import megamek.common.event.GamePlayerChangeEvent;
import megamek.common.event.GamePlayerChatEvent;
import megamek.common.event.GamePlayerConnectedEvent;
import megamek.common.event.GamePlayerDisconnectedEvent;
import megamek.common.event.GameReportEvent;
import megamek.common.event.GameSettingsChangeEvent;
import megamek.common.event.GameTurnChangeEvent;
import megamek.common.options.GameOptions;
import megamek.common.weapons.AttackHandler;
import megamek.server.SmokeCloud;
import megamek.server.victory.SpaghettiVictoryFactory;
import megamek.server.victory.Victory;
import megamek.server.victory.VictoryFactory;
/**
* The game class is the root of all data about the game in progress. Both the
* Client and the Server should have one of these objects and it is their job to
* keep it synched.
*/
public class Game implements Serializable, IGame {
/**
*
*/
private static final long serialVersionUID = 8376320092671792532L;
/**
* Define constants to describe the condition a unit was in when it wass
* removed from the game.
*/
private GameOptions options = new GameOptions();
public IBoard board = new Board();
private Vector<Entity> entities = new Vector<Entity>();
private Hashtable<Integer, Entity> entityIds = new Hashtable<Integer, Entity>();
/** Track entities removed from the game (probably by death) */
Vector<Entity> vOutOfGame = new Vector<Entity>();
private Vector<Player> players = new Vector<Player>();
private Vector<Team> teams = new Vector<Team>(); // DES
private Hashtable<Integer, Player> playerIds = new Hashtable<Integer, Player>();
/** have the entities been deployed? */
private boolean deploymentComplete = false;
/** how's the weather? */
private PlanetaryConditions planetaryConditions = new PlanetaryConditions();
/** what round is it? */
private int roundCount = 0;
/** The current turn list */
private Vector<GameTurn> turnVector = new Vector<GameTurn>();
private int turnIndex = 0;
/** The present phase */
private Phase phase = Phase.PHASE_UNKNOWN;
/** The past phase */
private Phase lastPhase = Phase.PHASE_UNKNOWN;
// phase state
private Vector<EntityAction> actions = new Vector<EntityAction>();
private Vector<AttackAction> pendingCharges = new Vector<AttackAction>();
private Vector<AttackAction> pendingRams = new Vector<AttackAction>();
private Vector<AttackAction> pendingTeleMissileAttacks = new Vector<AttackAction>();
private Vector<PilotingRollData> pilotRolls = new Vector<PilotingRollData>();
private Vector<PilotingRollData> extremeGravityRolls = new Vector<PilotingRollData>();
private Vector<PilotingRollData> controlRolls = new Vector<PilotingRollData>();
private Vector<Team> initiativeRerollRequests = new Vector<Team>();
// reports
private GameReports gameReports = new GameReports();
private boolean forceVictory = false;
private int victoryPlayerId = Player.PLAYER_NONE;
private int victoryTeam = Player.TEAM_NONE;
private Hashtable<Integer, Vector<Entity>> deploymentTable = new Hashtable<Integer, Vector<Entity>>();
private int lastDeploymentRound = 0;
private Hashtable<Coords, Vector<Minefield>> minefields = new Hashtable<Coords, Vector<Minefield>>();
private Vector<Minefield> vibrabombs = new Vector<Minefield>();
private Vector<AttackHandler> attacks = new Vector<AttackHandler>();
private Vector<ArtilleryAttackAction> offboardArtilleryAttacks = new Vector<ArtilleryAttackAction>();
private int lastEntityId;
private Vector<TagInfo> tagInfoForTurn = new Vector<TagInfo>();
private Vector<Flare> flares = new Vector<Flare>();
private HashMap<String, Object> victoryContext = null;
// internal integer value for an external game id link
private int externalGameId = 0;
// victory condition related stuff
private Victory victory = null;
private VictoryFactory vf = new SpaghettiVictoryFactory();
// smoke clouds
private ArrayList<SmokeCloud> smokeCloudList = new ArrayList<SmokeCloud>();
/**
* Constructor
*/
public Game() {
}
// Added public accessors for external game id
public int getExternalGameId() {
return externalGameId;
}
public void setExternalGameId(int value) {
externalGameId = value;
}
public IBoard getBoard() {
return board;
}
public void setBoard(IBoard board) {
IBoard oldBoard = this.board;
this.board = board;
processGameEvent(new GameBoardNewEvent(this, oldBoard, board));
}
public boolean containsMinefield(Coords coords) {
return minefields.containsKey(coords);
}
public Vector<Minefield> getMinefields(Coords coords) {
Vector<Minefield> mfs = minefields.get(coords);
if (mfs == null) {
return new Vector<Minefield>();
}
return mfs;
}
public int getNbrMinefields(Coords coords) {
Vector<Minefield> mfs = minefields.get(coords);
if (mfs == null) {
return 0;
}
return mfs.size();
}
/**
* Get the coordinates of all mined hexes in the game.
*
* @return an <code>Enumeration</code> of the <code>Coords</code>
* containing minefields. This will not be <code>null</code>.
*/
public Enumeration<Coords> getMinedCoords() {
return minefields.keys();
}
public void addMinefield(Minefield mf) {
addMinefieldHelper(mf);
processGameEvent(new GameBoardChangeEvent(this));
}
public void addMinefields(Vector<Minefield> mines) {
for (int i = 0; i < mines.size(); i++) {
Minefield mf = mines.elementAt(i);
addMinefieldHelper(mf);
}
processGameEvent(new GameBoardChangeEvent(this));
}
public void setMinefields(Vector<Minefield> minefields) {
clearMinefieldsHelper();
for (int i = 0; i < minefields.size(); i++) {
Minefield mf = minefields.elementAt(i);
addMinefieldHelper(mf);
}
processGameEvent(new GameBoardChangeEvent(this));
}
public void resetMinefieldDensity(Vector<Minefield> newMinefields) {
if(newMinefields.size() < 1) {
return;
}
Vector<Minefield> mfs = minefields.get(newMinefields.firstElement().getCoords());
mfs.clear();
for (int i = 0; i < newMinefields.size(); i++) {
Minefield mf = newMinefields.elementAt(i);
addMinefieldHelper(mf);
}
processGameEvent(new GameBoardChangeEvent(this));
}
protected void addMinefieldHelper(Minefield mf) {
Vector<Minefield> mfs = minefields.get(mf.getCoords());
if (mfs == null) {
mfs = new Vector<Minefield>();
mfs.addElement(mf);
minefields.put(mf.getCoords(), mfs);
return;
}
mfs.addElement(mf);
}
public void removeMinefield(Minefield mf) {
removeMinefieldHelper(mf);
processGameEvent(new GameBoardChangeEvent(this));
}
public void removeMinefieldHelper(Minefield mf) {
Vector<Minefield> mfs = minefields.get(mf.getCoords());
if (mfs == null) {
return;
}
Enumeration<Minefield> e = mfs.elements();
while (e.hasMoreElements()) {
Minefield mftemp = e.nextElement();
if (mftemp.equals(mf)) {
mfs.removeElement(mftemp);
break;
}
}
if (mfs.isEmpty()) {
minefields.remove(mf.getCoords());
}
}
public void clearMinefields() {
clearMinefieldsHelper();
processGameEvent(new GameBoardChangeEvent(this));
}
protected void clearMinefieldsHelper() {
minefields.clear();
}
public Vector<Minefield> getVibrabombs() {
return vibrabombs;
}
public void addVibrabomb(Minefield mf) {
vibrabombs.addElement(mf);
}
public void removeVibrabomb(Minefield mf) {
vibrabombs.removeElement(mf);
}
public boolean containsVibrabomb(Minefield mf) {
return vibrabombs.contains(mf);
}
public GameOptions getOptions() {
return options;
}
public void setOptions(GameOptions options) {
if (null == options) {
System.err.println("Can't set the game options to null!");
} else {
this.options = options;
processGameEvent(new GameSettingsChangeEvent(this));
}
}
/**
* Return an enumeration of teams in the game
*/
public Enumeration<Team> getTeams() {
return teams.elements();
}
/**
* Return the current number of teams in the game.
*/
public int getNoOfTeams() {
return teams.size();
}
/**
* This returns a clone of the vector of teams. Each element is one of the
* teams in the game.
*/
@SuppressWarnings("unchecked")
public Vector<Team> getTeamsVector() {
return (Vector<Team>) teams.clone();
}
/**
* Return a players team Note: may return null if player has no team
*/
public Team getTeamForPlayer(Player p) {
for (Enumeration<Team> i = teams.elements(); i.hasMoreElements();) {
final Team team = i.nextElement();
for (Enumeration<Player> j = team.getPlayers(); j.hasMoreElements();) {
final Player player = j.nextElement();
if (p == player) {
return team;
}
}
}
return null;
}
/**
* Set up the teams vector. Each player on a team (Team 1 .. Team X) is
* placed in the appropriate vector. Any player on 'No Team', is placed in
* their own object
*/
public void setupTeams() {
Vector<Team> initTeams = new Vector<Team>();
boolean useTeamInit = getOptions().getOption("team_initiative")
.booleanValue();
// Get all NO_TEAM players. If team_initiative is false, all
// players are on their own teams for initiative purposes.
for (Enumeration<Player> i = getPlayers(); i.hasMoreElements();) {
final Player player = i.nextElement();
if (!useTeamInit || (player.getTeam() == Player.TEAM_NONE)) {
Team new_team = new Team(Player.TEAM_NONE);
new_team.addPlayer(player);
initTeams.addElement(new_team);
}
}
if (useTeamInit) {
// Now, go through all the teams, and add the apropriate player
for (int t = Player.TEAM_NONE + 1; t < Player.MAX_TEAMS; t++) {
Team new_team = null;
for (Enumeration<Player> i = getPlayers(); i.hasMoreElements();) {
final Player player = i.nextElement();
if (player.getTeam() == t) {
if (new_team == null) {
new_team = new Team(t);
}
new_team.addPlayer(player);
}
}
if (new_team != null) {
initTeams.addElement(new_team);
}
}
}
teams = initTeams;
}
/**
* Return an enumeration of player in the game
*/
public Enumeration<Player> getPlayers() {
return players.elements();
}
/**
* Return the players vector
*/
public Vector<Player> getPlayersVector() {
return players;
}
/**
* Return the current number of active players in the game.
*/
public int getNoOfPlayers() {
return players.size();
}
/**
* Returns the individual player assigned the id parameter.
*/
public Player getPlayer(int id) {
if (Player.PLAYER_NONE == id) {
return null;
}
return playerIds.get(new Integer(id));
}
public void addPlayer(int id, Player player) {
player.setGame(this);
players.addElement(player);
playerIds.put(new Integer(id), player);
updatePlayer(player);
}
public void setPlayer(int id, Player player) {
final Player oldPlayer = getPlayer(id);
player.setGame(this);
players.setElementAt(player, players.indexOf(oldPlayer));
playerIds.put(new Integer(id), player);
updatePlayer(player);
}
protected void updatePlayer(Player player) {
processGameEvent(new GamePlayerChangeEvent(this, player));
}
public void removePlayer(int id) {
Player playerToRemove = getPlayer(id);
players.removeElement(playerToRemove);
playerIds.remove(new Integer(id));
processGameEvent(new GamePlayerChangeEvent(this, playerToRemove));
}
/**
* Returns the number of entities owned by the player, regardless of their
* status, as long as they are in the game.
*/
public int getEntitiesOwnedBy(Player player) {
int count = 0;
for (Enumeration<Entity> i = entities.elements(); i.hasMoreElements();) {
Entity entity = i.nextElement();
if (entity.getOwner().equals(player)) {
count++;
}
}
return count;
}
/**
* Returns the number of entities owned by the player, regardless of their
* status.
*/
public int getAllEntitiesOwnedBy(Player player) {
int count = 0;
for (Enumeration<Entity> i = entities.elements(); i.hasMoreElements();) {
Entity entity = i.nextElement();
if (entity.getOwner().equals(player)) {
count++;
}
}
for (Enumeration<Entity> i = vOutOfGame.elements(); i.hasMoreElements();) {
Entity entity = i.nextElement();
if (entity.getOwner().equals(player)) {
count++;
}
}
return count;
}
/**
* Returns the number of non-destroyed entityes owned by the player
*/
public int getLiveEntitiesOwnedBy(Player player) {
int count = 0;
for (Enumeration<Entity> i = entities.elements(); i.hasMoreElements();) {
Entity entity = i.nextElement();
if (entity.getOwner().equals(player) && !entity.isDestroyed()) {
count++;
}
}
return count;
}
/**
* Returns the number of non-destroyed entities owned by the
* player, including entities not yet deployed.
* Ignore offboard units and captured Mek pilots.
*/
public int getLiveDeployedEntitiesOwnedBy(Player player) {
int count = 0;
for (Enumeration<Entity> i = entities.elements(); i.hasMoreElements();) {
Entity entity = i.nextElement();
if (entity.getOwner().equals(player) && !entity.isDestroyed()
&& !entity.isOffBoard()
&& !entity.isCaptured()) {
count++;
}
}
return count;
}
/**
* Returns the number of non-destroyed deployed entities owned by the
* player. Ignore offboard units and captured Mek pilots.
*/
public int getLiveCommandersOwnedBy(Player player) {
int count = 0;
for (Enumeration<Entity> i = entities.elements(); i.hasMoreElements();) {
Entity entity = i.nextElement();
if (entity.getOwner().equals(player) && !entity.isDestroyed()
&& entity.isCommander() && !entity.isOffBoard()
&& !entity.isCaptured()) {
count++;
}
}
return count;
}
/**
* Returns true if the player has a valid unit with the Tactical Genius
* pilot special ability.
*/
public boolean hasTacticalGenius(Player player) {
for (Enumeration<Entity> i = entities.elements(); i.hasMoreElements();) {
Entity entity = i.nextElement();
if (entity.getCrew().getOptions().booleanOption("tactical_genius")
&& entity.getOwner().equals(player)
&& !entity.isDestroyed() && entity.isDeployed()
&& !entity.getCrew().isUnconscious()) {
return true;
}
}
return false;
}
/**
* Get a vector of entity objects that are "acceptable" to attack with this
* entity
*/
public Vector<Entity> getValidTargets(Entity entity) {
Vector<Entity> ents = new Vector<Entity>();
boolean friendlyFire = getOptions().booleanOption("friendly_fire");
for (Enumeration<Entity> i = entities.elements(); i.hasMoreElements();) {
Entity otherEntity = i.nextElement();
// Even if friendly fire is acceptable, do not shoot yourself
// Enemy units not on the board can not be shot.
if ((otherEntity.getPosition() != null)
&& !otherEntity.isOffBoard()
&& (entity.isEnemyOf(otherEntity) || (friendlyFire && (entity
.getId() != otherEntity.getId())))) {
ents.addElement(otherEntity);
}
}
return ents;
}
/**
* Returns true if this phase has turns. If false, the phase is simply
* waiting for everybody to declare "done".
*/
public boolean phaseHasTurns(IGame.Phase thisPhase) {
switch (thisPhase) {
case PHASE_SET_ARTYAUTOHITHEXES:
case PHASE_DEPLOY_MINEFIELDS:
case PHASE_DEPLOYMENT:
case PHASE_MOVEMENT:
case PHASE_FIRING:
case PHASE_PHYSICAL:
case PHASE_TARGETING:
case PHASE_OFFBOARD:
return true;
default:
return false;
}
}
public boolean isPhaseSimultaneous() {
switch (phase) {
case PHASE_DEPLOYMENT:
case PHASE_MOVEMENT:
return false;
case PHASE_FIRING:
return getOptions().booleanOption("simultaneous_firing");
case PHASE_PHYSICAL:
return getOptions().booleanOption("simultaneous_physical");
case PHASE_TARGETING:
return getOptions().booleanOption("simultaneous_targeting");
default:
return false;
}
}
/**
* Returns the current GameTurn object
*/
public GameTurn getTurn() {
if ((turnIndex < 0) || (turnIndex >= turnVector.size())) {
return null;
}
return turnVector.elementAt(turnIndex);
}
public GameTurn getTurnForPlayer(int pn) {
for(int i=turnIndex;i<turnVector.size();i++) {
GameTurn gt = turnVector.get(i);
if(gt.isValid(pn, this)) {
return gt;
}
}
return null;
}
/** Changes to the next turn, returning it. */
public GameTurn changeToNextTurn() {
turnIndex++;
return getTurn();
}
/** Resets the turn index to -1 (awaiting first turn) */
public void resetTurnIndex() {
turnIndex = -1;
}
/** Returns true if there is a turn after the current one */
public boolean hasMoreTurns() {
return turnVector.size() > (turnIndex + 1);
}
/** Inserts a turn that will come directly after the current one */
public void insertNextTurn(GameTurn turn) {
turnVector.insertElementAt(turn, turnIndex + 1);
}
/** Returns an Enumeration of the current turn list */
public Enumeration<GameTurn> getTurns() {
return turnVector.elements();
}
/** Returns the current turn index */
public int getTurnIndex() {
return turnIndex;
}
/** Sets the current turn index */
public void setTurnIndex(int turnIndex) {
//FIXME: occasionally getTurn() returns null. Handle that case intelligently.
this.turnIndex = turnIndex;
processGameEvent(new GameTurnChangeEvent(this, getPlayer(getTurn()
.getPlayerNum())));
}
/** Returns the current turn vector */
public Vector<GameTurn> getTurnVector() {
return turnVector;
}
/** Sets the current turn vector */
public void setTurnVector(Vector<GameTurn> turnVector) {
this.turnVector = turnVector;
}
public Phase getPhase() {
return phase;
}
public void setPhase(Phase phase) {
final Phase oldPhase = this.phase;
this.phase = phase;
// Handle phase-specific items.
switch (phase) {
case PHASE_LOUNGE:
reset();
break;
case PHASE_MOVEMENT:
resetActions();
break;
case PHASE_FIRING:
resetActions();
break;
case PHASE_PHYSICAL:
resetActions();
break;
case PHASE_INITIATIVE:
resetActions();
resetCharges();
resetRams();
break;
// TODO Is there better solution to handle charges?
case PHASE_PHYSICAL_REPORT:
case PHASE_END:
resetCharges();
resetRams();
break;
}
processGameEvent(new GamePhaseChangeEvent(this, oldPhase, phase));
}
public Phase getLastPhase() {
return lastPhase;
}
public void setLastPhase(Phase lastPhase) {
this.lastPhase = lastPhase;
}
public void setDeploymentComplete(boolean deploymentComplete) {
this.deploymentComplete = deploymentComplete;
}
public boolean isDeploymentComplete() {
return deploymentComplete;
}
/**
* Sets up up the hashtable of who deploys when
*/
public void setupRoundDeployment() {
deploymentTable = new Hashtable<Integer, Vector<Entity>>();
for (int i = 0; i < entities.size(); i++) {
Entity ent = entities.elementAt(i);
if (ent.isDeployed()) {
continue;
}
Vector<Entity> roundVec = deploymentTable.get(new Integer(ent
.getDeployRound()));
if (null == roundVec) {
roundVec = new Vector<Entity>();
deploymentTable
.put(new Integer(ent.getDeployRound()), roundVec);
}
roundVec.addElement(ent);
lastDeploymentRound = Math.max(lastDeploymentRound, ent
.getDeployRound());
}
}
/**
* Checks to see if we've past our deployment completion
*/
public void checkForCompleteDeployment() {
setDeploymentComplete(lastDeploymentRound < getRoundCount());
}
/**
* Check to see if we should deploy this round
*/
public boolean shouldDeployThisRound() {
return shouldDeployForRound(getRoundCount());
}
public boolean shouldDeployForRound(int round) {
Vector<Entity> vec = getEntitiesToDeployForRound(round);
return (((null == vec) || (vec.size() == 0)) ? false : true);
}
private Vector<Entity> getEntitiesToDeployForRound(int round) {
return deploymentTable.get(new Integer(round));
}
/**
* Clear this round from this list of entities to deploy
*/
public void clearDeploymentThisRound() {
deploymentTable.remove(new Integer(getRoundCount()));
}
/**
* Returns a vector of entities that have not yet deployed
*/
public Vector<Entity> getUndeployedEntities() {
Vector<Entity> entList = new Vector<Entity>();
Enumeration<Vector<Entity>> iter = deploymentTable.elements();
while (iter.hasMoreElements()) {
Vector<Entity> vecTemp = iter.nextElement();
for (int i = 0; i < vecTemp.size(); i++) {
entList.addElement(vecTemp.elementAt(i));
}
}
return entList;
}
/**
* Returns an enumeration of all the entites in the game.
*/
public Enumeration<Entity> getEntities() {
return entities.elements();
}
public Entity getPreviousEntityFromList(Entity current) {
if ((current != null) && (entities != null) && entities.contains(current)) {
int prev = entities.indexOf(current) - 1;
if (prev < 0) {
prev = entities.size() - 1; // wrap around to end
}
return entities.elementAt(prev);
}
return null;
}
public Entity getNextEntityFromList(Entity current) {
if ((current != null) && (entities != null) && entities.contains(current)) {
int next = entities.indexOf(current) + 1;
if (next >= entities.size()) {
next = 0; // wrap-around to begining
}
return entities.elementAt(next);
}
return null;
}
/**
* Returns the actual vector for the entities
*/
public Vector<Entity> getEntitiesVector() {
return entities;
}
public void setEntitiesVector(Vector<Entity> entities) {
this.entities = entities;
reindexEntities();
processGameEvent(new GameEntityNewEvent(this, entities));
}
/**
* Returns the actual vector for the out-of-game entities
*/
public Vector<Entity> getOutOfGameEntitiesVector() {
return vOutOfGame;
}
/**
* Swap out the current list of dead (or fled) units for a new one.
*
* @param vOutOfGame - the new <code>Vector</code> of dead or fled units.
* This value should <em>not</em> be <code>null</code>.
* @throws IllegalArgumentException if the new list is <code>null</code>.
*/
public void setOutOfGameEntitiesVector(Vector<Entity> vOutOfGame) {
megamek.debug.Assert.assertTrue(vOutOfGame != null,
"New out-of-game list should not be null.");
if (vOutOfGame != null) {
Vector<Entity> newOutOfGame = new Vector<Entity>();
// Add entities for the existing players to the game.
for (Enumeration<Entity> i = vOutOfGame.elements(); i
.hasMoreElements();) {
final Entity entity = i.nextElement();
int ownerId = entity.getOwnerId();
if ((ownerId != Entity.NONE) && (getPlayer(ownerId) != null)) {
entity.setGame(this);
newOutOfGame.addElement(entity);
}
}
this.vOutOfGame = newOutOfGame;
processGameEvent(new GameEntityNewOffboardEvent(this));
}
}
/**
* Returns an out-of-game entity.
*
* @param id the <code>int</code> ID of the out-of-game entity.
* @return the out-of-game <code>Entity</code> with that ID. If no
* out-of-game entity has that ID, returns a <code>null</code>.
*/
public Entity getOutOfGameEntity(int id) {
Entity match = null;
Enumeration<Entity> iter = vOutOfGame.elements();
while ((null == match) && iter.hasMoreElements()) {
Entity entity = iter.nextElement();
if (id == entity.getId()) {
match = entity;
}
}
return match;
}
/**
* Returns a <code>Vector</code> containing the <code>Entity</code>s
* that are in the same C3 network as the passed-in unit. The output will
* contain the passed-in unit, if the unit has a C3 computer. If the unit
* has no C3 computer, the output will be empty (but it will never be
* <code>null</code>).
*
* @param entity - the <code>Entity</code> whose C3 network co- members is
* required. This value may be <code>null</code>.
* @return a <code>Vector</code> that will contain all other
* <code>Entity</code>s that are in the same C3 network as the
* passed-in unit. This <code>Vector</code> may be empty, but it
* will not be <code>null</code>.
* @see #getC3SubNetworkMembers( Entity )
*/
public Vector<Entity> getC3NetworkMembers(Entity entity) {
Vector<Entity> members = new Vector<Entity>();
// Does the unit have a C3 computer?
if ((entity != null) && (entity.hasC3() || entity.hasC3i())) {
// Walk throught the entities in the game, and add all
// members of the C3 network to the output Vector.
Enumeration<Entity> units = entities.elements();
while (units.hasMoreElements()) {
Entity unit = units.nextElement();
if (entity.equals(unit) || entity.onSameC3NetworkAs(unit)) {
members.addElement(unit);
}
}
} // End entity-has-C3
return members;
}
/**
* Returns a <code>Vector</code> containing the <code>Entity</code>s
* that are in the C3 sub-network under the passed-in unit. The output will
* contain the passed-in unit, if the unit has a C3 computer. If the unit
* has no C3 computer, the output will be empty (but it will never be
* <code>null</code>). If the passed-in unit is a company commander or a
* member of a C3i network, this call is the same as
* <code>getC3NetworkMembers</code>.
*
* @param entity - the <code>Entity</code> whose C3 network sub- members
* is required. This value may be <code>null</code>.
* @return a <code>Vector</code> that will contain all other
* <code>Entity</code>s that are in the same C3 network under the
* passed-in unit. This <code>Vector</code> may be empty, but it
* will not be <code>null</code>.
* @see #getC3NetworkMembers( Entity )
*/
public Vector<Entity> getC3SubNetworkMembers(Entity entity) {
// Handle null, C3i, and company commander units.
if ((entity == null) || entity.hasC3i() || entity.C3MasterIs(entity)) {
return getC3NetworkMembers(entity);
}
Vector<Entity> members = new Vector<Entity>();
// Does the unit have a C3 computer?
if (entity.hasC3()) {
// Walk throught the entities in the game, and add all
// sub-members of the C3 network to the output Vector.
Enumeration<Entity> units = entities.elements();
while (units.hasMoreElements()) {
Entity unit = units.nextElement();
if (entity.equals(unit) || unit.C3MasterIs(entity)) {
members.addElement(unit);
}
}
} // End entity-has-C3
return members;
}
/**
* Returns a <code>Hashtable</code> that maps the <code>Coords</code> of
* each unit in this <code>Game</code> to a <code>Vector</code> of
* <code>Entity</code>s at that positions. Units that have no position
* (e.g. loaded units) will not be in the map.
*
* @return a <code>Hashtable</code> that maps the <code>Coords</code>
* positions or each unit in the game to a <code>Vector</code> of
* <code>Entity</code>s at that position.
*/
public Hashtable<Coords, Vector<Entity>> getPositionMap() {
Hashtable<Coords, Vector<Entity>> positionMap = new Hashtable<Coords, Vector<Entity>>();
Vector<Entity> atPos = null;
// Walk through the entities in this game.
for (Enumeration<Entity> i = entities.elements(); i.hasMoreElements();) {
final Entity entity = i.nextElement();
// Get the vector for this entity's position.
final Coords coords = entity.getPosition();
if (coords != null) {
atPos = positionMap.get(coords);
// If this is the first entity at this position,
// create the vector and add it to the map.
if (atPos == null) {
atPos = new Vector<Entity>();
positionMap.put(coords, atPos);
}
// Add the entity to the vector for this position.
atPos.addElement(entity);
}
} // Handle the next entity.
// Return the map.
return positionMap;
}
/**
* Returns an enumeration of salvagable entities.
*/
public Enumeration<Entity> getGraveyardEntities() {
Vector<Entity> graveyard = new Vector<Entity>();
for (Enumeration<Entity> i = vOutOfGame.elements(); i.hasMoreElements();) {
Entity entity = i.nextElement();
if ((entity.getRemovalCondition() == IEntityRemovalConditions.REMOVE_SALVAGEABLE)
|| (entity.getRemovalCondition() == IEntityRemovalConditions.REMOVE_EJECTED)) {
graveyard.addElement(entity);
}
}
return graveyard.elements();
}
/**
* Returns an enumeration of wrecked entities.
*/
public Enumeration<Entity> getWreckedEntities() {
Vector<Entity> wrecks = new Vector<Entity>();
for (Enumeration<Entity> i = vOutOfGame.elements(); i.hasMoreElements();) {
Entity entity = i.nextElement();
if ((entity.getRemovalCondition() == IEntityRemovalConditions.REMOVE_SALVAGEABLE)
|| (entity.getRemovalCondition() == IEntityRemovalConditions.REMOVE_EJECTED)
|| (entity.getRemovalCondition() == IEntityRemovalConditions.REMOVE_DEVASTATED)) {
wrecks.addElement(entity);
}
}
return wrecks.elements();
}
/**
* Returns an enumeration of entities that have retreated
*/
public Enumeration<Entity> getRetreatedEntities() {
Vector<Entity> sanctuary = new Vector<Entity>();
for (Enumeration<Entity> i = vOutOfGame.elements(); i.hasMoreElements();) {
Entity entity = i.nextElement();
if ((entity.getRemovalCondition() == IEntityRemovalConditions.REMOVE_IN_RETREAT)
|| (entity.getRemovalCondition() == IEntityRemovalConditions.REMOVE_CAPTURED)
|| (entity.getRemovalCondition() == IEntityRemovalConditions.REMOVE_PUSHED)) {
sanctuary.addElement(entity);
}
}
return sanctuary.elements();
}
/**
* Returns an enumeration of entities that were utterly destroyed
*/
public Enumeration<Entity> getDevastatedEntities() {
Vector<Entity> smithereens = new Vector<Entity>();
for (Enumeration<Entity> i = vOutOfGame.elements(); i.hasMoreElements();) {
Entity entity = i.nextElement();
if (entity.getRemovalCondition() == IEntityRemovalConditions.REMOVE_DEVASTATED) {
smithereens.addElement(entity);
}
}
return smithereens.elements();
}
/**
* Return the current number of entities in the game.
*/
public int getNoOfEntities() {
return entities.size();
}
/**
* Returns the appropriate target for this game given a type and id
*/
public Targetable getTarget(int nType, int nID) {
try {
switch (nType) {
case Targetable.TYPE_ENTITY:
return getEntity(nID);
case Targetable.TYPE_HEX_CLEAR:
case Targetable.TYPE_HEX_IGNITE:
case Targetable.TYPE_HEX_BOMB:
case Targetable.TYPE_MINEFIELD_DELIVER:
case Targetable.TYPE_FLARE_DELIVER:
case Targetable.TYPE_HEX_EXTINGUISH:
case Targetable.TYPE_HEX_ARTILLERY:
case Targetable.TYPE_HEX_SCREEN:
return new HexTarget(HexTarget.idToCoords(nID), board,
nType);
case Targetable.TYPE_FUEL_TANK:
case Targetable.TYPE_FUEL_TANK_IGNITE:
case Targetable.TYPE_BUILDING:
case Targetable.TYPE_BLDG_IGNITE:
return new BuildingTarget(BuildingTarget.idToCoords(nID),
board, nType);
case Targetable.TYPE_MINEFIELD_CLEAR:
return new MinefieldTarget(MinefieldTarget.idToCoords(nID),
board);
case Targetable.TYPE_INARC_POD:
return INarcPod.idToInstance(nID);
default:
return null;
}
} catch (IllegalArgumentException t) {
return null;
}
}
/**
* Returns the entity with the given id number, if any.
*/
public Entity getEntity(int id) {
return entityIds.get(new Integer(id));
}
public void addEntity(int id, Entity entity) {
entity.setGame(this);
entity.setGameOptions();
entities.addElement(entity);
entityIds.put(new Integer(id), entity);
if (id > lastEntityId) {
lastEntityId = id;
}
megamek.debug.Assert.assertTrue(entities.size() == entityIds.size());
processGameEvent(new GameEntityNewEvent(this, entity));
}
public void setEntity(int id, Entity entity) {
setEntity(id, entity, null);
megamek.debug.Assert.assertTrue(entities.size() == entityIds.size());
}
public void setEntity(int id, Entity entity, Vector<UnitLocation> movePath) {
final Entity oldEntity = getEntity(id);
if (oldEntity == null) {
addEntity(id, entity);
} else {
entity.setGame(this);
entities.setElementAt(entity, entities.indexOf(oldEntity));
entityIds.put(new Integer(id), entity);
// Not sure if this really required
if (id > lastEntityId) {
lastEntityId = id;
}
processGameEvent(new GameEntityChangeEvent(this, entity, movePath));
}
megamek.debug.Assert.assertTrue(entities.size() == entityIds.size());
}
/**
* @return int containing an unused entity id
*/
public int getNextEntityId() {
return lastEntityId + 1;
}
/**
* Returns true if an entity with the specified id number exists in this
* game.
*/
public boolean hasEntity(int entityId) {
return entityIds.containsKey(new Integer(entityId));
}
/**
* Remove an entity from the master list. If we can't find that entity,
* (probably due to double-blind) ignore it.
*/
public void removeEntity(int id, int condition) {
Entity toRemove = getEntity(id);
if (toRemove == null) {
// This next statement has been cluttering up double-blind
// logs for quite a while now. I'm assuming it's no longer
// useful.
// System.err.println("Game#removeEntity: could not find entity to
// remove");
return;
}
entities.removeElement(toRemove);
entityIds.remove(new Integer(id));
toRemove.setRemovalCondition(condition);
// do not keep never-joined entities
if ((vOutOfGame != null)
&& (condition != IEntityRemovalConditions.REMOVE_NEVER_JOINED)) {
vOutOfGame.addElement(toRemove);
}
// We also need to remove it from the list of things to be deployed...
// we might still be in this list if we never joined the game
if (deploymentTable.size() > 0) {
Enumeration<Vector<Entity>> iter = deploymentTable.elements();
while (iter.hasMoreElements()) {
Vector<Entity> vec = iter.nextElement();
for (int i = vec.size() - 1; i >= 0; i--) {
Entity en = vec.elementAt(i);
if (en.getId() == id) {
vec.removeElementAt(i);
}
}
}
}
processGameEvent(new GameEntityRemoveEvent(this, toRemove));
}
/**
* Resets this game.
*/
public void reset() {
roundCount = 0;
entities.removeAllElements();
entityIds.clear();
vOutOfGame.removeAllElements();
resetActions();
resetCharges();
resetRams();
resetPSRs();
resetArtilleryAttacks();
resetAttacks();
removeMinefields();
removeArtyAutoHitHexes();
flares.removeAllElements();
clearAllReports();
smokeCloudList.clear();
lastEntityId = 0;
forceVictory = false;
victoryPlayerId = Player.PLAYER_NONE;
victoryTeam = Player.TEAM_NONE;
}
private void removeArtyAutoHitHexes() {
Enumeration<Player> iter = getPlayers();
while (iter.hasMoreElements()) {
Player player = iter.nextElement();
player.removeArtyAutoHitHexes();
}
}
private void removeMinefields() {
minefields.clear();
vibrabombs.removeAllElements();
Enumeration<Player> iter = getPlayers();
while (iter.hasMoreElements()) {
Player player = iter.nextElement();
player.removeMinefields();
}
}
/**
* Regenerates the entities by id hashtable by going thru all entities in
* the Vector
*/
private void reindexEntities() {
entityIds.clear();
lastEntityId = 0;
if (entities != null) {
// Add these entities to the game.
for (Enumeration<Entity> i = entities.elements(); i
.hasMoreElements();) {
final Entity entity = i.nextElement();
final int id = entity.getId();
entityIds.put(new Integer(id), entity);
entity.setGame(this);
if (id > lastEntityId) {
lastEntityId = id;
}
}
}
}
/**
* Returns the first entity at the given coordinate, if any. Only returns
* targetable (non-dead) entities.
*
* @param c the coordinates to search at
*/
public Entity getFirstEntity(Coords c) {
for (Enumeration<Entity> i = entities.elements(); i.hasMoreElements();) {
final Entity entity = i.nextElement();
if (c.equals(entity.getPosition()) && entity.isTargetable()) {
return entity;
}
}
return null;
}
/**
* Returns the first enemy entity at the given coordinate, if any. Only
* returns targetable (non-dead) entities.
*
* @param c the coordinates to search at
* @param currentEntity the entity that is firing
*/
public Entity getFirstEnemyEntity(Coords c, Entity currentEntity) {
for (Enumeration<Entity> i = entities.elements(); i.hasMoreElements();) {
final Entity entity = i.nextElement();
if (c.equals(entity.getPosition()) && entity.isTargetable()
&& entity.isEnemyOf(currentEntity)) {
return entity;
}
}
return null;
}
/**
* Returns an Enumeration of the active entities at the given coordinates.
*/
public Enumeration<Entity> getEntities(Coords c) {
Vector<Entity> vector = new Vector<Entity>();
// Only build the list if the coords are on the board.
if (board.contains(c)) {
for (Enumeration<Entity> i = entities.elements(); i
.hasMoreElements();) {
final Entity entity = i.nextElement();
if (c.equals(entity.getPosition()) && entity.isTargetable()) {
vector.addElement(entity);
}
}
}
return vector.elements();
}
/**
* Return a Vector of Entites at Coords <code>c</code>
* @param c
* @return <code>Vector<Entity></code>
*/
public Vector<Entity> getEntitiesVector(Coords c) {
Vector<Entity> vector = new Vector<Entity>();
// Only build the list if the coords are on the board.
if (board.contains(c)) {
for (Enumeration<Entity> i = entities.elements(); i
.hasMoreElements();) {
final Entity entity = i.nextElement();
if (c.equals(entity.getPosition()) && entity.isTargetable()) {
vector.addElement(entity);
}
}
}
return vector;
}
/**
* Returns a Target for an Accidental Fall From above, or null if no
* possible target is there
*
* @param c The <code>Coords</code> of the hex in which the accidental
* fall from above happens
* @param ignore The entity who is falling
* @return The <code>Entity</code> that should be an AFFA target.
*/
public Entity getAffaTarget(Coords c, Entity ignore) {
Vector<Entity> vector = new Vector<Entity>();
if (board.contains(c)) {
for (Enumeration<Entity> i = entities.elements(); i
.hasMoreElements();) {
final Entity entity = i.nextElement();
if (c.equals(entity.getPosition()) && entity.isTargetable()
&& !(entity instanceof Infantry) && (entity != ignore)) {
vector.addElement(entity);
}
}
}
if (!vector.isEmpty()) {
int count = vector.size();
int random = Compute.randomInt(count);
return vector.elementAt(random);
}
return null;
}
/**
* Returns an <code>Enumeration</code> of the enemy's active entities at
* the given coordinates.
*
* @param c the <code>Coords</code> of the hex being examined.
* @param currentEntity the <code>Entity</code> whose enemies are needed.
* @return an <code>Enumeration</code> of <code>Entity</code>s at the
* given coordinates who are enemies of the given unit.
*/
public Enumeration<Entity> getEnemyEntities(final Coords c,
final Entity currentEntity) {
// Use an EntitySelector to avoid walking the entities vector twice.
return getSelectedEntities(new EntitySelector() {
private Coords coords = c;
private Entity friendly = currentEntity;
public boolean accept(Entity entity) {
if (coords.equals(entity.getPosition())
&& entity.isTargetable() && entity.isEnemyOf(friendly)) {
return true;
}
return false;
}
});
}
/**
* Returns an <code>Enumeration</code> of friendly active entities at the
* given coordinates.
*
* @param c the <code>Coords</code> of the hex being examined.
* @param currentEntity the <code>Entity</code> whose friends are needed.
* @return an <code>Enumeration</code> of <code>Entity</code>s at the
* given coordinates who are friends of the given unit.
*/
public Enumeration<Entity> getFriendlyEntities(final Coords c,
final Entity currentEntity) {
// Use an EntitySelector to avoid walking the entities vector twice.
return getSelectedEntities(new EntitySelector() {
private Coords coords = c;
private Entity friendly = currentEntity;
public boolean accept(Entity entity) {
if (coords.equals(entity.getPosition())
&& entity.isTargetable() && !entity.isEnemyOf(friendly)) {
return true;
}
return false;
}
});
}
/**
* Moves an entity into the graveyard so it stops getting sent out every
* phase.
*/
public void moveToGraveyard(int id) {
removeEntity(id, IEntityRemovalConditions.REMOVE_SALVAGEABLE);
}
/**
* See if the <code>Entity</code> with the given ID is out of the game.
*
* @param id - the ID of the <code>Entity</code> to be checked.
* @return <code>true</code> if the <code>Entity</code> is in the
* graveyard, <code>false</code> otherwise.
*/
public boolean isOutOfGame(int id) {
for (Enumeration<Entity> i = vOutOfGame.elements(); i.hasMoreElements();) {
Entity entity = i.nextElement();
if (entity.getId() == id) {
return true;
}
}
return false;
}
/**
* See if the <code>Entity</code> is out of the game.
*
* @param entity - the <code>Entity</code> to be checked.
* @return <code>true</code> if the <code>Entity</code> is in the
* graveyard, <code>false</code> otherwise.
*/
public boolean isOutOfGame(Entity entity) {
return isOutOfGame(entity.getId());
}
/**
* Returns the first entity that can act in the present turn, or null if
* none can.
*/
public Entity getFirstEntity() {
return getFirstEntity(getTurn());
}
/**
* Returns the first entity that can act in the specified turn, or null if
* none can.33
*/
public Entity getFirstEntity(GameTurn turn) {
return getEntity(getFirstEntityNum(turn));
}
/**
* Returns the id of the first entity that can act in the current turn, or
* -1 if none can.
*/
public int getFirstEntityNum() {
return getFirstEntityNum(getTurn());
}
/**
* Returns the id of the first entity that can act in the specified turn, or
* -1 if none can.
*/
public int getFirstEntityNum(GameTurn turn) {
if (turn == null) {
return -1;
}
for (Enumeration<Entity> i = entities.elements(); i.hasMoreElements();) {
final Entity entity = i.nextElement();
if (turn.isValidEntity(entity, this)) {
return entity.getId();
}
}
return -1;
}
/**
* Returns the next selectable entity that can act this turn, or null if
* none can.
*
* @param start the index number to start at
*/
public Entity getNextEntity(int start) {
return getEntity(getNextEntityNum(getTurn(), start));
}
public int getNextEntityNum(int start) {
return getNextEntityNum(getTurn(), start);
}
/**
* Returns the entity id of the next entity that can move during the
* specified
*
* @param turn the turn to use
* @param start the entity id to start at
*/
public int getNextEntityNum(GameTurn turn, int start) {
boolean startPassed = false;
for (Enumeration<Entity> i = entities.elements(); i.hasMoreElements();) {
final Entity entity = i.nextElement();
if (entity.getId() == start) {
startPassed = true;
} else if (startPassed && turn.isValidEntity(entity, this)) {
return entity.getId();
}
}
return getFirstEntityNum(turn);
}
/**
* Returns the number of the first deployable entity
*/
public int getFirstDeployableEntityNum() {
return getFirstDeployableEntityNum(getTurn());
}
public int getFirstDeployableEntityNum(GameTurn turn) {
// Repeat the logic from getFirstEntityNum.
if (turn == null) {
return -1;
}
for (Enumeration<Entity> i = entities.elements(); i.hasMoreElements();) {
final Entity entity = i.nextElement();
if (turn.isValidEntity(entity, this)
&& entity.shouldDeploy(getRoundCount())) {
return entity.getId();
}
}
return -1;
}
/**
* Returns the number of the next deployable entity
*/
public int getNextDeployableEntityNum(int entityId) {
return getNextDeployableEntityNum(getTurn(), entityId);
}
public int getNextDeployableEntityNum(GameTurn turn, int start) {
// Repeat the logic from getNextEntityNum.
boolean startPassed = false;
for (Enumeration<Entity> i = entities.elements(); i.hasMoreElements();) {
final Entity entity = i.nextElement();
if (entity.getId() == start) {
startPassed = true;
} else if (startPassed && turn.isValidEntity(entity, this)
&& entity.shouldDeploy(getRoundCount())) {
return entity.getId();
}
}
return getFirstDeployableEntityNum(turn);
}
/**
* Get the entities for the player.
*
* @param player - the <code>Player</code> whose entities are required.
* @param hide - should fighters loaded into squadrons be excluded?
* @return a <code>Vector</code> of <code>Entity</code>s.
*/
public ArrayList<Entity> getPlayerEntities(Player player, boolean hide) {
ArrayList<Entity> output = new ArrayList<Entity>();
for (Enumeration<Entity> i = entities.elements(); i.hasMoreElements();) {
final Entity entity = i.nextElement();
if(entity.isPartOfFighterSquadron() && hide) {
continue;
}
if (player.equals(entity.getOwner())) {
output.add(entity);
}
}
return output;
}
/**
* Determines if the indicated entity is stranded on a transport that can't
* move. <p/> According to <a
* href="http://www.classicbattletech.com/w3t/showflat.php?Cat=&Board=ask&Number=555466&page=2&view=collapsed&sb=5&o=0&fpart=">
* Randall Bills</a>, the "minimum move" rule allow stranded units to
* dismount at the start of the turn.
*
* @param entity the <code>Entity</code> that may be stranded
* @return <code>true</code> if the entity is stranded <code>false</code>
* otherwise.
*/
public boolean isEntityStranded(Entity entity) {
// Is the entity being transported?
final int transportId = entity.getTransportId();
Entity transport = getEntity(transportId);
if ((Entity.NONE != transportId) && (null != transport)) {
// Can that transport unload the unit?
if (transport.isImmobile() || (0 == transport.getWalkMP())) {
return true;
}
}
return false;
}
/**
* Returns the number of remaining selectable infantry owned by a player.
*/
public int getInfantryLeft(int playerId) {
Player player = getPlayer(playerId);
int remaining = 0;
for (Enumeration<Entity> i = entities.elements(); i.hasMoreElements();) {
final Entity entity = i.nextElement();
if (player.equals(entity.getOwner())
&& entity.isSelectableThisTurn()
&& (entity instanceof Infantry)) {
remaining++;
}
}
return remaining;
}
/**
* Returns the number of remaining selectable Protomechs owned by a player.
*/
public int getProtomechsLeft(int playerId) {
Player player = getPlayer(playerId);
int remaining = 0;
for (Enumeration<Entity> i = entities.elements(); i.hasMoreElements();) {
final Entity entity = i.nextElement();
if (player.equals(entity.getOwner())
&& entity.isSelectableThisTurn()
&& (entity instanceof Protomech)) {
remaining++;
}
}
return remaining;
}
/**
* Returns the number of Vehicles that <code>playerId</code>
* has not moved yet this turn.
* @param playerId
* @return number of vehicles <code>playerId</code> has not moved yet this turn
*/
public int getVehiclesLeft(int playerId) {
Player player = getPlayer(playerId);
int remaining = 0;
for (Enumeration<Entity> i = entities.elements(); i.hasMoreElements();) {
final Entity entity = i.nextElement();
if (player.equals(entity.getOwner())
&& entity.isSelectableThisTurn()
&& (entity instanceof Tank)) {
remaining++;
}
}
return remaining;
}
/**
* Removes the first turn found that the specified entity can move in.
* Used when a turn is played out of order
*/
public GameTurn removeFirstTurnFor(Entity entity) {
assert(phase != Phase.PHASE_MOVEMENT); //special move multi cases ignored
for (int i = turnIndex; i < turnVector.size(); i++) {
GameTurn turn = turnVector.elementAt(i);
if (turn.isValidEntity(entity, this)) {
turnVector.removeElementAt(i);
return turn;
}
}
return null;
}
/**
* Removes the last, next turn found that the specified entity can move in.
* Used when, say, an entity dies mid-phase.
*/
public void removeTurnFor(Entity entity) {
// If the game option "move multiple infantry per mech" is selected,
// then we might not need to remove a turn at all.
// A turn only needs to be removed when going from 4 inf (2 turns) to
// 3 inf (1 turn)
if (getOptions().booleanOption("inf_move_multi")
&& (entity instanceof Infantry) && (phase == Phase.PHASE_MOVEMENT)) {
if ((getInfantryLeft(entity.getOwnerId()) % getOptions().intOption(
"inf_proto_move_multi")) != 1) {
// exception, if the _next_ turn is an infantry turn, remove
// that
// contrived, but may come up e.g. one inf accidently kills
// another
if (hasMoreTurns()) {
GameTurn nextTurn = turnVector.elementAt(turnIndex + 1);
if (nextTurn instanceof GameTurn.EntityClassTurn) {
GameTurn.EntityClassTurn ect = (GameTurn.EntityClassTurn) nextTurn;
if (ect.isValidClass(GameTurn.CLASS_INFANTRY)
&& !ect.isValidClass(~GameTurn.CLASS_INFANTRY)) {
turnVector.removeElementAt(turnIndex + 1);
}
}
}
return;
}
}
// Same thing but for protos
if (getOptions().booleanOption("protos_move_multi")
&& (entity instanceof Protomech) && (phase == Phase.PHASE_MOVEMENT)) {
if ((getProtomechsLeft(entity.getOwnerId()) % getOptions()
.intOption("inf_proto_move_multi")) != 1) {
// exception, if the _next_ turn is an protomek turn, remove
// that
// contrived, but may come up e.g. one inf accidently kills
// another
if (hasMoreTurns()) {
GameTurn nextTurn = turnVector.elementAt(turnIndex + 1);
if (nextTurn instanceof GameTurn.EntityClassTurn) {
GameTurn.EntityClassTurn ect = (GameTurn.EntityClassTurn) nextTurn;
if (ect.isValidClass(GameTurn.CLASS_PROTOMECH)
&& !ect.isValidClass(~GameTurn.CLASS_PROTOMECH)) {
turnVector.removeElementAt(turnIndex + 1);
}
}
}
return;
}
}
// Same thing but for vehicles
if (getOptions().booleanOption("vehicle_lance_movement")
&& (entity instanceof Tank) && (phase == Phase.PHASE_MOVEMENT)) {
if ((getProtomechsLeft(entity.getOwnerId()) % getOptions()
.intOption("vehicle_lance_movement_number")) != 1) {
// exception, if the _next_ turn is an tank turn, remove
// that
// contrived, but may come up e.g. one tank accidently kills
// another
if (hasMoreTurns()) {
GameTurn nextTurn = turnVector.elementAt(turnIndex + 1);
if (nextTurn instanceof GameTurn.EntityClassTurn) {
GameTurn.EntityClassTurn ect = (GameTurn.EntityClassTurn) nextTurn;
if (ect.isValidClass(GameTurn.CLASS_TANK)
&& !ect.isValidClass(~GameTurn.CLASS_TANK)) {
turnVector.removeElementAt(turnIndex + 1);
}
}
}
return;
}
}
for (int i = turnVector.size() - 1; i >= turnIndex; i--) {
GameTurn turn = turnVector.elementAt(i);
if (turn.isValidEntity(entity, this)) {
turnVector.removeElementAt(i);
break;
}
}
}
/**
* Check each player for the presence of a Battle Armor squad equipped with
* a Magnetic Clamp. If one unit is found, update that player's units to
* allow the squad to be transported. <p/> This method should be called
* </b>*ONCE*</b> per game, after all units for all players have been
* loaded.
*
* @return <code>true</code> if a unit was updated, <code>false</code>
* if no player has a Battle Armor squad equipped with a Magnetic
* Clamp.
*/
public boolean checkForMagneticClamp() {
// Declare local variables.
Player player = null;
Entity unit = null;
boolean result;
Hashtable<Player, Boolean> playerFlags = null;
String name = null;
// Assume that we don't need new transporters.
result = false;
// Create a map of flags for the players.
playerFlags = new Hashtable<Player, Boolean>(getNoOfPlayers());
// Walk through the game's entities.
for (Enumeration<Entity> i = entities.elements(); i.hasMoreElements();) {
// Is the next unit a Battle Armor squad?
unit = i.nextElement();
if (unit instanceof BattleArmor) {
// Does the unit have a Magnetic Clamp?
for (Mounted equip : unit.getMisc()) {
name = equip.getType().getInternalName();
if (BattleArmor.MAGNETIC_CLAMP.equals(name)) {
// The unit's player needs new transporters.
result = true;
playerFlags.put(unit.getOwner(), Boolean.TRUE);
// Stop looking.
break;
}
}
} // End unit-is-BattleArmor
} // Handle the next entity.
// Do we need to add any Magnetic Clamp transporters?
if (result) {
// Walk through the game's entities again.
for (Enumeration<Entity> i = entities.elements(); i
.hasMoreElements();) {
// Get this unit's player.
unit = i.nextElement();
player = unit.getOwner();
// Does this player need updated transporters?
if (Boolean.TRUE.equals(playerFlags.get(player))) {
// Add the appropriate transporter to the unit.
if (!unit.isOmni() && (unit instanceof Mech)) {
unit.addTransporter(new ClampMountMech());
} else if ((unit instanceof Tank) && !(unit instanceof VTOL)) {
unit.addTransporter(new ClampMountTank());
}
}
} // End player-needs-transports
} // Handle the next unit.
// Return the result.
return result;
} // End private boolean checkForMagneticClamp()
/**
* Adds the specified action to the actions list for this phase.
*/
public void addAction(EntityAction ea) {
actions.addElement(ea);
processGameEvent(new GameNewActionEvent(this, ea));
}
public void setArtilleryVector(Vector<ArtilleryAttackAction> v) {
offboardArtilleryAttacks = v;
processGameEvent(new GameBoardChangeEvent(this));
}
public void resetArtilleryAttacks() {
offboardArtilleryAttacks.removeAllElements();
}
public Enumeration<ArtilleryAttackAction> getArtilleryAttacks() {
return offboardArtilleryAttacks.elements();
}
public int getArtillerySize() {
return offboardArtilleryAttacks.size();
}
/** Returns an Enumeration of actions scheduled for this phase. */
public Enumeration<EntityAction> getActions() {
return actions.elements();
}
/** Resets the actions list. */
public void resetActions() {
actions.removeAllElements();
}
/** Removes all actions by the specified entity */
public void removeActionsFor(int entityId) {
// or rather, only keeps actions NOT by that entity
Vector<EntityAction> toKeep = new Vector<EntityAction>(actions.size());
for (Enumeration<EntityAction> i = actions.elements(); i
.hasMoreElements();) {
EntityAction ea = i.nextElement();
if (ea.getEntityId() != entityId) {
toKeep.addElement(ea);
}
}
actions = toKeep;
}
/**
* Remove a specified action
*
* @param o The action to remove.
*/
public void removeAction(Object o) {
actions.removeElement(o);
}
public int actionsSize() {
return actions.size();
}
/**
* Returns the actions vector. Do not use to modify the actions; I will be
* angry. >:[ Used for sending all actions to the client.
*/
public Vector<EntityAction> getActionsVector() {
return actions;
}
public void addInitiativeRerollRequest(Team t) {
initiativeRerollRequests.addElement(t);
}
public void rollInitAndResolveTies() {
if (getOptions().booleanOption("individual_initiative")) {
Vector<TurnOrdered> vRerolls = new Vector<TurnOrdered>();
for (int i = 0; i < entities.size(); i++) {
Entity e = entities.elementAt(i);
if (initiativeRerollRequests.contains(getTeamForPlayer(e
.getOwner()))) {
vRerolls.add(e);
}
}
TurnOrdered.rollInitAndResolveTies(entities, vRerolls);
} else {
TurnOrdered.rollInitAndResolveTies(teams, initiativeRerollRequests);
}
initiativeRerollRequests.removeAllElements();
}
public int getNoOfInitiativeRerollRequests() {
return initiativeRerollRequests.size();
}
/**
* Adds a pending displacement attack to the list for this phase.
*/
public void addCharge(AttackAction ea) {
pendingCharges.addElement(ea);
processGameEvent(new GameNewActionEvent(this, ea));
}
/**
* Returns an Enumeration of displacement attacks scheduled for the end of
* the physical phase.
*/
public Enumeration<AttackAction> getCharges() {
return pendingCharges.elements();
}
/** Resets the pending charges list. */
public void resetCharges() {
pendingCharges.removeAllElements();
}
/**
* Returns the charges vector. Do not modify. >:[ Used for sending all
* charges to the client.
*/
public Vector<AttackAction> getChargesVector() {
return pendingCharges;
}
/**
* Adds a pending ramming attack to the list for this phase.
*/
public void addRam(AttackAction ea) {
pendingRams.addElement(ea);
processGameEvent(new GameNewActionEvent(this, ea));
}
/**
* Returns an Enumeration of ramming attacks scheduled for the end of
* the physical phase.
*/
public Enumeration<AttackAction> getRams() {
return pendingRams.elements();
}
/** Resets the pending rams list. */
public void resetRams() {
pendingRams.removeAllElements();
}
/**
* Returns the rams vector. Do not modify. >:[ Used for sending all
* charges to the client.
*/
public Vector<AttackAction> getRamsVector() {
return pendingRams;
}
/**
* Adds a pending ramming attack to the list for this phase.
*/
public void addTeleMissileAttack(AttackAction ea) {
pendingTeleMissileAttacks.addElement(ea);
processGameEvent(new GameNewActionEvent(this, ea));
}
/**
* Returns an Enumeration of ramming attacks scheduled for the end of
* the physical phase.
*/
public Enumeration<AttackAction> getTeleMissileAttacks() {
return pendingTeleMissileAttacks.elements();
}
/** Resets the pending rams list. */
public void resetTeleMissileAttacks() {
pendingTeleMissileAttacks.removeAllElements();
}
/**
* Returns the rams vector. Do not modify. >:[ Used for sending all
* charges to the client.
*/
public Vector<AttackAction> getTeleMissileAttacksVector() {
return pendingTeleMissileAttacks;
}
/** Adds a pending PSR to the list for this phase. */
public void addPSR(PilotingRollData psr) {
pilotRolls.addElement(psr);
}
/** Returns an Enumeration of pending PSRs. */
public Enumeration<PilotingRollData> getPSRs() {
return pilotRolls.elements();
}
/** Adds a pending extreme Gravity PSR to the list for this phase. */
public void addExtremeGravityPSR(PilotingRollData psr) {
extremeGravityRolls.addElement(psr);
}
/** Returns an Enumeration of pending extreme GravityPSRs. */
public Enumeration<PilotingRollData> getExtremeGravityPSRs() {
return extremeGravityRolls.elements();
}
/** Resets the PSR list for a given entity. */
public void resetPSRs(Entity entity) {
PilotingRollData roll;
Vector<Integer> rollsToRemove = new Vector<Integer>();
int i = 0;
// first, find all the rolls belonging to the target entity
for (i = 0; i < pilotRolls.size(); i++) {
roll = pilotRolls.elementAt(i);
if (roll.getEntityId() == entity.getId()) {
rollsToRemove.addElement(new Integer(i));
}
}
// now, clear them out
for (i = rollsToRemove.size() - 1; i > -1; i--) {
pilotRolls.removeElementAt(rollsToRemove.elementAt(i).intValue());
}
}
/** Resets the extreme Gravity PSR list. */
public void resetExtremeGravityPSRs() {
extremeGravityRolls.removeAllElements();
}
/** Resets the extreme Gravity PSR list for a given entity. */
public void resetExtremeGravityPSRs(Entity entity) {
PilotingRollData roll;
Vector<Integer> rollsToRemove = new Vector<Integer>();
int i = 0;
// first, find all the rolls belonging to the target entity
for (i = 0; i < extremeGravityRolls.size(); i++) {
roll = extremeGravityRolls.elementAt(i);
if (roll.getEntityId() == entity.getId()) {
rollsToRemove.addElement(new Integer(i));
}
}
// now, clear them out
for (i = rollsToRemove.size() - 1; i > -1; i--) {
extremeGravityRolls.removeElementAt(rollsToRemove.elementAt(i)
.intValue());
}
}
/** Resets the PSR list. */
public void resetPSRs() {
pilotRolls.removeAllElements();
}
/**
* add an AttackHandler to the attacks list
*
* @param ah - The <code>AttackHandler</code> to add
*/
public void addAttack(AttackHandler ah) {
attacks.add(ah);
}
/**
* remove an AttackHandler from the attacks list
*
* @param ah - The <code>AttackHandler</code> to remove
*/
public void removeAttack(AttackHandler ah) {
attacks.removeElement(ah);
}
/**
* get the attacks
*
* @return a <code>Enumeration</code> of all <code>AttackHandler</code>s
*/
public Enumeration<AttackHandler> getAttacks() {
return attacks.elements();
}
/**
* get the attacks vector
*
* @return the <code>Vector</code> containing the attacks
*/
public Vector<AttackHandler> getAttacksVector() {
return attacks;
}
/**
* reset the attacks vector
*/
public void resetAttacks() {
attacks = new Vector<AttackHandler>();
}
/**
* set the attacks vector
*
* @param v - the <code>Vector</code> that should be the new attacks
* vector
*/
public void setAttacksVector(Vector<AttackHandler> v) {
attacks = v;
}
/**
* Getter for property roundCount.
*
* @return Value of property roundCount.
*/
public int getRoundCount() {
return roundCount;
}
public void setRoundCount(int roundCount) {
this.roundCount = roundCount;
}
/** Increments the round counter */
public void incrementRoundCount() {
roundCount++;
}
/**
* Getter for property forceVictory.
*
* @return Value of property forceVictory.
*/
public boolean isForceVictory() {
return forceVictory;
}
/**
* Setter for property forceVictory.
*
* @param forceVictory New value of property forceVictory.
*/
public void setForceVictory(boolean forceVictory) {
this.forceVictory = forceVictory;
}
public void addReports(Vector<Report> v) {
if (v.size() == 0) {
System.out.println("Game.addReports() received blank vector.");
return;
}
gameReports.add(roundCount, v);
}
public Vector<Report> getReports(int r) {
return gameReports.get(r);
}
public Vector<Vector<Report>> getAllReports() {
return gameReports.get();
}
public void setAllReports(Vector<Vector<Report>> v) {
gameReports.set(v);
}
public void clearAllReports() {
gameReports.clear();
}
public void end(int winner, int winnerTeam) {
setVictoryPlayerId(winner);
setVictoryTeam(winnerTeam);
processGameEvent(new GameEndEvent(this));
}
/**
* Getter for property victoryPlayerId.
*
* @return Value of property victoryPlayerId.
*/
public int getVictoryPlayerId() {
return victoryPlayerId;
}
/**
* Setter for property victoryPlayerId.
*
* @param victoryPlayerId New value of property victoryPlayerId.
*/
public void setVictoryPlayerId(int victoryPlayerId) {
this.victoryPlayerId = victoryPlayerId;
}
/**
* Getter for property victoryTeam.
*
* @return Value of property victoryTeam.
*/
public int getVictoryTeam() {
return victoryTeam;
}
/**
* Setter for property victoryTeam.
*
* @param victoryTeam New value of property victoryTeam.
*/
public void setVictoryTeam(int victoryTeam) {
this.victoryTeam = victoryTeam;
}
/**
* Returns true if the specified player is either the victor, or is on the
* winning team. Best to call during PHASE_VICTORY.
*/
public boolean isPlayerVictor(Player player) {
if (player.getTeam() == Player.TEAM_NONE) {
return player.getId() == victoryPlayerId;
}
return player.getTeam() == victoryTeam;
}
public HashMap<String, Object> getVictoryContext() {
return victoryContext;
}
public void setVictoryContext(HashMap<String, Object> ctx) {
victoryContext = ctx;
}
/** Shortcut to isPlayerVictor(Player player) */
public boolean isPlayerVictor(int playerId) {
return isPlayerVictor(getPlayer(playerId));
}
/**
* Get all <code>Entity</code>s that pass the given selection criteria.
*
* @param selector the <code>EntitySelector</code> that implements test
* that an entity must pass to be included. This value may be
* <code>null</code> (in which case all entities in the game
* will be returned).
* @return an <code>Enumeration</code> of all entities that the selector
* accepts. This value will not be <code>null</code> but it may be
* empty.
*/
public Enumeration<Entity> getSelectedEntities(EntitySelector selector) {
Enumeration<Entity> retVal;
// If no selector was supplied, return all entities.
if (null == selector) {
retVal = this.getEntities();
}
// Otherwise, return an anonymous Enumeration
// that selects entities in this game.
else {
final EntitySelector entry = selector;
retVal = new Enumeration<Entity>() {
private EntitySelector entitySelector = entry;
private Entity current = null;
private Enumeration<Entity> iter = getEntities();
// Do any more entities meet the selection criteria?
public boolean hasMoreElements() {
// See if we have a pre-approved entity.
if (null == current) {
// Find the first acceptable entity
while ((null == current) && iter.hasMoreElements()) {
current = iter.nextElement();
if (!entitySelector.accept(current)) {
current = null;
}
}
}
return (null != current);
}
// Get the next entity that meets the selection criteria.
public Entity nextElement() {
// Pre-approve an entity.
if (!hasMoreElements()) {
return null;
}
// Use the pre-approved entity, and null out our reference.
Entity next = current;
current = null;
return next;
}
};
} // End use-selector
// Return the selected entities.
return retVal;
}
/**
* Count all <code>Entity</code>s that pass the given selection criteria.
*
* @param selector the <code>EntitySelector</code> that implements test
* that an entity must pass to be included. This value may be
* <code>null</code> (in which case the count of all entities
* in the game will be returned).
* @return the <code>int</code> count of all entities that the selector
* accepts. This value will not be <code>null</code> but it may be
* empty.
*/
public int getSelectedEntityCount(EntitySelector selector) {
int retVal = 0;
// If no selector was supplied, return the count of all game entities.
if (null == selector) {
retVal = getNoOfEntities();
}
// Otherwise, count the entities that meet the selection criteria.
else {
Enumeration<Entity> iter = this.getEntities();
while (iter.hasMoreElements()) {
if (selector.accept(iter.nextElement())) {
retVal++;
}
}
} // End use-selector
// Return the number of selected entities.
return retVal;
}
/**
* Get all out-of-game <code>Entity</code>s that pass the given selection
* criteria.
*
* @param selector the <code>EntitySelector</code> that implements test
* that an entity must pass to be included. This value may be
* <code>null</code> (in which case all entities in the game
* will be returned).
* @return an <code>Enumeration</code> of all entities that the selector
* accepts. This value will not be <code>null</code> but it may be
* empty.
*/
public Enumeration<Entity> getSelectedOutOfGameEntities(
EntitySelector selector) {
Enumeration<Entity> retVal;
// If no selector was supplied, return all entities.
if (null == selector) {
retVal = vOutOfGame.elements();
}
// Otherwise, return an anonymous Enumeration
// that selects entities in this game.
else {
final EntitySelector entry = selector;
retVal = new Enumeration<Entity>() {
private EntitySelector entitySelector = entry;
private Entity current = null;
private Enumeration<Entity> iter = vOutOfGame.elements();
// Do any more entities meet the selection criteria?
public boolean hasMoreElements() {
// See if we have a pre-approved entity.
if (null == current) {
// Find the first acceptable entity
while ((null == current) && iter.hasMoreElements()) {
current = iter.nextElement();
if (!entitySelector.accept(current)) {
current = null;
}
}
}
return (null != current);
}
// Get the next entity that meets the selection criteria.
public Entity nextElement() {
// Pre-approve an entity.
if (!hasMoreElements()) {
return null;
}
// Use the pre-approved entity, and null out our reference.
Entity next = current;
current = null;
return next;
}
};
} // End use-selector
// Return the selected entities.
return retVal;
}
/**
* Count all out-of-game<code>Entity</code>s that pass the given
* selection criteria.
*
* @param selector the <code>EntitySelector</code> that implements test
* that an entity must pass to be included. This value may be
* <code>null</code> (in which case the count of all
* out-of-game entities will be returned).
* @return the <code>int</code> count of all entities that the selector
* accepts. This value will not be <code>null</code> but it may be
* empty.
*/
public int getSelectedOutOfGameEntityCount(EntitySelector selector) {
int retVal = 0;
// If no selector was supplied, return the count of all game entities.
if (null == selector) {
retVal = vOutOfGame.size();
}
// Otherwise, count the entities that meet the selection criteria.
else {
Enumeration<Entity> iter = vOutOfGame.elements();
while (iter.hasMoreElements()) {
if (selector.accept(iter.nextElement())) {
retVal++;
}
}
} // End use-selector
// Return the number of selected entities.
return retVal;
}
/**
* Returns true if the player has any valid units this turn that are not
* infantry, not protomechs, or not either of those. This method is
* utitilized by the "A players Infantry moves after that players other
* units", and "A players Protomechs move after that players other units"
* options.
*/
public boolean checkForValidNonInfantryAndOrProtomechs(int playerId) {
Iterator<Entity> iter = getPlayerEntities(getPlayer(playerId), false)
.iterator();
while (iter.hasNext()) {
Entity entity = iter.next();
boolean excluded = false;
if ((entity instanceof Infantry)
&& getOptions().booleanOption("inf_move_later")) {
excluded = true;
} else if ((entity instanceof Protomech)
&& getOptions().booleanOption("protos_move_later")) {
excluded = true;
}
if (!excluded && getTurn().isValidEntity(entity, this)) {
return true;
}
}
return false;
}
/**
* Get Entities that have have a iNarc Nemesis pod attached and are situated
* between two Coords
*
* @param attacker The attacking <code>Entity</code>.
* @param target The <code>Coords</code> of the original target.
* @return a <code>Enumeration</code> of entities that have nemesis pods
* attached and are located between attacker and target and are
* friendly with the attacker.
*/
public Enumeration<Entity> getNemesisTargets(Entity attacker, Coords target) {
final Coords attackerPos = attacker.getPosition();
final ArrayList<Coords> in = Coords.intervening(attackerPos, target);
Vector<Entity> nemesisTargets = new Vector<Entity>();
for (Coords c : in) {
for (Enumeration<Entity> e = getEntities(c); e.hasMoreElements();) {
Entity entity = e.nextElement();
if (entity.isINarcedWith(INarcPod.NEMESIS)
&& !entity.isEnemyOf(attacker)) {
nemesisTargets.addElement(entity);
}
}
}
return nemesisTargets.elements();
}
private Vector<GameListener> gameListeners = new Vector<GameListener>();
/**
* Adds the specified game listener to receive board events from this board.
*
* @param listener the game listener.
*/
public void addGameListener(GameListener listener) {
gameListeners.addElement(listener);
}
/**
* Removes the specified game listener.
*
* @param listener the game listener.
*/
public void removeGameListener(GameListener listener) {
gameListeners.removeElement(listener);
}
/**
* Returns all the GameListeners.
*
* @return
*/
public Vector<GameListener> getGameListeners() {
return gameListeners;
}
/**
* purges all Game Listener objects.
*/
public void purgeGameListeners() {
gameListeners.clear();
}
/**
* Processes game events occurring on this connection by dispatching them to
* any registered GameListener objects.
*
* @param event the game event.
*/
public void processGameEvent(GameEvent event) {
for (Enumeration<GameListener> e = gameListeners.elements(); e
.hasMoreElements();) {
GameListener l = e.nextElement();
switch (event.getType()) {
case GameEvent.GAME_PLAYER_CONNECTED:
l.gamePlayerConnected((GamePlayerConnectedEvent) event);
break;
case GameEvent.GAME_PLAYER_DISCONNECTED:
l.gamePlayerDisconnected((GamePlayerDisconnectedEvent) event);
break;
case GameEvent.GAME_PLAYER_CHANGE:
l.gamePlayerChange((GamePlayerChangeEvent) event);
break;
case GameEvent.GAME_PLAYER_CHAT:
l.gamePlayerChat((GamePlayerChatEvent) event);
break;
case GameEvent.GAME_PHASE_CHANGE:
l.gamePhaseChange((GamePhaseChangeEvent) event);
break;
case GameEvent.GAME_TURN_CHANGE:
l.gameTurnChange((GameTurnChangeEvent) event);
break;
case GameEvent.GAME_REPORT:
l.gameReport((GameReportEvent) event);
break;
case GameEvent.GAME_END:
l.gameEnd((GameEndEvent) event);
break;
case GameEvent.GAME_BOARD_NEW:
l.gameBoardNew((GameBoardNewEvent) event);
break;
case GameEvent.GAME_BOARD_CHANGE:
l.gameBoardChanged((GameBoardChangeEvent) event);
break;
case GameEvent.GAME_SETTINGS_CHANGE:
l.gameSettingsChange((GameSettingsChangeEvent) event);
break;
case GameEvent.GAME_MAP_QUERY:
l.gameMapQuery((GameMapQueryEvent) event);
break;
case GameEvent.GAME_ENTITY_NEW:
l.gameEntityNew((GameEntityNewEvent) event);
break;
case GameEvent.GAME_ENTITY_REMOVE:
l.gameEntityRemove((GameEntityRemoveEvent) event);
break;
case GameEvent.GAME_ENTITY_NEW_OFFBOARD:
l.gameEntityNewOffboard((GameEntityNewOffboardEvent) event);
break;
case GameEvent.GAME_ENTITY_CHANGE:
l.gameEntityChange((GameEntityChangeEvent) event);
break;
case GameEvent.GAME_NEW_ACTION:
l.gameNewAction((GameNewActionEvent) event);
break;
}
}
}
/**
* Returns this turn's tag information
*/
public Vector<TagInfo> getTagInfo() {
return tagInfoForTurn;
}
public void addTagInfo(TagInfo info) {
tagInfoForTurn.addElement(info);
}
public void updateTagInfo(TagInfo info, int index) {
tagInfoForTurn.setElementAt(info, index);
}
public void resetTagInfo() {
tagInfoForTurn.removeAllElements();
}
public void clearTagInfoShots(Entity ae, Coords tc) {
for (int i = 0; i < tagInfoForTurn.size(); i++) {
TagInfo info = tagInfoForTurn.elementAt(i);
Entity attacker = getEntity(info.attackerId);
Entity target = getEntity(info.targetId);
if (!ae.isEnemyOf(attacker) && target.isOnSameSheet(tc)) {
info.shots = 0;
tagInfoForTurn.setElementAt(info, i);
}
}
}
/**
* Get a list of flares
*/
public Vector<Flare> getFlares() {
return flares;
}
/**
* Set the list of flares
*/
public void setFlares(Vector<Flare> flares) {
this.flares = flares;
processGameEvent(new GameBoardChangeEvent(this));
}
/**
* Add a new flare
*/
public void addFlare(Flare flare) {
flares.addElement(flare);
processGameEvent(new GameBoardChangeEvent(this));
}
/**
* returns true if the hex is illuminated by a flare
*/
public boolean isPositionIlluminated(Coords c) {
for (Enumeration<Flare> e = flares.elements(); e.hasMoreElements();) {
Flare flare = e.nextElement();
if (flare.illuminates(c)) {
return true;
}
}
return false;
}
/**
* Age the flare list and remove any which have burnt out Artillery flares
* drift with wind. (called at end of turn)
*/
public Vector<Report> ageFlares() {
Vector<Report> reports = new Vector<Report>();
Report r;
for (int i = flares.size() - 1; i >= 0; i--) {
Flare flare = flares.elementAt(i);
r = new Report(5235);
r.add(flare.position.getBoardNum());
r.newlines = 0;
reports.addElement(r);
if ((flare.flags & Flare.F_IGNITED) != 0) {
flare.turnsToBurn--;
if ((flare.flags & Flare.F_DRIFTING) != 0) {
int dir = planetaryConditions.getWindDirection();
int str = planetaryConditions.getWindStrength();
// strength 1 and 2: drift 1 hex
// strength 3: drift 2 hexes
// strength 4: drift 3 hexes
// for each above strenght 4 (storm), drift one more
if (str > 0) {
flare.position = flare.position.translated(dir);
if (str > 2) {
flare.position = flare.position.translated(dir);
}
if (str > 3) {
flare.position = flare.position.translated(dir);
}
if (str > 4) {
flare.position = flare.position.translated(dir);
}
if (str > 5) {
flare.position = flare.position.translated(dir);
}
r = new Report(5236);
r.add(flare.position.getBoardNum());
r.newlines = 0;
reports.addElement(r);
}
}
} else {
r = new Report(5237);
r.newlines = 0;
reports.addElement(r);
flare.flags |= Flare.F_IGNITED;
}
if (flare.turnsToBurn <= 0) {
r = new Report(5238);
reports.addElement(r);
flares.removeElementAt(i);
} else {
r = new Report(5239);
r.add(flare.turnsToBurn);
reports.addElement(r);
flares.setElementAt(flare, i);
}
}
processGameEvent(new GameBoardChangeEvent(this));
return reports;
}
public boolean gameTimerIsExpired() {
return ((getOptions().booleanOption("use_game_turn_limit")) && (getRoundCount() == getOptions()
.intOption("game_turn_limit")));
}
/**
* use victoryfactory to generate a new victorycondition checker provided
* that the victorycontext is saved properly, calling this method at any
* time is ok and should not affect anything unless the
* victorycondition-configoptions have changed.
*/
public void createVictoryConditions() {
victory = vf
.createVictory("this string should be taken from game options");
}
public Victory getVictory() {
return victory;
}
//a shortcut function for determining whether vectored movement is applicable
public boolean useVectorMove() {
return getOptions().booleanOption("advanced_movement") && board.inSpace();
}
/** Adds a pending Control roll to the list for this phase. */
public void addControlRoll(PilotingRollData control) {
controlRolls.addElement(control);
}
/** Returns an Enumeration of pending Control rolls. */
public Enumeration<PilotingRollData> getControlRolls() {
return controlRolls.elements();
}
/** Resets the Control Roll list for a given entity. */
public void resetControlRolls(Entity entity) {
PilotingRollData roll;
Vector<Integer> rollsToRemove = new Vector<Integer>();
int i = 0;
// first, find all the rolls belonging to the target entity
for (i = 0; i < controlRolls.size(); i++) {
roll = controlRolls.elementAt(i);
if (roll.getEntityId() == entity.getId()) {
rollsToRemove.addElement(new Integer(i));
}
}
// now, clear them out
for (i = rollsToRemove.size() - 1; i > -1; i--) {
controlRolls.removeElementAt(rollsToRemove.elementAt(i).intValue());
}
}
/** Resets the PSR list. */
public void resetControlRolls() {
controlRolls.removeAllElements();
}
/** A set of checks for aero units to make sure that the movement order is
* maintained
*/
public boolean checkForValidSpaceStations(int playerId) {
Iterator<Entity> iter = getPlayerEntities(getPlayer(playerId), false).iterator();
while (iter.hasNext()) {
Entity entity = iter.next();
if ((entity instanceof SpaceStation) && getTurn().isValidEntity(entity, this)) {
return true;
}
}
return false;
}
public boolean checkForValidJumpships(int playerId) {
Iterator<Entity> iter = getPlayerEntities(getPlayer(playerId), false).iterator();
while (iter.hasNext()) {
Entity entity = iter.next();
if ((entity instanceof Jumpship) && !(entity instanceof Warship) && getTurn().isValidEntity(entity, this)) {
return true;
}
}
return false;
}
public boolean checkForValidWarships(int playerId) {
Iterator<Entity> iter = getPlayerEntities(getPlayer(playerId), false).iterator();
while (iter.hasNext()) {
Entity entity = iter.next();
if ((entity instanceof Warship) && getTurn().isValidEntity(entity, this)) {
return true;
}
}
return false;
}
public boolean checkForValidDropships(int playerId) {
Iterator<Entity> iter = getPlayerEntities(getPlayer(playerId), false).iterator();
while (iter.hasNext()) {
Entity entity = iter.next();
if ((entity instanceof Dropship) && getTurn().isValidEntity(entity, this)) {
return true;
}
}
return false;
}
public boolean checkForValidSmallCraft(int playerId) {
Iterator<Entity> iter = getPlayerEntities(getPlayer(playerId), false).iterator();
while (iter.hasNext()) {
Entity entity = iter.next();
if ((entity instanceof SmallCraft) && getTurn().isValidEntity(entity, this)) {
return true;
}
}
return false;
}
public PlanetaryConditions getPlanetaryConditions() {
return planetaryConditions;
}
public void setPlanetaryConditions(PlanetaryConditions conditions) {
if (null == conditions) {
System.err.println("Can't set the planetary conditions to null!");
} else {
planetaryConditions = conditions;
processGameEvent(new GameSettingsChangeEvent(this));
}
}
public void addSmokeCloud(SmokeCloud cloud) {
smokeCloudList.add(cloud);
}
public List<SmokeCloud> getSmokeCloudList() {
return smokeCloudList;
}
}