package net.sf.colossus.client;
import java.util.HashSet;
import java.util.Set;
import net.sf.colossus.common.IOptions;
import net.sf.colossus.common.Options;
import net.sf.colossus.game.BattleCritter;
import net.sf.colossus.game.Game;
import net.sf.colossus.variant.BattleHex;
import net.sf.colossus.variant.CreatureType;
import net.sf.colossus.variant.MasterBoardTerrain;
/**
* Class BattleMovement does client-side battle move calculations.
*
* @author David Ripton
* @author Romain Dolbeau
*/
// XXX Massively duplicated code. Merge later.
// YYY Move this up to become game.BattleMovement, and use it on server side
// as well, instead of the methods in BattleServerSide itself.
final class BattleMovement
{
private final Game game;
// TODO instead listener to the option changes
// WARNING Option changes from server appear here as string-based,
// so a listener based on the boolean option would not work.
// For now, we just make sure we create BattleMovement after
// all server-to-client-option-sync'ing is completed.
final boolean cumulativeSlow;
final boolean oneHexAllowed;
BattleMovement(Game game, IOptions options)
{
this.game = game;
cumulativeSlow = options.getOption(Options.cumulativeSlow);
oneHexAllowed = options.getOption(Options.oneHexAllowed);
}
/** Recursively find moves from this hex. Return a set of all
* legal destinations. Do not double back. */
private Set<BattleHex> findMoves(BattleHex hex, CreatureType creature,
boolean flies, int movesLeft, int cameFrom, boolean first)
{
Set<BattleHex> set = new HashSet<BattleHex>();
for (int i = 0; i < 6; i++)
{
// Do not double back.
if (i != cameFrom)
{
BattleHex neighbor = hex.getNeighbor(i);
if (neighbor != null)
{
int reverseDir = (i + 3) % 6;
int entryCost;
if (!game.getBattle().isOccupied(neighbor))
{
entryCost = neighbor.getEntryCost(creature,
reverseDir, cumulativeSlow);
}
else
{
entryCost = BattleHex.IMPASSIBLE_COST;
}
if ((entryCost != BattleHex.IMPASSIBLE_COST)
&& ((entryCost <= movesLeft) || (first && oneHexAllowed)))
{
// Mark that hex as a legal move.
set.add(neighbor);
// If there are movement points remaining, continue
// checking moves from there. Fliers skip this
// because flying is more efficient.
if (!flies && movesLeft > entryCost)
{
set.addAll(findMoves(neighbor, creature, flies,
movesLeft - entryCost, reverseDir, false));
}
}
// Fliers can fly over any hex for 1 movement point,
// but some Hex cannot be flown over by some creatures.
if (flies && movesLeft > 1
&& neighbor.canBeFlownOverBy(creature))
{
set.addAll(findMoves(neighbor, creature, flies,
movesLeft - 1, reverseDir, false));
}
}
}
}
return set;
}
/** This method is called by the defender on turn 1 in a
* Startlisted Terrain,
* so we know that there are no enemies on board, and all allies
* are mobile.
*/
private Set<BattleHex> findUnoccupiedStartlistHexes(
MasterBoardTerrain terrain)
{
Set<BattleHex> set = new HashSet<BattleHex>();
for (String hexLabel : terrain.getStartList())
{
BattleHex hex = terrain.getHexByLabel(hexLabel);
if (!game.getBattle().isOccupied(hex))
{
set.add(hex);
}
}
return set;
}
/** Find all legal moves for this critter.*/
public Set<BattleHex> showMoves(BattleCritter critter)
{
Set<BattleHex> set = new HashSet<BattleHex>();
if (!critter.hasMoved()
&& !game.getBattle().isInContact(critter, false))
{
MasterBoardTerrain terrain = game.getBattleSite().getTerrain();
if (terrain.hasStartList()
&& game.getBattleTurnNumber() == 1
&& game.getBattleActiveLegion().equals(
game.getBattle().getDefendingLegion()))
{
set = findUnoccupiedStartlistHexes(terrain);
}
else
{
CreatureType type = critter.getType();
BattleHex hex = critter.getCurrentHex();
set = findMoves(hex, type, type.isFlier(), type.getSkill()
- critter.getSlowed(), -1, true);
}
}
return set;
}
}