package net.sf.colossus.server;
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.Game;
import net.sf.colossus.variant.BattleHex;
import net.sf.colossus.variant.MasterBoardTerrain;
/**
* This holds currently the BattleMovement related methods that has
* been so far part of BattleServerSide.
*
* TODO Further clean up, and unify with client.BattleMovement, and
* eventually move up to game.
*
* The client side version is in better shape, but some of the methods
* it uses from "game" package classes are abstract in game package and in
* server package classes they are only dummies!
*
* @author David Ripton (BattleServerSide)
* @author Romain Dolbeau (BattleServerSide)
* @author Clemens Katzer
*/
public class BattleMovementServerSide
{
private final Game game;
// TODO instead listener to the option changes
final boolean cumulativeSlow;
final boolean oneHexAllowed;
BattleMovementServerSide(IOptions options, Game game)
{
this.game = game;
cumulativeSlow = options.getOption(Options.cumulativeSlow);
oneHexAllowed = options.getOption(Options.oneHexAllowed);
}
/** Recursively find moves from this hex. Return a set of string hex IDs
* for all legal destinations. Do not double back. If ignoreMobileAllies
* is true, pretend that allied creatures that can move out of the
* way are not there. */
private Set<BattleHex> findMoves(BattleHex hex,
CreatureServerSide critter, boolean flies, int movesLeft,
int cameFrom, boolean ignoreMobileAllies, 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;
CreatureServerSide bogey = getBattleSS().getCreatureSS(
neighbor);
if (bogey == null
|| (ignoreMobileAllies
&& bogey.getMarkerId().equals(
critter.getMarkerId()) && !getBattleSS()
.isInContact(bogey, false)))
{
entryCost = neighbor.getEntryCost(critter.getType(),
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, critter, flies,
movesLeft - entryCost, reverseDir,
ignoreMobileAllies, 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(critter.getType()))
{
set.addAll(findMoves(neighbor, critter, flies,
movesLeft - 1, reverseDir, ignoreMobileAllies,
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(
boolean ignoreMobileAllies, MasterBoardTerrain terrain)
{
assert terrain != null;
Set<BattleHex> set = new HashSet<BattleHex>();
for (String hexLabel : terrain.getStartList())
{
BattleHex hex = terrain.getHexByLabel(hexLabel);
if (ignoreMobileAllies || !getBattleSS().isOccupied(hex))
{
set.add(hex);
}
}
return set;
}
/**
* Find all legal moves for this critter.
*/
public Set<BattleHex> showMoves(CreatureServerSide critter,
boolean ignoreMobileAllies)
{
Set<BattleHex> set = new HashSet<BattleHex>();
if (!critter.hasMoved() && !getBattleSS().isInContact(critter, false))
{
if (getBattleSS().getLocation().getTerrain().hasStartList()
&& (getBattleSS().getBattleTurnNumber() == 1)
&& getBattleSS().isDefenderActive())
{
set = findUnoccupiedStartlistHexes(ignoreMobileAllies,
getBattleSS().getLocation().getTerrain());
}
else
{
set = findMoves(critter.getCurrentHex(), critter,
critter.isFlier(),
critter.getSkill() - critter.getSlowed(), -1,
ignoreMobileAllies, true);
}
}
return set;
}
BattleServerSide getBattleSS()
{
return ((GameServerSide)game).getBattleSS();
}
}