package games.strategy.triplea.delegate;
import java.io.Serializable;
import java.util.Collection;
import java.util.Iterator;
import games.strategy.engine.data.Change;
import games.strategy.engine.data.CompositeChange;
import games.strategy.engine.data.GameData;
import games.strategy.engine.data.GameStep;
import games.strategy.engine.data.NamedAttachable;
import games.strategy.engine.data.PlayerID;
import games.strategy.engine.data.ProductionFrontier;
import games.strategy.engine.data.ProductionRule;
import games.strategy.engine.data.Resource;
import games.strategy.engine.data.Territory;
import games.strategy.engine.data.Unit;
import games.strategy.engine.data.UnitType;
import games.strategy.engine.data.changefactory.ChangeFactory;
import games.strategy.engine.delegate.IDelegateBridge;
import games.strategy.engine.message.IRemote;
import games.strategy.triplea.Constants;
import games.strategy.triplea.TripleAUnit;
import games.strategy.triplea.attachments.TerritoryAttachment;
import games.strategy.triplea.attachments.UnitAttachment;
import games.strategy.util.IntegerMap;
import games.strategy.util.Match;
/**
* This delegate is only supposed to be run once, per game, at the start of the game.
*/
public class InitializationDelegate extends BaseTripleADelegate {
private boolean m_needToInitialize = true;
/** Creates a new instance of InitializationDelegate. */
public InitializationDelegate() {}
@Override
public void initialize(final String name, final String displayName) {
m_name = name;
m_displayName = displayName;
}
/**
* Called before the delegate will run.
*/
@Override
public void start() {
super.start();
if (m_needToInitialize) {
init(m_bridge);
m_needToInitialize = false;
}
}
@Override
public void end() {
super.end();
}
@Override
public Serializable saveState() {
final InitializationExtendedDelegateState state = new InitializationExtendedDelegateState();
state.superState = super.saveState();
// add other variables to state here:
state.m_needToInitialize = this.m_needToInitialize;
return state;
}
@Override
public void loadState(final Serializable state) {
final InitializationExtendedDelegateState s = (InitializationExtendedDelegateState) state;
super.loadState(s.superState);
this.m_needToInitialize = s.m_needToInitialize;
}
@Override
public boolean delegateCurrentlyRequiresUserInput() {
return false;
}
protected void init(final IDelegateBridge aBridge) {
initDestroyerArtillery(aBridge);
initShipyards(aBridge);
initTwoHitBattleship(aBridge);
initOriginalOwner(aBridge);
initTech(aBridge);
initSkipUnusedBids(aBridge.getData());
initDeleteAssetsOfDisabledPlayers(aBridge);
initTransportedLandUnits(aBridge);
resetUnitState(aBridge);
}
/**
* The initTransportedLandUnits has some side effects, and we need to reset unit state to get rid of them.
*/
private void resetUnitState(final IDelegateBridge aBridge) {
final Change change = MoveDelegate.getResetUnitStateChange(getData());
if (!change.isEmpty()) {
m_bridge.getHistoryWriter().startEvent("Cleaning up unit state.");
m_bridge.addChange(change);
}
}
/**
* Want to make sure that all units in the sea that can be transported are
* marked as being transported by something.
* We assume that all transportable units in the sea are in a transport, no
* exceptions.
*/
private void initTransportedLandUnits(final IDelegateBridge aBridge) {
final GameData data = aBridge.getData();
// check every territory
boolean historyItemCreated = false;
final Iterator<Territory> allTerritories = data.getMap().getTerritories().iterator();
while (allTerritories.hasNext()) {
final Territory current = allTerritories.next();
// only care about water
if (!current.isWater()) {
continue;
}
final Collection<Unit> units = current.getUnits().getUnits();
if (units.size() == 0 || !Match.someMatch(units, Matches.UnitIsLand)) {
continue;
}
// map transports, try to fill
final Collection<Unit> transports = Match.getMatches(units, Matches.UnitIsTransport);
final Collection<Unit> land = Match.getMatches(units, Matches.UnitIsLand);
for (final Unit toLoad : land) {
final UnitAttachment ua = UnitAttachment.get(toLoad.getType());
final int cost = ua.getTransportCost();
if (cost == -1) {
throw new IllegalStateException("Non transportable unit in sea");
}
// find the next transport that can hold it
final Iterator<Unit> transportIter = transports.iterator();
boolean found = false;
while (transportIter.hasNext()) {
final Unit transport = transportIter.next();
final int capacity = TransportTracker.getAvailableCapacity(transport);
if (capacity >= cost) {
if (!historyItemCreated) {
aBridge.getHistoryWriter().startEvent("Initializing Units in Transports");
historyItemCreated = true;
}
try {
aBridge.addChange(TransportTracker.loadTransportChange((TripleAUnit) transport, toLoad));
} catch (final IllegalStateException e) {
System.err.println(
"You can only edit add transports+units after the initialization delegate of the game is finished. "
+ "If this error came up and you have not used Edit Mode to add units + transports, then please "
+ "report this as a bug: \r\n" + e.getMessage());
}
found = true;
break;
}
}
if (!found) {
throw new IllegalStateException(
"Cannot load all land units in sea transports. " + "Please make sure you have enough transports. "
+ "You may need to re-order the xml's placement of transports and land units, "
+ "as the engine will try to fill them in the order they are given.");
}
}
}
}
private void initDeleteAssetsOfDisabledPlayers(final IDelegateBridge aBridge) {
final GameData data = aBridge.getData();
if (!games.strategy.triplea.Properties.getDisabledPlayersAssetsDeleted(data)) {
return;
}
for (final PlayerID player : data.getPlayerList().getPlayers()) {
if (player.isNull() || !player.getIsDisabled()) {
continue;
}
// delete all the stuff they have
final CompositeChange change = new CompositeChange();
for (final Resource r : player.getResources().getResourcesCopy().keySet()) {
final int deleted = player.getResources().getQuantity(r);
if (deleted != 0) {
change.add(ChangeFactory.changeResourcesChange(player, r, -deleted));
}
}
final Collection<Unit> heldUnits = player.getUnits().getUnits();
if (!heldUnits.isEmpty()) {
change.add(ChangeFactory.removeUnits(player, heldUnits));
}
final Match<Unit> owned = Matches.unitIsOwnedBy(player);
for (final Territory t : data.getMap().getTerritories()) {
final Collection<Unit> terrUnits = t.getUnits().getMatches(owned);
if (!terrUnits.isEmpty()) {
change.add(ChangeFactory.removeUnits(t, terrUnits));
}
}
if (!change.isEmpty()) {
aBridge.getHistoryWriter().startEvent("Remove all resources and units from: " + player.getName());
aBridge.addChange(change);
}
}
}
private void initSkipUnusedBids(final GameData data) {
// we have a lot of bid steps, 12 for pact of steel
// in multi player this can be time consuming, since each vm
// must be notified (and have its ui) updated for each step,
// so remove the bid steps that arent used
for (final GameStep step : data.getSequence()) {
if (step.getDelegate() instanceof BidPlaceDelegate || step.getDelegate() instanceof BidPurchaseDelegate) {
if (!BidPurchaseDelegate.doesPlayerHaveBid(data, step.getPlayerID())) {
step.setMaxRunCount(0);
}
}
}
}
private void initTech(final IDelegateBridge bridge) {
final GameData data = bridge.getData();
final Iterator<PlayerID> players = data.getPlayerList().getPlayers().iterator();
while (players.hasNext()) {
final PlayerID player = players.next();
final Iterator<TechAdvance> advances = TechTracker.getCurrentTechAdvances(player, data).iterator();
if (advances.hasNext()) {
bridge.getHistoryWriter().startEvent("Initializing " + player.getName() + " with tech advances");
while (advances.hasNext()) {
final TechAdvance advance = advances.next();
advance.perform(player, bridge);
}
}
}
}
private void initDestroyerArtillery(final IDelegateBridge aBridge) {
final GameData data = aBridge.getData();
final boolean addArtilleryAndDestroyers = games.strategy.triplea.Properties.getUse_Destroyers_And_Artillery(data);
if (!isWW2V2(data) && addArtilleryAndDestroyers) {
final CompositeChange change = new CompositeChange();
final ProductionRule artillery = data.getProductionRuleList().getProductionRule("buyArtillery");
final ProductionRule destroyer = data.getProductionRuleList().getProductionRule("buyDestroyer");
final ProductionFrontier frontier = data.getProductionFrontierList().getProductionFrontier("production");
if (artillery != null && !frontier.getRules().contains(artillery)) {
change.add(ChangeFactory.addProductionRule(artillery, frontier));
}
if (destroyer != null && !frontier.getRules().contains(destroyer)) {
change.add(ChangeFactory.addProductionRule(destroyer, frontier));
}
final ProductionRule artilleryIT =
data.getProductionRuleList().getProductionRule("buyArtilleryIndustrialTechnology");
final ProductionRule destroyerIT =
data.getProductionRuleList().getProductionRule("buyDestroyerIndustrialTechnology");
final ProductionFrontier frontierIT =
data.getProductionFrontierList().getProductionFrontier("productionIndustrialTechnology");
if (artilleryIT != null && !frontierIT.getRules().contains(artilleryIT)) {
change.add(ChangeFactory.addProductionRule(artilleryIT, frontierIT));
}
if (destroyerIT != null && !frontierIT.getRules().contains(destroyerIT)) {
change.add(ChangeFactory.addProductionRule(destroyerIT, frontierIT));
}
if (!change.isEmpty()) {
aBridge.getHistoryWriter().startEvent("Adding destroyers and artillery production rules");
aBridge.addChange(change);
}
}
}
private void initShipyards(final IDelegateBridge aBridge) {
final GameData data = aBridge.getData();
final boolean useShipyards = games.strategy.triplea.Properties.getUse_Shipyards(data);
if (useShipyards) {
final CompositeChange change = new CompositeChange();
final ProductionFrontier frontierShipyards =
data.getProductionFrontierList().getProductionFrontier("productionShipyards");
/*
* Find the productionRules, if the unit is NOT a sea unit, add it to the ShipYards prod rule.
*/
final ProductionFrontier frontierNONShipyards =
data.getProductionFrontierList().getProductionFrontier("production");
final Collection<ProductionRule> rules = frontierNONShipyards.getRules();
for (final ProductionRule rule : rules) {
final String ruleName = rule.getName();
final IntegerMap<NamedAttachable> ruleResults = rule.getResults();
final NamedAttachable named = ruleResults.keySet().iterator().next();
if (!(named instanceof UnitType)) {
continue;
}
final UnitType unit = data.getUnitTypeList().getUnitType(named.getName());
final boolean isSea = UnitAttachment.get(unit).getIsSea();
if (!isSea) {
final ProductionRule prodRule = data.getProductionRuleList().getProductionRule(ruleName);
change.add(ChangeFactory.addProductionRule(prodRule, frontierShipyards));
}
}
aBridge.getHistoryWriter().startEvent("Adding shipyard production rules - land/air units");
aBridge.addChange(change);
}
}
private boolean isWW2V2(final GameData data) {
return games.strategy.triplea.Properties.getWW2V2(data);
}
private void initTwoHitBattleship(final IDelegateBridge aBridge) {
final GameData data = aBridge.getData();
final boolean userEnabled = games.strategy.triplea.Properties.getTwoHitBattleships(data);
final UnitType battleShipUnit = data.getUnitTypeList().getUnitType(Constants.UNIT_TYPE_BATTLESHIP);
if (battleShipUnit == null) {
return;
}
final UnitAttachment battleShipAttachment = UnitAttachment.get(battleShipUnit);
final boolean defaultEnabled = battleShipAttachment.getHitPoints() > 1;
if (userEnabled != defaultEnabled) {
aBridge.getHistoryWriter().startEvent("TwoHitBattleships:" + userEnabled);
aBridge.addChange(ChangeFactory.attachmentPropertyChange(battleShipAttachment, userEnabled ? 2 : 1, "hitPoints"));
}
}
private void initOriginalOwner(final IDelegateBridge aBridge) {
final GameData data = aBridge.getData();
final CompositeChange changes = new CompositeChange();
for (final Territory current : data.getMap()) {
if (!current.getOwner().isNull()) {
final TerritoryAttachment territoryAttachment = TerritoryAttachment.get(current);
if (territoryAttachment == null) {
throw new IllegalStateException("No territory attachment for " + current);
}
if (territoryAttachment.getOriginalOwner() == null && current.getOwner() != null) {
changes.add(OriginalOwnerTracker.addOriginalOwnerChange(current, current.getOwner()));
}
final Collection<Unit> factoryAndInfrastructure = current.getUnits().getMatches(Matches.UnitIsInfrastructure);
changes.add(OriginalOwnerTracker.addOriginalOwnerChange(factoryAndInfrastructure, current.getOwner()));
} else if (!current.isWater()) {
final TerritoryAttachment territoryAttachment = TerritoryAttachment.get(current);
if (territoryAttachment == null) {
throw new IllegalStateException("No territory attachment for " + current);
}
}
}
aBridge.getHistoryWriter().startEvent("Adding original owners");
aBridge.addChange(changes);
}
@Override
public Class<? extends IRemote> getRemoteType() {
return null;
}
}
class InitializationExtendedDelegateState implements Serializable {
private static final long serialVersionUID = -9000446777655823735L;
Serializable superState;
public boolean m_needToInitialize;
}