package VASSAL.command; import java.awt.Point; import VASSAL.build.GameModule; import VASSAL.build.module.GlobalOptions; import VASSAL.build.module.Map; import VASSAL.build.module.map.HighlightLastMoved; import VASSAL.counters.BoundsTracker; import VASSAL.counters.Deck; import VASSAL.counters.DeckVisitor; import VASSAL.counters.DeckVisitorDispatcher; import VASSAL.counters.GamePiece; import VASSAL.counters.PieceVisitorDispatcher; import VASSAL.counters.Properties; import VASSAL.counters.Stack; /* * $Id$ * * Copyright (c) 2003 by Rodney Kinney * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License (LGPL) as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, copies are available * at http://www.opensource.org. */ /** * Command that moves a piece to a new location and position within a stack. * While this can be accomplished with a {@link ChangePiece} command, this * command is safer in terms of recovering from changes to the game state that may have occurred * since the command was created. For instance, A {@link ChangePiece} command that adds * a piece to a {@link VASSAL.counters.Stack} will cause the piece to disappear if the * stack has been deleted. This Command will recover more gracefully. */ public class MovePiece extends Command { private String id; private String newMapId; private String oldMapId; private Point newPosition; private Point oldPosition; private String newUnderneathId; private String oldUnderneathId; private String playerId; /** * * @param id The id of the piece being moved * @param newMapId The id of the map being moved to * @param newPosition the new position * @param newUnderneathId The id of the piece which will be immediately beneath this piece in any containing Stack. May be null * @param oldMapId The id of the map being moved from * @param oldPosition the old position * @param oldUnderneathId The id of the piece which was immediately beneath this piece in its original containing Stack. * @param playerId the id of the player making this move */ public MovePiece(String id, String newMapId, Point newPosition, String newUnderneathId, String oldMapId, Point oldPosition, String oldUnderneathId, String playerId) { this.id = id; this.newMapId = newMapId; this.oldMapId = oldMapId; this.newPosition = newPosition; this.oldPosition = oldPosition; this.newUnderneathId = newUnderneathId; this.oldUnderneathId = oldUnderneathId; this.playerId = playerId; } public String getId() { return id; } public String getNewMapId() { return newMapId; } public String getOldMapId() { return oldMapId; } public Point getNewPosition() { return newPosition; } public Point getOldPosition() { return oldPosition; } public String getNewUnderneathId() { return newUnderneathId; } public String getOldUnderneathId() { return oldUnderneathId; } public String getPlayerId() { return playerId; } protected void executeCommand() { GamePiece piece = GameModule.getGameModule().getGameState().getPieceForId(id); if (piece != null) { BoundsTracker bounds = new BoundsTracker(); bounds.addPiece(piece); Map newMap = Map.getMapById(newMapId); if (newMap != null) { PieceVisitorDispatcher mergeFinder = createMergeFinder(newMap, piece, newPosition); if (newUnderneathId != null) { GamePiece under = GameModule.getGameModule().getGameState().getPieceForId(newUnderneathId); if (under != null && under.getPosition().equals(newPosition)) { newMap.getStackMetrics().merge(under, piece); } else { if (newMap.apply(mergeFinder) == null) { newMap.placeAt(piece, newPosition); } } } else { if (newMap.apply(mergeFinder) == null) { newMap.placeAt(piece, newPosition); } if (piece.getParent() != null) { piece.getParent().insert(piece, 0); } } } else { Map oldMap = Map.getMapById(oldMapId); if (oldMap != null) { oldMap.removePiece(piece); } } bounds.addPiece(piece); // Highlight the stack the piece was moved to HighlightLastMoved.setLastMoved(piece); bounds.repaint(); if (piece.getMap() != null && GlobalOptions.getInstance().centerOnOpponentsMove() && !Boolean.TRUE.equals(piece.getProperty(Properties.INVISIBLE_TO_ME))) { piece.getMap().ensureVisible(piece.getMap().selectionBoundsOf(piece)); } } } protected Command myUndoCommand() { return new MovePiece(id, oldMapId, oldPosition, oldUnderneathId, newMapId, newPosition, newUnderneathId, playerId); } /** * Creates a new {@link PieceVisitorDispatcher} that will create a {@link Command} object * to merge the target piece with any applicable pieces at the target location * @param map * @param p * @param pt * @return */ protected PieceVisitorDispatcher createMergeFinder(final Map map, final GamePiece p, final Point pt) { PieceVisitorDispatcher dispatch = new DeckVisitorDispatcher(new DeckVisitor() { public Object visitDeck(Deck d) { if (d.getPosition().equals(pt)) { return map.getStackMetrics().merge(d, p); } else { return null; } } public Object visitStack(Stack s) { if (s.getPosition().equals(pt) && map.getStackMetrics().isStackingEnabled() && !Boolean.TRUE.equals(p.getProperty(Properties.NO_STACK)) && s.topPiece(playerId) != null && map.getPieceCollection().canMerge(p,s)) { return map.getStackMetrics().merge(s, p); } else { return null; } } public Object visitDefault(GamePiece piece) { if (piece.getPosition().equals(pt) && map.getStackMetrics().isStackingEnabled() && !Boolean.TRUE.equals(p.getProperty(Properties.NO_STACK)) && !Boolean.TRUE.equals(piece.getProperty(Properties.NO_STACK)) && map.getPieceCollection().canMerge(p,piece)) { String hiddenBy = (String) piece.getProperty(Properties.HIDDEN_BY); if (hiddenBy == null || hiddenBy.equals(playerId)) { return map.getStackMetrics().merge(piece, p); } else { return null; } } else { return null; } } }); return dispatch; } public String getDetails() { return "id="+id+",map="+newMapId+",position="+newPosition+",under="+newUnderneathId; } }