305 lines
12 KiB
Java
305 lines
12 KiB
Java
package battleship.view;
|
|
|
|
import battleship.control.WindowKeyboardListener;
|
|
import battleship.control.WindowMouseListener;
|
|
import battleship.model.Direction;
|
|
import battleship.model.Game;
|
|
import battleship.model.Ship;
|
|
import battleship.model.player.Human;
|
|
import battleship.model.player.Player;
|
|
import battleship.utils.Pair;
|
|
import battleship.utils.Triplet;
|
|
|
|
import javax.swing.*;
|
|
import java.awt.*;
|
|
import java.awt.event.KeyEvent;
|
|
import java.awt.geom.RoundRectangle2D;
|
|
|
|
/**
|
|
* Window view, instanced if argument 2 equals to "nogui"
|
|
* @see View
|
|
* @see AbstractView
|
|
*/
|
|
public class Window extends AbstractView {
|
|
|
|
final JFrame frame;
|
|
|
|
/**
|
|
* grids height, do no represent frame size
|
|
*/
|
|
public final int height = 600;
|
|
/**
|
|
* grids width, do no represent frame size
|
|
*/
|
|
public final int width = 1200;
|
|
private final WindowMouseListener mouseComponent;
|
|
private final WindowKeyboardListener keyboardComponent;
|
|
String upperTitle = "";
|
|
String upperSubTitle = "";
|
|
|
|
public Window(Game game) {
|
|
super(game);
|
|
this.frame = new JFrame("Battleship");
|
|
frame.setSize(width + width / 13, height + height / 4);
|
|
frame.setResizable(false);
|
|
frame.setContentPane(new Draw(this));
|
|
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
|
|
frame.setVisible(true);
|
|
this.mouseComponent = new WindowMouseListener(this);
|
|
frame.addMouseListener(mouseComponent);
|
|
this.keyboardComponent = new WindowKeyboardListener(this);
|
|
frame.addKeyListener(keyboardComponent);
|
|
|
|
}
|
|
|
|
@Override
|
|
protected String getKeyInput() throws InterruptedException {
|
|
return keyboardInput();
|
|
}
|
|
|
|
/**
|
|
* Display a text above the grid
|
|
* @param s text to display
|
|
*/
|
|
@Override
|
|
protected void setUpperText(String s) {
|
|
upperTitle = s;
|
|
}
|
|
|
|
/**
|
|
* Ask {@code player} to set position of its ships
|
|
* @param player player we ask
|
|
* @throws InterruptedException see {@link Window#mouseInput(Player)} and {@link Window#getDirectionFromChar()}
|
|
*/
|
|
@Override
|
|
public void setShips(Player player) throws InterruptedException {
|
|
if(player instanceof Human) {
|
|
for(int i : shipsSize) {
|
|
Ship ship = new Ship(new Pair<>(-1, -1), i, Direction.DEFAULT);
|
|
boolean valid = false;
|
|
while(!player.setShips(ship)) {
|
|
frame.repaint();
|
|
if(valid)
|
|
openDialog("Erreur de placement, votre navire se superpose avec un autre, ou la direction donnée n'est pas valide");
|
|
|
|
upperTitle = "joueur " + player.getId() + ", Placez votre premier navire de taille " + i + " à l'aide de la souris";
|
|
ship.setCoords(mouseInput(player));
|
|
upperTitle = "joueur " + player.getId() + ", Choisissez la direction de votre navire avec le clavier";
|
|
upperSubTitle = "H, B, G, D pour respectivement Haut, Bas, Gauche, Droite";
|
|
frame.repaint();
|
|
ship.setDirection(getDirectionFromChar());
|
|
ship.recalculateFullCoords();
|
|
valid = true;
|
|
}
|
|
|
|
}
|
|
upperTitle = "";
|
|
upperSubTitle = "";
|
|
} else {
|
|
super.setShips(player);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* ask {@link Game#currentPlayer} for keyboard input
|
|
* @return String given by player
|
|
* @throws InterruptedException throw if this Thread is interrupted while {@link Thread#sleep(long) sleeping}
|
|
*/
|
|
@Override
|
|
protected String keyboardInput() throws InterruptedException {
|
|
keyboardComponent.requestInput = true;
|
|
while(true) {
|
|
Thread.sleep(25);
|
|
if(keyboardComponent.keyTyped != KeyEvent.CHAR_UNDEFINED) {
|
|
keyboardComponent.requestInput = false;
|
|
String value = String.valueOf(keyboardComponent.keyTyped).toUpperCase();
|
|
keyboardComponent.keyTyped = KeyEvent.CHAR_UNDEFINED;
|
|
return value;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* ask {@code player} for mouse input
|
|
* @param player {@link Game#currentPlayer}
|
|
* @return coordinate of {@code player} opponent grid
|
|
* @throws InterruptedException throw if this Thread is interrupted while {@link Thread#sleep(long) sleeping}
|
|
*/
|
|
@Override
|
|
protected Pair<Integer, Integer> mouseInput(Player player) throws InterruptedException {
|
|
mouseComponent.requestInput = true;
|
|
while(true) {
|
|
Thread.sleep(25);
|
|
if(mouseComponent.playerIdLastInput != 0) {
|
|
if(player.getId() == mouseComponent.playerIdLastInput) {
|
|
mouseComponent.requestInput = false;
|
|
mouseComponent.playerIdLastInput = 0;
|
|
Pair<Integer, Integer> value = mouseComponent.lastInput;
|
|
mouseComponent.lastInput = null;
|
|
return value;
|
|
} else {
|
|
openDialog("Vous avez cliquer sur une zone de jeu qui n'est pas la votre");
|
|
mouseComponent.playerIdLastInput = 0;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Open a window with {@code message} as content and with a "OK" button
|
|
* @param message message to display
|
|
* @param exitOnClose {@code true} if when user close this window, the program exit, {@code false} otherwise
|
|
*/
|
|
public void openDialog(String message, boolean exitOnClose) {
|
|
JOptionPane.showMessageDialog(frame, message);
|
|
if(exitOnClose)
|
|
System.exit(0);
|
|
}
|
|
|
|
/**
|
|
* Open a window with {@code message} as content and with a "OK" button
|
|
* @param message message to display
|
|
* @see Window#openDialog(String, boolean)
|
|
*/
|
|
public void openDialog(String message) {
|
|
openDialog(message, false);
|
|
}
|
|
|
|
/**
|
|
* refresh windows to display updated content
|
|
*/
|
|
@Override
|
|
public void displayBoard() {
|
|
frame.paintComponents(frame.getGraphics());
|
|
}
|
|
|
|
/**
|
|
* ask player to choose a position in its opponent grid
|
|
* @param player {@link battleship.model.Game#currentPlayer}
|
|
* @return a couple ({@link Pair} containing the x and y coordinate (left side store Y and right side X)
|
|
* @throws InterruptedException see {@link Window#mouseInput(Player)}
|
|
*/
|
|
@Override
|
|
public Pair<Integer, Integer> chooseMove(Player player) throws InterruptedException {
|
|
setUpperText("Joueur " + player.getId() + " cliquer sur l'emplacement ou vous souhaitez tirer");
|
|
frame.repaint();
|
|
if(player instanceof Human) {
|
|
Pair<Integer, Integer> coords = new Pair<>(-1, -1);
|
|
boolean valid = false;
|
|
while(!player.areValid(coords.getLeft(), coords.getRight())) {
|
|
if(valid)
|
|
openDialog("Erreur de placement, ce coup a déjà été effectué");
|
|
valid = true;
|
|
coords = mouseInput(game.getOtherPlayer(player));
|
|
}
|
|
return coords;
|
|
}
|
|
return super.chooseMove(player);
|
|
|
|
}
|
|
|
|
/**
|
|
* open a dialog to display the winner and exit the program when window is closed
|
|
* @param winner the winner of the game.
|
|
*/
|
|
@Override
|
|
public void displayWinner(Player winner) {
|
|
openDialog("Le joueur " + winner.getId() + " a gagné(e)", true);
|
|
}
|
|
|
|
/**
|
|
* Panel where we paint the board
|
|
* @see JPanel
|
|
*/
|
|
class Draw extends JPanel {
|
|
|
|
private final Window window;
|
|
|
|
public Draw(Window window) {
|
|
this.window = window;
|
|
}
|
|
|
|
public void paintComponent(Graphics g) {
|
|
super.paintComponent(g);
|
|
Graphics2D g2d = (Graphics2D)g;
|
|
g2d.drawString(upperTitle, (int) (window.width /2 - (upperTitle.length() * 2.5)), 50);
|
|
g2d.drawString(upperSubTitle, (int) (window.width / 2 - (upperSubTitle.length() * 2.5)), 65);
|
|
int width = window.width;
|
|
int height = window.height;
|
|
int initialHeight = height / 12;
|
|
int initialWidth = width / 23;
|
|
for(int abscisse = initialWidth; abscisse < width; abscisse += initialWidth) {
|
|
g2d.drawLine(abscisse, initialHeight * 2, abscisse, height);
|
|
if(abscisse == initialWidth * 11)
|
|
abscisse += initialWidth;
|
|
}
|
|
for(int ordonnee = initialHeight * 2; ordonnee < height + 1; ordonnee += initialHeight) {
|
|
g2d.drawLine(initialWidth, ordonnee, initialWidth * 11, ordonnee);
|
|
g2d.drawLine(initialWidth * 13, ordonnee, width - 4, ordonnee);
|
|
}
|
|
|
|
for(int i = 1; i < 3; ++i) {
|
|
Player player = game.players[i-1];
|
|
for(Ship ship : player.getShips()) {
|
|
int x1 = i == 1 ? initialWidth : initialWidth * 13;
|
|
int y1 = initialHeight * 2;
|
|
int shipWidth = initialWidth;
|
|
int shipHeight = initialHeight;
|
|
switch(ship.getDirection()) {
|
|
case DOWN:
|
|
x1 += initialWidth * ship.getCoords().getRight();
|
|
y1 += initialHeight * ship.getCoords().getLeft();
|
|
shipHeight = initialHeight * ship.getSize();
|
|
g.setColor(new Color(0, 255, 255));
|
|
break;
|
|
case UP:
|
|
x1 += initialWidth * ship.getCoords().getRight();
|
|
shipHeight = initialHeight * ship.getSize();
|
|
y1 += initialHeight * ship.getCoords().getLeft() - shipHeight + initialHeight;
|
|
g.setColor(new Color(255, 255, 0));
|
|
break;
|
|
case RIGHT:
|
|
x1 += initialWidth * ship.getCoords().getRight();
|
|
y1 += initialHeight * ship.getCoords().getLeft();
|
|
shipWidth = initialWidth * ship.getSize();
|
|
g.setColor(new Color(0, 255, 0));
|
|
break;
|
|
case LEFT:
|
|
shipWidth = initialWidth * ship.getSize();
|
|
x1 += initialWidth * ship.getCoords().getRight() - shipWidth + initialWidth;
|
|
y1 += initialHeight * ship.getCoords().getLeft();
|
|
g.setColor(new Color(0, 0, 255));
|
|
break;
|
|
}
|
|
g2d.fillRoundRect(x1 + 1, y1 + 1, shipWidth - 1, shipHeight - 1, 25, 25);
|
|
}
|
|
}
|
|
for(int i = 1; i < 3; ++i) {
|
|
Player player = game.players[i-1];
|
|
int halfBoxSizeWidth = initialWidth / 2;
|
|
int halfBoxSizeHeight = initialHeight / 2;
|
|
float rectangleSize = initialWidth / 4f;
|
|
int sqrt = (int) Math.sqrt(initialHeight * initialHeight + initialWidth * initialWidth) - 10;
|
|
for(Triplet<Integer, Integer, Boolean> move : player.getMoves()) {
|
|
int x1 = (i == 1 ? initialWidth * 13 : initialWidth) + initialWidth * move.getMiddle();
|
|
int y1 = initialHeight * 2 + initialHeight * move.getLeft() + 8;
|
|
RoundRectangle2D cross1 = new RoundRectangle2D.Float(x1, y1, rectangleSize, sqrt, 15, 15);
|
|
RoundRectangle2D cross2 = new RoundRectangle2D.Float(x1 + initialWidth - 9, y1 - 9, rectangleSize, sqrt, 15, 15);
|
|
if(move.getRight()) {
|
|
g.setColor(new Color(255, 0, 0));
|
|
} else {
|
|
g.setColor(new Color(0, 123, 255));
|
|
}
|
|
g2d.rotate(Math.toRadians(-45), x1, y1);
|
|
g2d.fill(cross1);
|
|
g2d.rotate(Math.toRadians(45), x1, y1);
|
|
g2d.rotate(Math.toRadians(45), x1 + initialWidth - 9, y1 - 9);
|
|
g2d.fill(cross2);
|
|
g2d.rotate(Math.toRadians(-45), x1 + initialWidth - 9, y1 - 9);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|