2
* This program is free software; you can redistribute it and/or modify
3
* it under the terms of the GNU General Public License as published by
4
* the Free Software Foundation; either version 2 of the License, or
5
* (at your option) any later version.
6
* This program is distributed in the hope that it will be useful,
7
* but WITHOUT ANY WARRANTY; without even the implied warranty of
8
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9
* GNU General Public License for more details.
10
* You should have received a copy of the GNU General Public License
11
* along with this program; if not, write to the Free Software
12
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
* Created on November 2, 2001, 8:45 PM
21
package games.strategy.triplea;
23
import games.strategy.common.player.AbstractHumanPlayer;
24
import games.strategy.engine.data.PlayerID;
25
import games.strategy.engine.data.ProductionRule;
26
import games.strategy.engine.data.RepairRule;
27
import games.strategy.engine.data.Territory;
28
import games.strategy.engine.data.Unit;
29
import games.strategy.engine.gamePlayer.IGamePlayer;
30
import games.strategy.net.GUID;
31
import games.strategy.triplea.attatchments.PlayerAttachment;
32
import games.strategy.triplea.attatchments.TerritoryAttachment;
33
import games.strategy.triplea.delegate.BidPurchaseDelegate;
34
import games.strategy.triplea.delegate.DiceRoll;
35
import games.strategy.triplea.delegate.Matches;
36
import games.strategy.triplea.delegate.dataObjects.BattleListing;
37
import games.strategy.triplea.delegate.dataObjects.CasualtyDetails;
38
import games.strategy.triplea.delegate.dataObjects.FightBattleDetails;
39
import games.strategy.triplea.delegate.dataObjects.MoveDescription;
40
import games.strategy.triplea.delegate.dataObjects.TechResults;
41
import games.strategy.triplea.delegate.dataObjects.TechRoll;
42
import games.strategy.triplea.delegate.remote.IAbstractPlaceDelegate;
43
import games.strategy.triplea.delegate.remote.IBattleDelegate;
44
import games.strategy.triplea.delegate.remote.IMoveDelegate;
45
import games.strategy.triplea.delegate.remote.IPurchaseDelegate;
46
import games.strategy.triplea.delegate.remote.ITechDelegate;
47
import games.strategy.triplea.delegate.remote.IEditDelegate;
48
import games.strategy.triplea.formatter.MyFormatter;
49
import games.strategy.triplea.player.ITripleaPlayer;
50
import games.strategy.triplea.ui.BattleDisplay;
51
import games.strategy.triplea.ui.PlaceData;
52
import games.strategy.triplea.ui.TripleAFrame;
53
import games.strategy.util.CompositeMatchAnd;
54
import games.strategy.util.IntegerMap;
55
import games.strategy.util.InverseMatch;
56
import games.strategy.util.Match;
58
import games.strategy.engine.data.GameData;
59
import java.awt.event.ActionEvent;
60
import java.util.ArrayList;
61
import java.util.Collection;
62
import java.util.HashMap;
63
import java.util.Iterator;
64
import java.util.List;
66
import javax.swing.AbstractAction;
67
import javax.swing.ButtonModel;
68
import javax.swing.SwingUtilities;
72
* @author Sean Bridges
75
public class TripleAPlayer extends AbstractHumanPlayer<TripleAFrame> implements IGamePlayer, ITripleaPlayer
77
/** Creates new TripleAPlayer */
78
public TripleAPlayer(String name)
83
public void reportError(String error)
85
m_ui.notifyError(error);
88
public void reportMessage(String message)
90
m_ui.notifyMessage(message);
93
public void start(String name)
95
boolean badStep = false;
99
m_ui.setEditDelegate((IEditDelegate)m_bridge.getRemote("edit"));
105
SwingUtilities.invokeLater( new Runnable() {
108
m_ui.getEditModeButtonModel().addActionListener(m_editModeAction);
109
m_ui.getEditModeButtonModel().setEnabled(true);
114
if (name.endsWith("Bid"))
116
else if (name.endsWith("Tech"))
118
else if (name.endsWith("TechActivation"))
119
{} // the delegate handles everything
120
else if (name.endsWith("Purchase"))
122
else if (name.endsWith("Move"))
123
move(name.endsWith("NonCombatMove"));
124
else if (name.endsWith("Battle"))
126
else if (name.endsWith("Place"))
127
place(name.indexOf("Bid") != -1);
128
else if (name.endsWith("EndTurn"))
134
SwingUtilities.invokeLater( new Runnable() {
137
m_ui.getEditModeButtonModel().setEnabled(false);
138
m_ui.getEditModeButtonModel().removeActionListener(m_editModeAction);
139
m_ui.setEditDelegate(null);
145
throw new IllegalArgumentException("Unrecognized step name:" + name);
148
private AbstractAction m_editModeAction = new AbstractAction()
150
public void actionPerformed(ActionEvent ae)
152
boolean editMode = ((ButtonModel)ae.getSource()).isSelected();
156
// All GameDataChangeListeners will be notified upon success
157
IEditDelegate editDelegate = (IEditDelegate) m_bridge.getRemote("edit");
158
editDelegate.setEditMode(editMode);
163
// toggle back to previous state since setEditMode failed
164
m_ui.getEditModeButtonModel().setSelected(!m_ui.getEditModeButtonModel().isSelected());
172
m_bridge.getGameData().acquireReadLock();
175
if (m_id.getResources().getQuantity(Constants.PUS) == 0 || !isTechDevelopment(m_bridge.getGameData()))
180
m_bridge.getGameData().releaseReadLock();
183
TechRoll techRoll = m_ui.getTechRolls(m_id);
184
if (techRoll != null)
186
ITechDelegate techDelegate = (ITechDelegate) m_bridge.getRemote();
187
TechResults techResults = techDelegate.rollTech(
188
techRoll.getRolls(), techRoll.getTech(), techRoll.getNewTokens());
190
if (techResults.isError())
192
m_ui.notifyError(techResults.getErrorString());
195
m_ui.notifyTechResults(techResults);
199
private void move(boolean nonCombat)
201
if (!m_scrambledUnitsReturned && nonCombat && getScramble_Rules_In_Effect())
203
m_scrambledUnitsReturned = true;
204
CompositeMatchAnd<Unit> unitWasScrambled = new CompositeMatchAnd<Unit>();
205
unitWasScrambled.add(Matches.UnitWasScrambled);
207
Collection<Unit> scrambledUnits = new ArrayList<Unit>();
210
for(Territory t : m_bridge.getGameData().getMap().getTerritories())
212
scrambledUnits.addAll(t.getUnits().getMatches(unitWasScrambled));
215
//units were scrambled, move them
216
if(!scrambledUnits.isEmpty())
217
returnScrambledUnits(scrambledUnits);
220
if (!hasUnitsThatCanMove(nonCombat))
223
MoveDescription moveDescription = m_ui.getMove(m_id, m_bridge, nonCombat);
224
if (moveDescription == null)
228
if(!canAirLand(true, m_id))
239
IMoveDelegate moveDel = (IMoveDelegate) m_bridge.getRemote();
240
String error = moveDel.move(moveDescription.getUnits(), moveDescription.getRoute(), moveDescription.getTransportsThatCanBeLoaded());
243
m_ui.notifyError(error);
247
private void returnScrambledUnits(Collection<Unit> scrambledUnits)
249
// find only players with units in the collection
250
Collection <PlayerID> players = new ArrayList<PlayerID>();
251
for(Unit unit : scrambledUnits)
253
if(!players.contains(unit.getOwner()))
254
players.add(unit.getOwner());
257
for(PlayerID player : players)
259
MoveDescription moveDescription = m_ui.getMove(player, m_bridge, true);
260
if (moveDescription == null)
262
if(!canAirLand(true, player))
267
IMoveDelegate moveDel = (IMoveDelegate) m_bridge.getRemote();
268
String error = moveDel.move(moveDescription.getUnits(), moveDescription.getRoute());
271
m_ui.notifyError(error);
273
//get just the player's units
274
Collection <Unit> playerUnits = new ArrayList<Unit>();
275
playerUnits.addAll(Match.getMatches(scrambledUnits, Matches.unitIsOwnedBy(player)));
277
//if they haven't all been moved- do it again
278
if(Match.someMatch(playerUnits, Matches.UnitWasScrambled))
280
returnScrambledUnits(scrambledUnits);
287
private boolean canAirLand(boolean movePhase, PlayerID player)
290
Collection<Territory> airCantLand;
292
airCantLand = ((IMoveDelegate) m_bridge.getRemote()).getTerritoriesWhereAirCantLand(player);
294
airCantLand = ((IAbstractPlaceDelegate) m_bridge.getRemote()).getTerritoriesWhereAirCantLand();
296
if (airCantLand.isEmpty())
300
StringBuilder buf = new StringBuilder(
301
"Air in following territories cant land:");
302
Iterator<Territory> iter = airCantLand.iterator();
303
while (iter.hasNext())
305
buf.append(((Territory) iter.next()).getName());
308
if (!m_ui.getOKToLetAirDie(m_id, buf.toString(), movePhase))
314
private boolean canUnitsFight()
316
Collection<Territory> unitsCantFight;
318
unitsCantFight = ((IMoveDelegate) m_bridge.getRemote()).getTerritoriesWhereUnitsCantFight();
321
if (unitsCantFight.isEmpty())
325
StringBuilder buf = new StringBuilder(
326
"Units in the following territories will die:");
327
Iterator<Territory> iter = unitsCantFight.iterator();
328
while (iter.hasNext())
330
buf.append(((Territory) iter.next()).getName());
333
if (m_ui.getOKToLetUnitsDie(m_id, buf.toString(), true))
338
private boolean hasUnitsThatCanMove(boolean nonCom)
340
CompositeMatchAnd<Unit> moveableUnitOwnedByMe = new CompositeMatchAnd<Unit>();
341
moveableUnitOwnedByMe.add(Matches.unitIsOwnedBy(m_id));
342
moveableUnitOwnedByMe.add(Matches.UnitIsNotStatic(m_id));
343
//if not non combat, can not move aa units
345
moveableUnitOwnedByMe.add(new InverseMatch<Unit>(Matches.UnitIsAAorIsAAmovement));
347
m_bridge.getGameData().acquireReadLock();
350
Iterator<Territory> territoryIter = m_bridge.getGameData().getMap()
351
.getTerritories().iterator();
352
while (territoryIter.hasNext())
354
Territory item = (Territory) territoryIter.next();
355
if (item.getUnits().someMatch(moveableUnitOwnedByMe))
364
m_bridge.getGameData().releaseReadLock();
369
private void purchase(boolean bid)
373
if(!BidPurchaseDelegate.doesPlayerHaveBid(m_bridge.getGameData(), m_id))
377
// NoPUPurchaseDelegate will run first, before this section. After running, the engine will wait for the player by coming here. Exit if we are a No PU delegate purchase
378
if (this.m_bridge.getStepName().endsWith("NoPUPurchase"))
381
//we have no production frontier
382
else if(m_id.getProductionFrontier() == null || m_id.getProductionFrontier().getRules().isEmpty())
388
//if my capital is captured, I can't produce, but I may have PUs if I captured someone else's capital
389
List<Territory> capitalsListOriginal = new ArrayList<Territory>(TerritoryAttachment.getAllCapitals(m_id, m_bridge.getGameData()));
390
List<Territory> capitalsListOwned = new ArrayList<Territory>(TerritoryAttachment.getAllCurrentlyOwnedCapitals(m_id, m_bridge.getGameData()));
391
PlayerAttachment pa = PlayerAttachment.get(m_id);
392
if ((!capitalsListOriginal.isEmpty() && capitalsListOwned.isEmpty()) || (pa != null && pa.getRetainCapitalProduceNumber() > capitalsListOwned.size()))
395
int minPUsNeededToBuild = Integer.MAX_VALUE;
396
m_bridge.getGameData().acquireReadLock();
399
Iterator<ProductionRule> prodRules = m_id.getProductionFrontier().getRules().iterator();
400
while(prodRules.hasNext())
402
ProductionRule rule = (ProductionRule) prodRules.next();
403
minPUsNeededToBuild = Math.min(rule.getCosts().getInt(m_bridge.getGameData().getResourceList().getResource(Constants.PUS)), minPUsNeededToBuild);
406
if(m_id.getRepairFrontier() != null)
408
Iterator<RepairRule> repairRules = m_id.getRepairFrontier().getRules().iterator();
409
while(repairRules.hasNext())
411
RepairRule rule = (RepairRule) repairRules.next();
412
minPUsNeededToBuild = Math.min(rule.getCosts().getInt(m_bridge.getGameData().getResourceList().getResource(Constants.PUS)), minPUsNeededToBuild);
416
//can we buy anything
417
if (m_id.getResources().getQuantity(Constants.PUS) < minPUsNeededToBuild)
422
m_bridge.getGameData().releaseReadLock();
426
//Check if any factories need to be repaired
428
IPurchaseDelegate purchaseDel = (IPurchaseDelegate) m_bridge.getRemote();
430
if(isSBRAffectsUnitProduction(m_bridge.getGameData()))
432
GameData data = m_bridge.getGameData();
433
Collection<Territory> bombedTerrs = new ArrayList<Territory>();
434
for(Territory t : Match.getMatches(data.getMap().getTerritories(), Matches.territoryHasOwnedIsFactoryOrCanProduceUnits(data, m_id)))
436
TerritoryAttachment ta = TerritoryAttachment.get(t);
437
//changed this to > from !=
438
if(ta.getProduction() > ta.getUnitProduction())
444
Collection<Unit> damagedUnits = new ArrayList<Unit>();
445
Match<Unit> myFactories = new CompositeMatchAnd<Unit>(Matches.unitIsOwnedBy(m_id), Matches.UnitIsFactoryOrCanBeDamaged);
447
for (Territory t : bombedTerrs)
449
damagedUnits.addAll(Match.getMatches(t.getUnits().getUnits(), myFactories));
452
if(bombedTerrs.size() > 0 && damagedUnits.size() > 0)
454
HashMap<Unit, IntegerMap<RepairRule>> repair = m_ui.getRepair(m_id, bid);
457
purchaseDel = (IPurchaseDelegate) m_bridge.getRemote();
458
error = purchaseDel.purchaseRepair(repair);
461
m_ui.notifyError(error);
462
//dont give up, keep going
469
else if (isDamageFromBombingDoneToUnitsInsteadOfTerritories(m_bridge.getGameData()))
471
GameData data = m_bridge.getGameData();
472
Match<Unit> myDamaged = new CompositeMatchAnd<Unit>(Matches.unitIsOwnedBy(m_id), Matches.UnitHasSomeUnitDamage());
473
Collection<Unit> damagedUnits = new ArrayList<Unit>();
474
for (Territory t : data.getMap().getTerritories())
476
damagedUnits.addAll(Match.getMatches(t.getUnits().getUnits(), myDamaged));
479
if(damagedUnits.size() > 0)
481
HashMap<Unit, IntegerMap<RepairRule>> repair = m_ui.getRepair(m_id, bid);
484
purchaseDel = (IPurchaseDelegate) m_bridge.getRemote(); //TODO: veq fix
485
error = purchaseDel.purchaseRepair(repair);
488
m_ui.notifyError(error);
489
//dont give up, keep going
496
IntegerMap<ProductionRule> prod = m_ui.getProduction(m_id, bid);
499
purchaseDel = (IPurchaseDelegate) m_bridge.getRemote();
500
error = purchaseDel.purchase(prod);
504
m_ui.notifyError(error);
505
//dont give up, keep going
512
private void battle()
516
IBattleDelegate battleDel = (IBattleDelegate) m_bridge.getRemote();
517
BattleListing battles = battleDel.getBattles();
520
if (battles.isEmpty())
525
FightBattleDetails details = m_ui.getBattle(m_id, battles
526
.getBattles(), battles.getStrategicRaids());
528
if(m_bridge.isGameOver())
531
String error = battleDel.fightBattle(details.getWhere(), details.isBombingRaid());
534
m_ui.notifyError(error);
538
private void place(boolean bid)
540
IAbstractPlaceDelegate placeDel = (IAbstractPlaceDelegate) m_bridge.getRemote();
543
if (m_id.getUnits().size() == 0 && placeDel.getPlacementsMade() == 0)
548
PlaceData data = m_ui.waitForPlace(m_id ,bid, m_bridge);
551
//this only happens in lhtr rules
552
if(canAirLand(false, m_id))
558
String error = placeDel.placeUnits(data.getUnits(), data.getAt());
560
m_ui.notifyError(error);
564
private void endTurn()
566
m_ui.waitForEndTurn(m_id, m_bridge);
571
* @see games.strategy.triplea.player.ITripleaPlayer#selectCasualties(java.lang.String, java.util.Collection, java.util.Map, int, java.lang.String, games.strategy.triplea.delegate.DiceRoll, games.strategy.engine.data.PlayerID, java.util.List)
574
public CasualtyDetails selectCasualties(Collection<Unit> selectFrom, Map<Unit, Collection<Unit>> dependents, int count, String message, DiceRoll dice, PlayerID hit, List<Unit> defaultCasualties, GUID battleID)
576
return m_ui.getBattlePanel().getCasualties(selectFrom, dependents, count, message, dice,hit, defaultCasualties, battleID);
580
* @see games.strategy.triplea.player.ITripleaPlayer#selectFixedDice(int, int, boolean, java.lang.String)
582
public int[] selectFixedDice(int numDice, int hitAt, boolean hitOnlyIfEquals, String title, int diceSides)
584
return m_ui.selectFixedDice(numDice, hitAt, hitOnlyIfEquals, title, diceSides);
588
* @see games.strategy.triplea.player.ITripleaPlayer#selectBombardingTerritory(games.strategy.engine.data.Unit, games.strategy.engine.data.Territory, java.util.Collection, boolean)
590
public Territory selectBombardingTerritory(Unit unit, Territory unitTerritory, Collection<Territory> territories, boolean noneAvailable)
592
return m_ui.getBattlePanel().getBombardment(unit, unitTerritory, territories, noneAvailable);
596
* Ask if the player wants to attack subs
598
public boolean selectAttackSubs(Territory unitTerritory)
600
return m_ui.getBattlePanel().getAttackSubs(unitTerritory);
604
* Ask if the player wants to attack transports
606
public boolean selectAttackTransports(Territory unitTerritory)
608
return m_ui.getBattlePanel().getAttackTransports(unitTerritory);
612
* Ask if the player wants to attack units
614
public boolean selectAttackUnits(Territory unitTerritory)
616
return m_ui.getBattlePanel().getAttackUnits(unitTerritory);
620
* Ask if the player wants to shore bombard
622
public boolean selectShoreBombard(Territory unitTerritory)
624
return m_ui.getBattlePanel().getShoreBombard(unitTerritory);
628
* @see games.strategy.triplea.player.ITripleaPlayer#shouldBomberBomb(games.strategy.engine.data.Territory)
630
public boolean shouldBomberBomb(Territory territory)
632
return m_ui.getStrategicBombingRaid(territory);
637
* @see games.strategy.triplea.player.ITripleaPlayer#shouldBomberBomb(games.strategy.engine.data.Territory)
639
public Unit whatShouldBomberBomb(Territory territory, Collection<Unit> units)
641
return m_ui.getStrategicBombingRaidTarget(territory, units);
645
public Territory whereShouldRocketsAttack(Collection<Territory> candidates, Territory from)
647
return m_ui.getRocketAttack(candidates, from);
651
* @see games.strategy.triplea.player.ITripleaPlayer#getNumberOfFightersToMoveToNewCarrier(java.util.Collection, games.strategy.engine.data.Territory)
653
public Collection<Unit> getNumberOfFightersToMoveToNewCarrier(Collection<Unit> fightersThatCanBeMoved, Territory from)
655
return m_ui.moveFightersToCarrier(fightersThatCanBeMoved, from);
659
* @see games.strategy.triplea.player.ITripleaPlayer#selectTerritoryForAirToLand(java.util.Collection, java.lang.String)
661
public Territory selectTerritoryForAirToLand(Collection<Territory> candidates)
663
return m_ui.selectTerritoryForAirToLand(candidates);
668
* @see games.strategy.triplea.player.ITripleaPlayer#confirmMoveInFaceOfAA(java.util.Collection)
670
public boolean confirmMoveInFaceOfAA(Collection<Territory> aaFiringTerritories)
672
String question = "AA guns will fire in " + MyFormatter.territoriesToText(aaFiringTerritories, "and") + ", do you still want to move?";
673
return m_ui.getOK(question);
677
public boolean confirmMoveKamikaze()
679
String question = "Not all air units in destination territory can land, do you still want to move?";
680
return m_ui.getOK(question);
683
public boolean confirmMoveHariKari()
685
String question = "All units in destination territory will automatically die, do you still want to move?";
686
return m_ui.getOK(question);
690
* @see games.strategy.triplea.player.ITripleaPlayer#retreatQuery(games.strategy.net.GUID, boolean, java.util.Collection, java.lang.String, java.lang.String)
692
public Territory retreatQuery(GUID battleID, boolean submerge, Collection<Territory> possibleTerritories, String message)
694
return m_ui.getBattlePanel().getRetreat(battleID, message, possibleTerritories,submerge);
698
* @see games.strategy.triplea.player.ITripleaPlayer#scrambleQuery(games.strategy.net.GUID, java.util.Collection, java.lang.String, java.lang.String)
700
public Collection<Unit> scrambleQuery(GUID battleID, Collection<Territory> possibleTerritories, String message)
702
return m_ui.getBattlePanel().getScramble(m_bridge, battleID, message, possibleTerritories);
705
public void confirmEnemyCasualties(GUID battleId, String message, PlayerID hitPlayer)
707
//no need, we have already confirmed since we are firing player
708
if(m_ui.playing(hitPlayer))
710
//we dont want to confirm enemy casualties
711
if(!BattleDisplay.getShowEnemyCasualtyNotification())
714
m_ui.getBattlePanel().confirmCasualties(battleId, message);
717
public void confirmOwnCasualties(GUID battleId, String message)
719
m_ui.getBattlePanel().confirmCasualties(battleId, message);
722
public final boolean isSBRAffectsUnitProduction(GameData data)
724
return games.strategy.triplea.Properties.getSBRAffectsUnitProduction(data);
727
public final boolean isDamageFromBombingDoneToUnitsInsteadOfTerritories(GameData data)
729
return games.strategy.triplea.Properties.getDamageFromBombingDoneToUnitsInsteadOfTerritories(data);
732
private static boolean isTechDevelopment(GameData data)
734
return games.strategy.triplea.Properties.getTechDevelopment(data);
737
private boolean getScramble_Rules_In_Effect()
739
return games.strategy.triplea.Properties.getScramble_Rules_In_Effect(m_bridge.getGameData());