package games.strategy.triplea.delegate;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import games.strategy.engine.data.Change;
import games.strategy.engine.data.CompositeChange;
import games.strategy.engine.data.GameData;
import games.strategy.engine.data.GameMap;
import games.strategy.engine.data.PlayerID;
import games.strategy.engine.data.RelationshipTracker.Relationship;
import games.strategy.engine.data.Resource;
import games.strategy.engine.data.Territory;
import games.strategy.engine.data.Unit;
import games.strategy.engine.data.changefactory.ChangeFactory;
import games.strategy.engine.delegate.IDelegateBridge;
import games.strategy.engine.message.IRemote;
import games.strategy.engine.pbem.PBEMMessagePoster;
import games.strategy.engine.random.IRandomStats.DiceType;
import games.strategy.triplea.Constants;
import games.strategy.triplea.Properties;
import games.strategy.triplea.attachments.PlayerAttachment;
import games.strategy.triplea.attachments.RelationshipTypeAttachment;
import games.strategy.triplea.attachments.TechAbilityAttachment;
import games.strategy.triplea.attachments.TerritoryAttachment;
import games.strategy.triplea.attachments.UnitAttachment;
import games.strategy.triplea.delegate.remote.IAbstractForumPosterDelegate;
import games.strategy.triplea.formatter.MyFormatter;
import games.strategy.triplea.player.ITripleAPlayer;
import games.strategy.util.CompositeMatchAnd;
import games.strategy.util.IntegerMap;
import games.strategy.util.Match;
import games.strategy.util.ThreadUtil;
import games.strategy.util.Tuple;
/**
* At the end of the turn collect income.
*/
public abstract class AbstractEndTurnDelegate extends BaseTripleADelegate implements IAbstractForumPosterDelegate {
public static final String END_TURN_REPORT_STRING = "End of Turn Report for ";
private static final int CONVOY_BLOCKADE_DICE_SIDES = 6;
private boolean m_needToInitialize = true;
private boolean m_hasPostedTurnSummary = false;
private boolean isGiveUnitsByTerritory() {
return games.strategy.triplea.Properties.getGiveUnitsByTerritory(getData());
}
public boolean canPlayerCollectIncome(final PlayerID player, final GameData data) {
return TerritoryAttachment.doWeHaveEnoughCapitalsToProduce(m_player, getData());
}
/**
* Called before the delegate will run.
*/
@Override
public void start() {
// figure out our current PUs before we do anything else, including super methods
final GameData data = m_bridge.getData();
final Resource PUs = data.getResourceList().getResource(Constants.PUS);
final int leftOverPUs = m_bridge.getPlayerID().getResources().getQuantity(PUs);
super.start();
if (!m_needToInitialize) {
return;
}
final StringBuilder endTurnReport = new StringBuilder();
m_hasPostedTurnSummary = false;
final PlayerAttachment pa = PlayerAttachment.get(m_player);
// can't collect unless you own your own capital
if (!canPlayerCollectIncome(m_player, data)) {
endTurnReport.append(rollWarBondsForFriends(m_bridge, m_player, data));
// we do not collect any income this turn
} else {
// just collect resources
final Collection<Territory> territories = data.getMap().getTerritoriesOwnedBy(m_player);
int toAdd = getProduction(territories);
final int blockadeLoss = getBlockadeProductionLoss(m_player, data, m_bridge, endTurnReport);
toAdd -= blockadeLoss;
toAdd *= Properties.getPU_Multiplier(data);
int total = m_player.getResources().getQuantity(PUs) + toAdd;
final String transcriptText;
if (blockadeLoss == 0) {
transcriptText = m_player.getName() + " collect " + toAdd + MyFormatter.pluralize(" PU", toAdd) + "; end with "
+ total + MyFormatter.pluralize(" PU", total) + " total";
} else {
transcriptText =
m_player.getName() + " collect " + toAdd + MyFormatter.pluralize(" PU", toAdd) + " (" + blockadeLoss
+ " lost to blockades)" + "; end with " + total + MyFormatter.pluralize(" PU", total) + " total";
}
m_bridge.getHistoryWriter().startEvent(transcriptText);
endTurnReport.append(transcriptText).append("<br />");
// do war bonds
final int bonds = rollWarBonds(m_bridge, m_player, data);
if (bonds > 0) {
total += bonds;
toAdd += bonds;
final String bondText = m_player.getName() + " collect " + bonds + MyFormatter.pluralize(" PU", bonds)
+ " from War Bonds; end with " + total + MyFormatter.pluralize(" PU", total) + " total";
m_bridge.getHistoryWriter().startEvent(bondText);
endTurnReport.append("<br />").append(bondText).append("<br />");
}
if (total < 0) {
toAdd -= total;
total = 0;
}
final Change change = ChangeFactory.changeResourcesChange(m_player, PUs, toAdd);
m_bridge.addChange(change);
if (data.getProperties().get(Constants.PACIFIC_THEATER, false) && pa != null) {
final Change changeVP = (ChangeFactory.attachmentPropertyChange(pa,
(pa.getVps() + (toAdd / 10) + (pa.getCaptureVps() / 10)), "vps"));
final Change changeCapVP = ChangeFactory.attachmentPropertyChange(pa, "0", "captureVps");
final CompositeChange ccVP = new CompositeChange(changeVP, changeCapVP);
m_bridge.addChange(ccVP);
}
endTurnReport.append("<br />").append(addOtherResources(m_bridge));
endTurnReport.append("<br />").append(doNationalObjectivesAndOtherEndTurnEffects(m_bridge));
// now we do upkeep costs, including upkeep cost as a percentage of our entire income for this turn (including
// NOs)
final int currentPUs = m_player.getResources().getQuantity(PUs);
final float gainedPUS = Math.max(0, currentPUs - leftOverPUs);
int relationshipUpkeepCostFlat = 0;
int relationshipUpkeepCostPercentage = 0;
int relationshipUpkeepTotalCost = 0;
for (final Relationship r : data.getRelationshipTracker().getRelationships(m_player)) {
final String[] upkeep = r.getRelationshipType().getRelationshipTypeAttachment().getUpkeepCost().split(":");
if (upkeep.length == 1 || upkeep[1].equals(RelationshipTypeAttachment.UPKEEP_FLAT)) {
relationshipUpkeepCostFlat += Integer.parseInt(upkeep[0]);
} else if (upkeep[1].equals(RelationshipTypeAttachment.UPKEEP_PERCENTAGE)) {
relationshipUpkeepCostPercentage += Integer.parseInt(upkeep[0]);
}
}
relationshipUpkeepCostPercentage = Math.min(100, relationshipUpkeepCostPercentage);
if (relationshipUpkeepCostPercentage != 0) {
relationshipUpkeepTotalCost += Math.round(gainedPUS * (relationshipUpkeepCostPercentage) / 100f);
}
if (relationshipUpkeepCostFlat != 0) {
relationshipUpkeepTotalCost += relationshipUpkeepCostFlat;
}
// we can't remove more than we have, and we also must flip the sign
relationshipUpkeepTotalCost = Math.min(currentPUs, relationshipUpkeepTotalCost);
relationshipUpkeepTotalCost = -1 * relationshipUpkeepTotalCost;
if (relationshipUpkeepTotalCost != 0) {
final int newTotal = currentPUs + relationshipUpkeepTotalCost;
final String transcriptText2 = m_player.getName() + (relationshipUpkeepTotalCost < 0 ? " pays " : " taxes ")
+ (-1 * relationshipUpkeepTotalCost) + MyFormatter.pluralize(" PU", relationshipUpkeepTotalCost)
+ " in order to maintain current relationships with other players, and ends the turn with " + newTotal
+ MyFormatter.pluralize(" PU", newTotal);
m_bridge.getHistoryWriter().startEvent(transcriptText2);
endTurnReport.append("<br />").append(transcriptText2).append("<br />");
final Change upkeep = ChangeFactory.changeResourcesChange(m_player, PUs, relationshipUpkeepTotalCost);
m_bridge.addChange(upkeep);
}
}
if (GameStepPropertiesHelper.isRepairUnits(data)) {
MoveDelegate.repairMultipleHitPointUnits(m_bridge, m_bridge.getPlayerID());
}
if (isGiveUnitsByTerritory() && pa != null && pa.getGiveUnitControl() != null
&& !pa.getGiveUnitControl().isEmpty()) {
changeUnitOwnership(m_bridge);
}
m_needToInitialize = false;
showEndTurnReport(endTurnReport.toString());
}
protected void showEndTurnReport(final String endTurnReport) {
if (endTurnReport != null && endTurnReport.trim().length() > 6 && !m_player.isAI()) {
final ITripleAPlayer currentPlayer = getRemotePlayer(m_player);
final String player = m_player.getName();
currentPlayer.reportMessage("<html><b style=\"font-size:120%\" >" + END_TURN_REPORT_STRING + player
+ "</b><br /><br />" + endTurnReport + "</html>", END_TURN_REPORT_STRING + player);
}
}
/**
* Called before the delegate will stop running.
*/
@Override
public void end() {
super.end();
m_needToInitialize = true;
DelegateFinder.battleDelegate(getData()).getBattleTracker().clear();
}
@Override
public Serializable saveState() {
final EndTurnExtendedDelegateState state = new EndTurnExtendedDelegateState();
state.superState = super.saveState();
// add other variables to state here:
state.m_needToInitialize = m_needToInitialize;
state.m_hasPostedTurnSummary = m_hasPostedTurnSummary;
return state;
}
@Override
public void loadState(final Serializable state) {
final EndTurnExtendedDelegateState s = (EndTurnExtendedDelegateState) state;
super.loadState(s.superState);
m_needToInitialize = s.m_needToInitialize;
m_hasPostedTurnSummary = s.m_hasPostedTurnSummary;
}
@Override
public boolean delegateCurrentlyRequiresUserInput() {
// currently we need to call this regardless, because it resets player sounds for the turn.
return true;
}
private int rollWarBonds(final IDelegateBridge aBridge, final PlayerID player, final GameData data) {
final int count = TechAbilityAttachment.getWarBondDiceNumber(player, data);
final int sides = TechAbilityAttachment.getWarBondDiceSides(player, data);
if (sides <= 0 || count <= 0) {
return 0;
}
final String annotation = player.getName() + " rolling to resolve War Bonds: ";
DiceRoll dice;
dice = DiceRoll.rollNDice(aBridge, count, sides, player, DiceType.NONCOMBAT, annotation);
int total = 0;
for (int i = 0; i < dice.size(); i++) {
total += dice.getDie(i).getValue() + 1;
}
getRemotePlayer(player).reportMessage(annotation + MyFormatter.asDice(dice), annotation + MyFormatter.asDice(dice));
return total;
}
private String rollWarBondsForFriends(final IDelegateBridge aBridge, final PlayerID player, final GameData data) {
final int count = TechAbilityAttachment.getWarBondDiceNumber(player, data);
final int sides = TechAbilityAttachment.getWarBondDiceSides(player, data);
if (sides <= 0 || count <= 0) {
return "";
}
// basically, if we are sharing our technology with someone, and we have warbonds but they do not, then we roll our
// warbonds and give
// them the proceeds (Global 1940)
final PlayerAttachment playerattachment = PlayerAttachment.get(player);
if (playerattachment == null) {
return "";
}
final Collection<PlayerID> shareWith = playerattachment.getShareTechnology();
if (shareWith == null || shareWith.isEmpty()) {
return "";
}
// take first one
PlayerID giveWarBondsTo = null;
for (final PlayerID p : shareWith) {
final int pCount = TechAbilityAttachment.getWarBondDiceNumber(p, data);
final int pSides = TechAbilityAttachment.getWarBondDiceSides(p, data);
if (pSides <= 0 && pCount <= 0) {
// if both are zero, then it must mean we did not share our war bonds tech with them, even though we are sharing
// all tech (because
// they cannot have this tech)
if (canPlayerCollectIncome(p, data)) {
giveWarBondsTo = p;
break;
}
}
}
if (giveWarBondsTo == null) {
return "";
}
final String annotation =
player.getName() + " rolling to resolve War Bonds, and giving results to " + giveWarBondsTo.getName() + ": ";
final DiceRoll dice = DiceRoll.rollNDice(aBridge, count, sides, player, DiceType.NONCOMBAT, annotation);
int totalWarBonds = 0;
for (int i = 0; i < dice.size(); i++) {
totalWarBonds += dice.getDie(i).getValue() + 1;
}
final Resource PUs = data.getResourceList().getResource(Constants.PUS);
final int currentPUs = giveWarBondsTo.getResources().getQuantity(PUs);
final String transcriptText =
player.getName() + " rolls " + totalWarBonds + MyFormatter.pluralize(" PU", totalWarBonds)
+ " from War Bonds, giving the total to " + giveWarBondsTo.getName() + ", who ends with "
+ (currentPUs + totalWarBonds) + MyFormatter.pluralize(" PU", (currentPUs + totalWarBonds)) + " total";
aBridge.getHistoryWriter().startEvent(transcriptText);
final Change change = ChangeFactory.changeResourcesChange(giveWarBondsTo, PUs, totalWarBonds);
aBridge.addChange(change);
getRemotePlayer(player).reportMessage(annotation + MyFormatter.asDice(dice), annotation + MyFormatter.asDice(dice));
return transcriptText + "<br />";
}
private static void changeUnitOwnership(final IDelegateBridge aBridge) {
final PlayerID Player = aBridge.getPlayerID();
final PlayerAttachment pa = PlayerAttachment.get(Player);
final Collection<PlayerID> PossibleNewOwners = pa.getGiveUnitControl();
final Collection<Territory> territories = aBridge.getData().getMap().getTerritories();
final CompositeChange change = new CompositeChange();
final Collection<Tuple<Territory, Collection<Unit>>> changeList =
new ArrayList<>();
for (final Territory currTerritory : territories) {
final TerritoryAttachment ta = TerritoryAttachment.get(currTerritory);
// if ownership should change in this territory
if (ta != null && ta.getChangeUnitOwners() != null && !ta.getChangeUnitOwners().isEmpty()) {
final Collection<PlayerID> terrNewOwners = ta.getChangeUnitOwners();
for (final PlayerID terrNewOwner : terrNewOwners) {
if (PossibleNewOwners.contains(terrNewOwner)) {
// PlayerOwnerChange
final Collection<Unit> units =
currTerritory.getUnits().getMatches(new CompositeMatchAnd<>(Matches.unitOwnedBy(Player),
Matches.UnitCanBeGivenByTerritoryTo(terrNewOwner)));
if (!units.isEmpty()) {
change.add(ChangeFactory.changeOwner(units, terrNewOwner, currTerritory));
changeList.add(Tuple.of(currTerritory, units));
}
}
}
}
}
if (!change.isEmpty() && !changeList.isEmpty()) {
if (changeList.size() == 1) {
final Tuple<Territory, Collection<Unit>> tuple = changeList.iterator().next();
aBridge.getHistoryWriter().startEvent("Some Units in " + tuple.getFirst().getName() + " change ownership: "
+ MyFormatter.unitsToTextNoOwner(tuple.getSecond()), tuple.getSecond());
} else {
aBridge.getHistoryWriter().startEvent("Units Change Ownership");
for (final Tuple<Territory, Collection<Unit>> tuple : changeList) {
aBridge.getHistoryWriter().addChildToEvent("Some Units in " + tuple.getFirst().getName()
+ " change ownership: " + MyFormatter.unitsToTextNoOwner(tuple.getSecond()), tuple.getSecond());
}
}
aBridge.addChange(change);
}
}
protected abstract String addOtherResources(IDelegateBridge bridge);
protected abstract String doNationalObjectivesAndOtherEndTurnEffects(IDelegateBridge bridge);
protected int getProduction(final Collection<Territory> territories) {
return getProduction(territories, getData());
}
public static int getProduction(final Collection<Territory> territories, final GameData data) {
int value = 0;
for (final Territory current : territories) {
final TerritoryAttachment attachment = TerritoryAttachment.get(current);
if (attachment == null) {
throw new IllegalStateException("No attachment for owned territory:" + current.getName());
}
// Match will Check if territory is originally owned convoy center, or if it is contested
if (Matches.territoryCanCollectIncomeFrom(current.getOwner(), data).match(current)) {
value += attachment.getProduction();
}
}
return value;
}
// finds losses due to blockades, positive value returned.
protected int getBlockadeProductionLoss(final PlayerID player, final GameData data, final IDelegateBridge aBridge,
final StringBuilder endTurnReport) {
final PlayerAttachment playerRules = PlayerAttachment.get(player);
if (playerRules != null && playerRules.getImmuneToBlockade()) {
return 0;
}
final GameMap map = data.getMap();
final Collection<Territory> blockable = Match.getMatches(map.getTerritories(), Matches.territoryIsBlockadeZone);
if (blockable.isEmpty()) {
return 0;
}
final Match<Unit> enemyUnits = new CompositeMatchAnd<>(Matches.enemyUnit(player, data));
int totalLoss = 0;
final boolean rollDiceForBlockadeDamage = games.strategy.triplea.Properties.getConvoyBlockadesRollDiceForCost(data);
final Collection<String> transcripts = new ArrayList<>();
final HashMap<Territory, Tuple<Integer, List<Territory>>> damagePerBlockadeZone =
new HashMap<>();
boolean rolledDice = false;
for (final Territory b : blockable) {
// match will check for land, convoy zones, and also contested territories
final List<Territory> viableNeighbors =
Match.getMatches(map.getNeighbors(b), new CompositeMatchAnd<>(Matches.isTerritoryOwnedBy(player),
Matches.territoryCanCollectIncomeFrom(player, data)));
final int maxLoss = getProduction(viableNeighbors);
if (maxLoss <= 0) {
continue;
}
int loss = 0;
final Collection<Unit> enemies = Match.getMatches(b.getUnits().getUnits(), enemyUnits);
if (enemies.isEmpty()) {
continue;
}
if (rollDiceForBlockadeDamage) {
int numberOfDice = 0;
for (final Unit u : enemies) {
numberOfDice += UnitAttachment.get(u.getType()).getBlockade();
}
if (numberOfDice > 0) {
// there is an issue with maps that have lots of rolls without any pause between them: they are causing the
// cypted random source
// (ie: live and pbem games) to lock up or error out
// so we need to slow them down a bit, until we come up with a better solution (like aggregating all the
// chances together, then
// getting a ton of random numbers at once instead of one at a time)
ThreadUtil.sleep(100);
final String transcript = "Rolling for Convoy Blockade Damage in " + b.getName();
final int[] dice = aBridge.getRandom(CONVOY_BLOCKADE_DICE_SIDES, numberOfDice,
enemies.iterator().next().getOwner(), DiceType.BOMBING, transcript);
transcripts.add(transcript + ". Rolls: " + MyFormatter.asDice(dice));
rolledDice = true;
for (final int d : dice) {
// we are zero based
final int roll = d + 1;
loss += (roll <= 3 ? roll : 0);
}
}
} else {
for (final Unit u : enemies) {
loss += UnitAttachment.get(u.getType()).getBlockade();
}
}
if (loss <= 0) {
continue;
}
final int lossForBlockade = Math.min(maxLoss, loss);
damagePerBlockadeZone.put(b, Tuple.of(lossForBlockade, viableNeighbors));
totalLoss += lossForBlockade;
}
if (totalLoss <= 0 && !rolledDice) {
return 0;
}
// now we need to make sure that we didn't deal more damage than the territories are worth, in the case of having
// multiple sea zones
// touching the same land zone.
final List<Territory> blockadeZonesSorted = new ArrayList<>(damagePerBlockadeZone.keySet());
Collections.sort(blockadeZonesSorted, getSingleBlockadeThenHighestToLowestBlockadeDamage(damagePerBlockadeZone));
// we want to match highest damage to largest producer first, that is why we sort twice
final IntegerMap<Territory> totalDamageTracker = new IntegerMap<>();
for (final Territory b : blockadeZonesSorted) {
final Tuple<Integer, List<Territory>> tuple = damagePerBlockadeZone.get(b);
int damageForZone = tuple.getFirst();
final List<Territory> terrsLosingIncome = new ArrayList<>(tuple.getSecond());
Collections.sort(terrsLosingIncome,
getSingleNeighborBlockadesThenHighestToLowestProduction(blockadeZonesSorted, map));
final Iterator<Territory> iter = terrsLosingIncome.iterator();
while (damageForZone > 0 && iter.hasNext()) {
final Territory t = iter.next();
final int maxProductionLessPreviousDamage = TerritoryAttachment.getProduction(t) - totalDamageTracker.getInt(t);
final int damageToTerr = Math.min(damageForZone, maxProductionLessPreviousDamage);
damageForZone -= damageToTerr;
totalDamageTracker.put(t, damageToTerr + totalDamageTracker.getInt(t));
}
}
final int realTotalLoss = Math.max(0, totalDamageTracker.totalValues());
if (rollDiceForBlockadeDamage && (realTotalLoss > 0 || (rolledDice && !transcripts.isEmpty()))) {
final String mainline = "Total Cost from Convoy Blockades: " + realTotalLoss;
aBridge.getHistoryWriter().startEvent(mainline);
endTurnReport.append(mainline).append("<br />");
for (final String t : transcripts) {
aBridge.getHistoryWriter().addChildToEvent(t);
endTurnReport.append("* ").append(t).append("<br />");
}
endTurnReport.append("<br />");
}
return realTotalLoss;
}
@Override
public void setHasPostedTurnSummary(final boolean hasPostedTurnSummary) {
m_hasPostedTurnSummary = hasPostedTurnSummary;
}
@Override
public boolean getHasPostedTurnSummary() {
return m_hasPostedTurnSummary;
}
@Override
public boolean postTurnSummary(final PBEMMessagePoster poster, final String title, final boolean includeSaveGame) {
m_hasPostedTurnSummary = poster.post(m_bridge.getHistoryWriter(), title, includeSaveGame);
return m_hasPostedTurnSummary;
}
@Override
public String getName() {
return m_name;
}
@Override
public String getDisplayName() {
return m_displayName;
}
@Override
public Class<? extends IRemote> getRemoteType() {
return IAbstractForumPosterDelegate.class;
}
private static Comparator<Territory> getSingleNeighborBlockadesThenHighestToLowestProduction(
final Collection<Territory> blockadeZones, final GameMap map) {
return (t1, t2) -> {
if (t1 == t2 || (t1 == null && t2 == null)) {
return 0;
}
if (t1 == null) {
return 1;
}
if (t2 == null) {
return -1;
}
if (t1.equals(t2)) {
return 0;
}
// if a territory is only touching 1 blockadeZone, we must take it first
final Collection<Territory> neighborBlockades1 = new ArrayList<>(map.getNeighbors(t1));
neighborBlockades1.retainAll(blockadeZones);
final int n1 = neighborBlockades1.size();
final Collection<Territory> neighborBlockades2 = new ArrayList<>(map.getNeighbors(t2));
neighborBlockades2.retainAll(blockadeZones);
final int n2 = neighborBlockades2.size();
if (n1 == 1 && n2 != 1) {
return -1;
}
if (n2 == 1 && n1 != 1) {
return 1;
}
final TerritoryAttachment ta1 = TerritoryAttachment.get(t1);
final TerritoryAttachment ta2 = TerritoryAttachment.get(t2);
if (ta1 == null && ta2 == null) {
return 0;
}
if (ta1 == null) {
return 1;
}
if (ta2 == null) {
return -1;
}
final int p1 = ta1.getProduction();
final int p2 = ta2.getProduction();
if (p1 == p2) {
return 0;
}
if (p1 > p2) {
return -1;
}
return 1;
};
}
private static Comparator<Territory> getSingleBlockadeThenHighestToLowestBlockadeDamage(
final HashMap<Territory, Tuple<Integer, List<Territory>>> damagePerBlockadeZone) {
return (t1, t2) -> {
if (t1 == t2 || (t1 == null && t2 == null)) {
return 0;
}
if (t1 == null) {
return 1;
}
if (t2 == null) {
return -1;
}
if (t1.equals(t2)) {
return 0;
}
final Tuple<Integer, List<Territory>> tuple1 = damagePerBlockadeZone.get(t1);
final Tuple<Integer, List<Territory>> tuple2 = damagePerBlockadeZone.get(t2);
final int num1 = tuple1.getSecond().size();
final int num2 = tuple2.getSecond().size();
if (num1 == 1 && num2 != 1) {
return -1;
}
if (num2 == 1 && num1 != 1) {
return 1;
}
final int d1 = tuple1.getFirst();
final int d2 = tuple2.getFirst();
if (d1 == d2) {
return 0;
}
if (d1 > d2) {
return -1;
}
return 1;
};
}
}
class EndTurnExtendedDelegateState implements Serializable {
private static final long serialVersionUID = -3939461840835898284L;
Serializable superState;
// add other variables here:
public boolean m_needToInitialize;
public boolean m_hasPostedTurnSummary;
}