package net.sf.colossus.server; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import net.sf.colossus.game.Caretaker; import net.sf.colossus.game.Player; import net.sf.colossus.variant.CreatureType; import net.sf.colossus.variant.ICustomRecruitBase; import net.sf.colossus.variant.MasterBoardTerrain; import net.sf.colossus.variant.MasterHex; /** * Base class to implement custom recruiting functions * (i.e. anything that is not a-number-of-creature to another creature) * * @author Romain Dolbeau */ abstract public class CustomRecruitBase implements ICustomRecruitBase { private static final Logger LOGGER = Logger .getLogger(CustomRecruitBase.class.getName()); final protected static List<Player> allPlayers = new ArrayList<Player>(); // TODO consider storing the Game instances instead, which would give access to both Caretaker and Player // instances private final static List<Caretaker> allCaretakerInfo = new ArrayList<Caretaker>(); private static GameServerSide serverGame = null; private final static List<CustomRecruitBase> allCustomRecruitBase = new ArrayList<CustomRecruitBase>(); public CustomRecruitBase() { LOGGER.log(Level.FINEST, "CUSTOM: adding " + getClass().getName()); allCustomRecruitBase.add(this); } /* full reset (change variant) */ synchronized public static final void reset() { allPlayers.clear(); allCaretakerInfo.clear(); allCustomRecruitBase.clear(); serverGame = null; } /* partial reset (change game) */ synchronized public static final void resetAllInstances() { allPlayers.clear(); allCaretakerInfo.clear(); serverGame = null; Iterator<CustomRecruitBase> it = allCustomRecruitBase.iterator(); while (it.hasNext()) { CustomRecruitBase crb = it.next(); crb.resetInstance(); } } protected synchronized void initCustomVariant() { // nothing to do, only Balrog needs this } synchronized static public final void initCustomVariantForAllCRBs() { Iterator<CustomRecruitBase> it = allCustomRecruitBase.iterator(); while (it.hasNext()) { CustomRecruitBase crb = it.next(); crb.initCustomVariant(); } } synchronized public static final void everyoneAdvanceTurn( int newActivePlayer) { Iterator<CustomRecruitBase> it = allCustomRecruitBase.iterator(); while (it.hasNext()) { CustomRecruitBase crb = it.next(); crb.changeOfTurn(newActivePlayer); } } synchronized public static final void addPlayerClientSide(Player pi) { allPlayers.add(pi); } synchronized public static final void addCaretakerClientSide(Caretaker ci) { allCaretakerInfo.add(ci); } synchronized public static final void setGame(GameServerSide g) { serverGame = g; } synchronized protected final void setCount(CreatureType type, int newCount, boolean reset) { // TODO Should only update server side, and propagate to all // clients via messages. // The way it's done now will probably fail for remote clients. // first update all known CaretakerInfo (if we're client(s)) Iterator<Caretaker> it = allCaretakerInfo.iterator(); while (it.hasNext()) { Caretaker ci = it.next(); if (reset) { ci.setAvailableCount(type, 0); ci.setDeadCount(type, 0); } else { ci.setAvailableCount(type, newCount); } } // update the Caretaker if we're server if (serverGame != null) { Caretaker ci = serverGame.getCaretaker(); if (reset) { ci.setAvailableCount(type, 0); ci.setDeadCount(type, 0); } else { ci.setAvailableCount(type, newCount); } } } synchronized protected final void adjustAvailableCount(CreatureType type) { // first update all known CaretakerInfo (if we're client(s)) Iterator<Caretaker> it = allCaretakerInfo.iterator(); while (it.hasNext()) { Caretaker ci = it.next(); ci.adjustAvailableCount(type); } // update the Caretaker if we're server if (serverGame != null) { Caretaker ci = serverGame.getCaretaker(); ci.adjustAvailableCount(type); } } synchronized protected final int getCount(CreatureType type) { int count = -1; int oldcount = -1; Iterator<Caretaker> it = allCaretakerInfo.iterator(); while (it.hasNext() && (count == -1)) { Caretaker ci = it.next(); oldcount = count; count = ci.getAvailableCount(type); if ((oldcount != -1) && (count != oldcount)) { LOGGER.log(Level.SEVERE, "in CustomRecruitBase, not all CaretakerInfo's" + " count match !"); } } // second, update the Caretaker if we're server if ((serverGame != null) && (count == -1)) { oldcount = count; count = serverGame.getCaretaker().getAvailableCount(type); if ((oldcount != -1) && (count != oldcount)) { LOGGER.log(Level.SEVERE, "in CustomRecruitBase, Caretaker's count " + "doesn't match CaretakerInfo's counts!"); } } return count; } synchronized protected final int getDeadCount(CreatureType type) { int count = -1; int oldcount = -1; Iterator<Caretaker> it = allCaretakerInfo.iterator(); while (it.hasNext() && (count == -1)) { Caretaker ci = it.next(); oldcount = count; count = ci.getDeadCount(type); if ((oldcount != -1) && (count != oldcount)) { LOGGER.log(Level.SEVERE, "in CustomRecruitBase, not all CaretakerInfo's " + "dead count match !"); } } // second, update the Caretaker if we're server if ((serverGame != null) && (count == -1)) { oldcount = count; count = serverGame.getCaretaker().getDeadCount(type); if ((oldcount != -1) && (count != oldcount)) { LOGGER.log(Level.SEVERE, "in CustomRecruitBase, Caretaker's dead count " + "doesn't match CaretakerInfo's counts!"); } } return count; } /** * List all creatures that can recruit in this terrain in a special way. */ abstract public List<CreatureType> getAllPossibleSpecialRecruiters( MasterBoardTerrain terrain); /** * List all creatures that can be recruited in this terrain * in a special way. */ abstract public List<CreatureType> getAllPossibleSpecialRecruits( MasterBoardTerrain terrain); /** * List creatures that can recruit in this terrain in a special way now. * @param hex The specific MasterHex considered for recruiting. * @return A List of possible special Recruiters in this hex. */ abstract public List<CreatureType> getPossibleSpecialRecruiters( MasterHex hex); /** * List creatures that can be recruited in this terrain * in a special way now. * @param hex The specific MasterHex considered for recruiting * (for an example, see getPossibleSpecialRecruits() in * BalrogRecruitment.java in Balrog variant directory) * @return A List of possible special Recruits in this hex. */ abstract public List<CreatureType> getPossibleSpecialRecruits(MasterHex hex); /** * Number of recruiters needed to get a recruit * in a special way in this terrain now. */ abstract public int numberOfRecruiterNeeded(CreatureType recruiter, CreatureType recruit, MasterHex hex); /** * Bookkeeping function, called once after every player turn. * * Protected as it should only be called from everyoneAdvanceTurn(). */ abstract protected void changeOfTurn(int newActivePlayer); /** * Reset, called at the beginning of a game. */ abstract protected void resetInstance(); }