javasweeper/Tile.java
eriedaberrie 5e9d532b95 Init
2023-05-21 21:37:48 -07:00

255 lines
6 KiB
Java

import java.awt.*;
import java.util.HashSet;
import java.awt.event.*;
import javax.swing.*;
public class Tile extends JComponent {
public static final int SIZE = 16;
public static final int REGULAR_INDEX = 0;
public static final int PRESSED_INDEX = 1;
public static final int MINE_INDEX = 2;
public static final int FLAGGED_INDEX = 3;
public static final int NOT_MINE_INDEX = 4;
public static final int EXPLODED_MINE_INDEX = 5;
private final Canvas GAME_CANVAS;
private final Image[] NUMBER_TILES;
private final Image[] SPECIAL_TILES;
// Store the mouse listener for convenient removal once the game ends
private final MouseListener TILE_MOUSE_LISTENER;
// A set of all the Tiles that are adjacent to this one, for calculating
// numbers and autorevealing zeros and chording
private final HashSet<Tile> ADJACENT_TILES;
// Whether or not the tile is a mine
private boolean mine;
// The number of mines adjacent to the current one
private int num;
// Whether or not this mine has been revealed already
private boolean revealed;
// Whether or not this mine has been flagged
private boolean flagged;
private boolean pressed;
private boolean mouseOver;
private boolean gameEnded;
private void init() {
mine = false;
num = 0;
revealed = false;
flagged = false;
pressed = false;
mouseOver = false;
gameEnded = false;
addMouseListener(TILE_MOUSE_LISTENER);
}
public Tile(Canvas gameCanvas, Image[] numberTiles, Image[] specialTiles) {
GAME_CANVAS = gameCanvas;
NUMBER_TILES = numberTiles;
SPECIAL_TILES = specialTiles;
ADJACENT_TILES = new HashSet<Tile>();
TILE_MOUSE_LISTENER = new MouseListener() {
@Override
public void mousePressed(MouseEvent e) {
boolean rightClicked = false;
switch (e.getButton()) {
case MouseEvent.BUTTON1:
GAME_CANVAS.setLeftMouseDown(true);
break;
case MouseEvent.BUTTON3:
rightClicked = true;
GAME_CANVAS.setRightMouseDown(true);
break;
default:
return;
}
Tile tile = GAME_CANVAS.getCurrentTile();
if (tile == null)
return;
if (rightClicked)
tile.rightClickAction();
tile.updatePressedState();
}
@Override
public void mouseReleased(MouseEvent e) {
boolean leftClicked = false;
switch (e.getButton()) {
case MouseEvent.BUTTON1:
leftClicked = true;
GAME_CANVAS.setLeftMouseDown(false);
break;
case MouseEvent.BUTTON3:
GAME_CANVAS.setRightMouseDown(false);
break;
default:
return;
}
Tile tile = GAME_CANVAS.getCurrentTile();
if (tile == null)
return;
if (leftClicked)
tile.leftClickAction();
tile.updatePressedState();
}
@Override
public void mouseEntered(MouseEvent e) {
GAME_CANVAS.setCurrentTile(Tile.this);
mouseOver = true;
updatePressedState();
}
@Override
public void mouseExited(MouseEvent e) {
if (GAME_CANVAS.getCurrentTile() == Tile.this)
GAME_CANVAS.setCurrentTile(null);
mouseOver = false;
updatePressedState();
}
@Override
public void mouseClicked(MouseEvent e) {
}
};
setPreferredSize(new Dimension(SIZE, SIZE));
init();
}
public void restart() {
removeMouseListener(TILE_MOUSE_LISTENER);
init();
}
public void addAdjacentTile(Tile tile) {
ADJACENT_TILES.add(tile);
}
// At the start of the game, place a mine at this tile, unless it is
// adjacent to or is the starting tile, or is already a mine. Returns
// whether or not a mine was actually placed.
public boolean makeMine(Tile startTile) {
if (mine || this == startTile || Main.isProtectedStart() && ADJACENT_TILES.contains(startTile))
return false;
mine = true;
// Increment the number on the adjacent tiles when a new mine is decided
for (Tile tile : ADJACENT_TILES)
tile.num++;
return true;
}
public void gameEnd() {
removeMouseListener(TILE_MOUSE_LISTENER);
gameEnded = true;
// Only repaint the tiles that actually change
if (mine || flagged)
repaint();
}
public void updatePressedState() {
if (pressed != (GAME_CANVAS.isLeftMouseDown() && mouseOver)) {
pressed = !pressed;
repaint();
}
boolean pressAdjacent = pressed && GAME_CANVAS.isChording();
for (Tile tile : ADJACENT_TILES) {
if (tile.pressed != pressAdjacent) {
tile.pressed = pressAdjacent;
tile.repaint();
}
}
}
private void rightClickAction() {
if (revealed)
return;
flagged = !flagged;
GAME_CANVAS.modifyFlagCount(flagged);
repaint();
}
private void leftClickAction() {
if (GAME_CANVAS.isChording()) {
if (!revealed)
return;
int adjacentFlags = 0;
for (Tile tile : ADJACENT_TILES)
if (tile.flagged)
adjacentFlags++;
if (adjacentFlags != num)
return;
for (Tile tile : ADJACENT_TILES)
if (!tile.flagged)
tile.reveal();
} else {
if (flagged || revealed)
return;
// Ensure that mines are placed before revealing anything
GAME_CANVAS.tryStartGame(this);
reveal();
}
GAME_CANVAS.postRevealCheck();
}
private void reveal() {
if (revealed)
return;
revealed = true;
repaint();
if (mine) {
GAME_CANVAS.setLoseFlag();
} else {
GAME_CANVAS.revealedSingleTile();
if (num == 0)
for (Tile tile : ADJACENT_TILES)
if (!tile.flagged)
tile.reveal();
}
}
// Determine what image should be drawn at this tile
private Image getImage() {
if (gameEnded && mine) {
// Automatically flag mines at wins, losses keep correct flags
if (!GAME_CANVAS.isGameLost() || flagged)
return SPECIAL_TILES[FLAGGED_INDEX];
if (revealed)
return SPECIAL_TILES[EXPLODED_MINE_INDEX];
return SPECIAL_TILES[MINE_INDEX];
}
if (revealed)
return NUMBER_TILES[num];
if (flagged) {
// Correctly flagged mines would have been caught earlier
if (gameEnded)
return SPECIAL_TILES[NOT_MINE_INDEX];
return SPECIAL_TILES[FLAGGED_INDEX];
}
// Tiles are only interactable if the game is ongoing
if (pressed && !gameEnded)
return SPECIAL_TILES[PRESSED_INDEX];
return SPECIAL_TILES[REGULAR_INDEX];
}
@Override
public void paintComponent(Graphics g) {
g.drawImage(getImage(), 0, 0, this);
}
}