GamePanelPresenter.java

  1. package org.microcol.gui.panelview;

  2. import java.awt.Rectangle;
  3. import java.awt.event.MouseAdapter;
  4. import java.awt.event.MouseEvent;
  5. import java.util.ArrayList;
  6. import java.util.List;

  7. import javax.swing.JMenuItem;
  8. import javax.swing.JPopupMenu;
  9. import javax.swing.JViewport;

  10. import org.microcol.gui.GamePreferences;
  11. import org.microcol.gui.LocalizationHelper;
  12. import org.microcol.gui.Localized;
  13. import org.microcol.gui.PathPlanning;
  14. import org.microcol.gui.Point;
  15. import org.microcol.gui.event.AboutGameEventController;
  16. import org.microcol.gui.event.CenterViewController;
  17. import org.microcol.gui.event.DebugRequestController;
  18. import org.microcol.gui.event.ExitGameController;
  19. import org.microcol.gui.event.FocusedTileController;
  20. import org.microcol.gui.event.FocusedTileEvent;
  21. import org.microcol.gui.event.GameController;
  22. import org.microcol.gui.event.KeyController;
  23. import org.microcol.gui.event.MoveUnitController;
  24. import org.microcol.gui.event.NewGameController;
  25. import org.microcol.gui.event.ShowGridController;
  26. import org.microcol.gui.event.StatusBarMessageController;
  27. import org.microcol.gui.event.StatusBarMessageEvent;
  28. import org.microcol.model.Location;
  29. import org.microcol.model.Ship;
  30. import org.microcol.model.Terrain;
  31. import org.microcol.model.event.ShipMovedEvent;
  32. import org.slf4j.Logger;
  33. import org.slf4j.LoggerFactory;

  34. import com.google.common.base.Preconditions;
  35. import com.google.inject.Inject;

  36. public class GamePanelPresenter implements Localized {

  37.     private final Logger logger = LoggerFactory.getLogger(GamePanelPresenter.class);

  38.     public interface Display {

  39.         GamePanelView getGamePanelView();

  40.         Location getCursorLocation();

  41.         void setCursorLocation(Location cursorLocation);

  42.         void setCursorNormal();

  43.         void setCursorGoto();

  44.         boolean isGotoMode();

  45.         void setGotoCursorTitle(Location gotoCursorTitle);

  46.         Location getGotoCursorTitle();

  47.         void setWalkAnimator(WalkAnimator walkAnimator);

  48.         WalkAnimator getWalkAnimator();

  49.         void initGame(boolean idGridShown);

  50.         void setGridShown(boolean isGridShown);

  51.         Area getArea();

  52.         void planScrollingAnimationToPoint(Point targetPoint);

  53.         void stopTimer();

  54.         VisualDebugInfo getVisualDebugInfo();
  55.        
  56.         void startMoveUnit(Ship ship);
  57.     }

  58.     private final GameController gameController;

  59.     private final FocusedTileController focusedTileController;

  60.     private final PathPlanning pathPlanning;

  61.     private final GamePanelPresenter.Display display;

  62.     private final StatusBarMessageController statusBarMessageController;

  63.     private Point lastMousePosition;

  64.     private final LocalizationHelper localizationHelper;

  65.     static class PopUpDemo extends JPopupMenu {

  66.         /**
  67.          * Default serialVersionUID.
  68.          */
  69.         private static final long serialVersionUID = 1L;

  70.         public PopUpDemo() {
  71.             for (int i = 0; i < 10; i++) {
  72.                 JMenuItem anItem = new JMenuItem("Item " + i + " click me!");
  73.                 add(anItem);
  74.             }
  75.         }
  76.     }

  77.     @Inject
  78.     public GamePanelPresenter(final GamePanelPresenter.Display display, final GameController gameController,
  79.             final KeyController keyController, final StatusBarMessageController statusBarMessageController,
  80.             final FocusedTileController focusedTileController, final PathPlanning pathPlanning,
  81.             final MoveUnitController moveUnitController, final NewGameController newGameController,
  82.             final GamePreferences gamePreferences, final ShowGridController showGridController,
  83.             final CenterViewController viewController, final AboutGameEventController gameEventController,
  84.             final ExitGameController exitGameController, final LocalizationHelper localizationHelper,
  85.             final DebugRequestController debugRequestController) {
  86.         this.focusedTileController = focusedTileController;
  87.         this.gameController = Preconditions.checkNotNull(gameController);
  88.         this.statusBarMessageController = Preconditions.checkNotNull(statusBarMessageController);
  89.         this.pathPlanning = Preconditions.checkNotNull(pathPlanning);
  90.         this.display = Preconditions.checkNotNull(display);
  91.         this.localizationHelper = Preconditions.checkNotNull(localizationHelper);

  92.         moveUnitController.addMoveUnitListener(event -> {
  93.             scheduleWalkAnimation(event);
  94.             /**
  95.              * Wait until animation is finished.
  96.              */
  97.             while (display.getWalkAnimator() != null && display.getWalkAnimator().isNextAnimationLocationAvailable()) {
  98.                 try {
  99.                     Thread.sleep(100);
  100.                 } catch (InterruptedException e1) {
  101.                     /**
  102.                      * Exception is intentionally sink.
  103.                      */
  104.                 }
  105.             }
  106.         });
  107.         moveUnitController.addStartMovingListener(event -> swithToMoveMode());

  108.         keyController.addListener(e -> {
  109.             /**
  110.              * Escape
  111.              */
  112.             if (27 == e.getKeyCode()) {
  113.                 onKeyPressed_escape();
  114.             }
  115.             /**
  116.              * Enter
  117.              */
  118.             if (10 == e.getKeyCode()) {
  119.                 onKeyPressed_enter();
  120.             }
  121.             logger.debug("Pressed key: '" + e.getKeyChar() + "' has code '" + e.getKeyCode() + "', modifiers '"
  122.                     + e.getModifiers() + "'");
  123.         });

  124.         final MouseAdapter ma = new MouseAdapter() {

  125.             @Override
  126.             public void mousePressed(final MouseEvent e) {
  127.                 logger.debug("mouse pressed at " + e.getX() + ", " + e.getY() + ", " + e.getButton());
  128.                 if (isMouseEnabled()) {
  129.                     if (e.isPopupTrigger()) {
  130.                         doPop(e);
  131.                     }
  132.                     onMousePressed(e);
  133.                 }
  134.             }

  135.             @Override
  136.             public void mouseReleased(final MouseEvent e) {
  137.                 if (isMouseEnabled()) {
  138.                     if (e.isPopupTrigger()) {
  139.                         doPop(e);
  140.                     }
  141.                 }
  142.             }

  143.             @Override
  144.             public void mouseMoved(final MouseEvent e) {
  145.                 if (isMouseEnabled()) {
  146.                     onMouseMoved(e);
  147.                 }
  148.             }

  149.             @Override
  150.             public void mouseDragged(final MouseEvent e) {
  151.                 if (isMouseEnabled()) {
  152.                     logger.debug("mouse dragged at " + e.getX() + ", " + e.getY() + ", " + e.getButton());
  153.                     onMouseDragged(e);
  154.                 }
  155.             }

  156.             private void doPop(MouseEvent e) {
  157.                 PopUpDemo menu = new PopUpDemo();
  158.                 menu.show(e.getComponent(), e.getX(), e.getY());
  159.             }
  160.         };

  161.         newGameController.addListener(event -> display.initGame(gamePreferences.isGridShown()));

  162.         showGridController.addListener(e -> display.setGridShown(e.isGridShown()));
  163.         debugRequestController.addListener(e -> {
  164.             display.getVisualDebugInfo().setLocations(e.getLocations());
  165.         });

  166.         display.getGamePanelView().addMouseListener(ma);
  167.         display.getGamePanelView().addMouseMotionListener(ma);
  168.         display.getGamePanelView().getParent().addComponentListener(new GamePanelListener(display));
  169.         viewController.addListener(event -> onCenterView());

  170.         exitGameController.addListener(event -> display.stopTimer());

  171.     }

  172.     private boolean isMouseEnabled() {
  173.         return gameController.getModel().getCurrentPlayer().isHuman();
  174.     }

  175.     private void onCenterView() {
  176.         logger.debug("Center view event");
  177.         Preconditions.checkNotNull(display.getCursorLocation(), "Cursor location is empty");
  178.         /**
  179.          * Here could be verification of race conditions like centering to
  180.          * bottom right corner of map. Luckily it's done by JViewport.
  181.          */
  182.         final Point p = display.getArea().getCenterAreaTo(Point.of(display.getCursorLocation()));
  183.         display.planScrollingAnimationToPoint(p);
  184.     }

  185.     private void swithToMoveMode() {
  186.         Preconditions.checkNotNull(display.getCursorLocation(), "Cursor location is empty");
  187.         final List<Ship> units = gameController.getModel().getCurrentPlayer().getShipsAt(display.getCursorLocation());
  188.         // TODO JJ Filter unit that have enough action points
  189.         Preconditions.checkState(!units.isEmpty(), "there are some moveable units");
  190.         final Ship unit = units.get(0);
  191.         display.startMoveUnit(unit);
  192.         display.setGotoCursorTitle(lastMousePosition.toLocation());
  193.         logger.debug("Switching '" + unit + "' to go mode.");
  194.         display.setCursorGoto();
  195.     }

  196.     private void onKeyPressed_escape() {
  197.         if (display.isGotoMode()) {
  198.             cancelGoToMode(display.getGotoCursorTitle());
  199.         }
  200.     }

  201.     private void onKeyPressed_enter() {
  202.         if (display.isGotoMode()) {
  203.             switchToNormalMode(display.getGotoCursorTitle());
  204.         }
  205.     }

  206.     private void onMousePressed(final MouseEvent e) {
  207.         final Location location = display.getArea().convertToLocation(Point.of(e.getX(), e.getY()));
  208.         logger.debug("location of mouse: " + location);
  209.         if (display.isGotoMode()) {
  210.             switchToNormalMode(location);
  211.         } else {
  212.             display.setCursorLocation(location);
  213.             focusedTileController.fireEvent(new FocusedTileEvent(gameController.getModel(), location,
  214.                     gameController.getModel().getMap().getTerrainAt(location)));
  215.         }
  216.     }

  217.     private void onMouseDragged(final MouseEvent e) {
  218.         if (lastMousePosition != null) {
  219.             final JViewport viewPort = (JViewport) display.getGamePanelView().getParent();
  220.             if (viewPort != null) {
  221.                 final Point currentPosition = Point.of(e.getX(), e.getY());
  222.                 final Point delta = lastMousePosition.substract(currentPosition);
  223.                 final Rectangle view = viewPort.getViewRect();
  224.                 view.x += delta.getX();
  225.                 view.y += delta.getY();
  226.                 display.getGamePanelView().scrollRectToVisible(view);
  227.             }
  228.         }
  229.     }

  230.     private void onMouseMoved(final MouseEvent e) {
  231.         lastMousePosition = Point.of(e.getX(), e.getY());
  232.         if (display.isGotoMode()) {
  233.             display.setGotoCursorTitle(lastMousePosition.toLocation());
  234.         }
  235.         /**
  236.          * Set status bar message
  237.          */
  238.         final Location where = lastMousePosition.toLocation();
  239.         final Terrain tile = gameController.getModel().getMap().getTerrainAt(where);
  240.         final StringBuilder buff = new StringBuilder();
  241.         buff.append(getText().get("statusBar.tile.start"));
  242.         buff.append(" ");
  243.         buff.append(localizationHelper.getTerrainName(tile));
  244.         buff.append(" ");
  245.         buff.append(getText().get("statusBar.tile.withUnit"));
  246.         buff.append(" ");
  247.         gameController.getModel().getShipsAt(where).forEach(ship -> {
  248.             buff.append(ship.getClass().getSimpleName());
  249.             buff.append(" ");
  250.         });
  251.         statusBarMessageController.fireEvent(new StatusBarMessageEvent(buff.toString()));
  252.     }

  253.     private void cancelGoToMode(final Location moveTo) {
  254.         display.setCursorNormal();
  255.         display.setCursorLocation(moveTo);
  256.         focusedTileController.fireEvent(new FocusedTileEvent(gameController.getModel(), moveTo,
  257.                 gameController.getModel().getMap().getTerrainAt(moveTo)));
  258.     }

  259.     private final void switchToNormalMode(final Location moveTo) {
  260.         logger.debug("Switching to normal mode, from " + display.getCursorLocation() + " to " + moveTo);
  261.         final List<Location> path = new ArrayList<Location>();
  262.         pathPlanning.paintPath(display.getCursorLocation(), moveTo, location -> path.add(location));
  263.         if (path.size() > 0) {
  264.             // TODO JJ active ship can be different from ship first at list
  265.             Ship ship = gameController.getModel().getCurrentPlayer().getShipsAt(display.getCursorLocation()).get(0);
  266.             gameController.performMove(ship, path);
  267.             focusedTileController.fireEvent(new FocusedTileEvent(gameController.getModel(), display.getCursorLocation(),
  268.                     gameController.getModel().getMap().getTerrainAt(display.getCursorLocation())));
  269.         }
  270.         display.setCursorLocation(moveTo);
  271.         display.setCursorNormal();
  272.     }

  273.     private void scheduleWalkAnimation(final ShipMovedEvent event) {
  274.         Preconditions.checkArgument(event.getPath().getLocations().size() >= 1,
  275.                 "Path for moving doesn't contains enought steps to move.");
  276.         display.planScrollingAnimationToPoint(display.getArea().getCenterAreaTo(Point.of(event.getStart())));
  277.         List<Location> path = new ArrayList<>(event.getPath().getLocations());
  278.         path.add(0, event.getStart());
  279.         final WalkAnimator walkAnimator = new WalkAnimator(pathPlanning, path, event.getShip());
  280.         display.setWalkAnimator(walkAnimator);
  281.     }

  282. }