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
15
package games.strategy.triplea.delegate;
17
import java.io.Serializable;
20
import games.strategy.engine.data.*;
21
import games.strategy.engine.delegate.IDelegateBridge;
22
import games.strategy.util.*;
23
import games.strategy.triplea.delegate.dataObjects.*;
24
import games.strategy.triplea.ui.MovePanel;
27
* Contains all the data to describe a move and to undo it.
30
public class UndoableMove implements Serializable
33
* Stores data about a move so that it can be undone.
34
* Stores the serialized state of the move and battle delegates (just
35
* as if they were saved), and a CompositeChange that represents all the changes that
36
* were made during the move.
38
* Some moves (such as those following an aa fire) can't be undone.
41
private CompositeChange m_undoChange = new CompositeChange();
42
private String m_reasonCantUndo;
43
private String m_description;
45
//this move is dependent on these moves
46
//these moves cant be undone until this one has been
47
private Set<UndoableMove> m_iDependOn = new HashSet<UndoableMove>();
48
//these moves depend on me
49
//we cant be undone until this is empty
50
private Set<UndoableMove> m_dependOnMe = new HashSet<UndoableMove>();
52
//list of countries we took over
53
private Set<Territory> m_conquered = new HashSet<Territory>();
55
//transports loaded by this move
56
private Set<Unit> m_loaded = new HashSet<Unit>();
58
//transports unloaded by this move
59
private Set<Unit> m_unloaded = new HashSet<Unit>();;
61
private final Route m_route;
62
private final Collection<Unit> m_units;
64
public Collection<Unit> getUnits()
69
public void addToConquered(Territory t)
74
public Route getRoute()
84
public void setIndex(int index)
89
public boolean getcanUndo()
91
return m_reasonCantUndo == null && m_dependOnMe.isEmpty();
94
public String getReasonCantUndo()
96
if(m_reasonCantUndo != null)
97
return m_reasonCantUndo;
98
else if(!m_dependOnMe.isEmpty())
100
(m_dependOnMe.iterator().next().getIndex() + 1) +
101
" must be undone first";
103
throw new IllegalStateException("no reason");
107
public void setCantUndo(String reason)
109
m_reasonCantUndo = reason;
112
public void addChange(Change aChange)
114
m_undoChange.add(aChange);
117
public String getDescription()
119
return m_description;
122
public void setDescription(String description)
124
m_description = description;
127
public UndoableMove(GameData data, Collection<Unit> units, Route route)
133
public void load(Unit transport)
135
m_loaded.add(transport);
138
public void unload(Unit transport)
140
m_unloaded.add(transport);
143
public void undo(IDelegateBridge bridge, GameData data)
145
BattleTracker battleTracker = DelegateFinder.battleDelegate(data).getBattleTracker();
148
bridge.getHistoryWriter().startEvent(bridge.getPlayerID().getName() +
149
" undo move " + (m_index + 1)+ ".");
150
bridge.getHistoryWriter().setRenderingData(new MoveDescription(m_units, m_route));
152
//undo any changes to the game data
153
bridge.addChange(m_undoChange.invert());
155
battleTracker.undoBattle(m_route, m_units, bridge.getPlayerID(), data, bridge);
157
//clean up dependencies
158
Iterator iter = m_iDependOn.iterator();
159
while (iter.hasNext()) {
160
UndoableMove other = (UndoableMove)iter.next();
161
other.m_dependOnMe.remove(this);
165
//if we are moving out of a battle zone, mark it
166
//this can happen for air units moving out of a battle zone
167
Battle battleLand =battleTracker.getPendingBattle(m_route.getStart(), false);
168
Battle battleAir =battleTracker.getPendingBattle(m_route.getStart(), true);
169
if(battleLand != null || battleAir != null)
171
iter = m_units.iterator();
172
while(iter.hasNext())
174
Unit unit = (Unit) iter.next();
175
Route routeUnitUsedToMove = DelegateFinder.moveDelegate(data).getRouteUsedToMoveInto(unit, m_route.getStart());
176
if(battleLand != null && !battleLand.isOver())
178
//route units used to move will be null in the case
179
//where an enemry sub is submerged in the territory, and another unit
180
//moved in to attack it, but some of the units in the original
181
//territory are moved out. Undoing this last move, the route used to move
182
//into the battle zone will be null
183
if(routeUnitUsedToMove != null) {
184
Change change = battleLand.addAttackChange(routeUnitUsedToMove, Collections.singleton(unit));
185
bridge.addChange(change);
188
if(battleAir != null && !battleAir.isOver())
190
Change change = battleAir.addAttackChange(routeUnitUsedToMove, Collections.singleton(unit));
191
bridge.addChange(change);
196
//Clear any temporary dependents
197
MovePanel.clearDependents(m_units);
201
* Update the dependencies.
203
public void initializeDependencies(List<UndoableMove> undoableMoves)
205
Iterator<UndoableMove> iter = undoableMoves.iterator();
206
while (iter.hasNext())
208
UndoableMove other = iter.next();
212
System.err.println(undoableMoves);
213
throw new IllegalStateException("other should not be null");
216
if( //if the other move has moves that depend on this
217
!Util.intersection(other.getUnits(), this.getUnits() ).isEmpty() ||
218
//if the other move has transports that we are loading
219
!Util.intersection(other.m_units, this.m_loaded).isEmpty() ||
220
//or we are moving through a previously conqueured territory
221
//we should be able to take this out later
222
//we need to add logic for this move to take over the same territories
223
//when the other move is undone
224
!Util.intersection(other.m_conquered, m_route.getTerritories()).isEmpty() ||
225
//or we are unloading transports that have moved in another turn
226
!Util.intersection(other.m_units, this.m_unloaded).isEmpty() ||
227
!Util.intersection(other.m_unloaded, this.m_unloaded).isEmpty()
230
m_iDependOn.add(other);
231
other.m_dependOnMe.add(this);
236
public boolean wasTransportUnloaded(Unit transport)
238
return m_unloaded.contains(transport);
241
public boolean wasTransportLoaded(Unit transport)
243
return m_loaded.contains(transport);
246
public String toString()
248
return "UndoableMove index;" + m_index + " description:" + m_description;