Fixed image loading, added rendering, added JMenu
@ -1,11 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="src" path="res"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER">
|
||||
<attributes>
|
||||
<attribute name="module" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="lib" path="res"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
|
BIN
res/Flag.ico
Before Width: | Height: | Size: 70 KiB |
BIN
res/Mine.ico
Before Width: | Height: | Size: 118 KiB |
BIN
res/Mine2.ico
Before Width: | Height: | Size: 126 KiB |
BIN
res/Mine3.ico
Before Width: | Height: | Size: 105 KiB |
BIN
res/Mine4.ico
Before Width: | Height: | Size: 102 KiB |
BIN
res/Smiley.ico
Before Width: | Height: | Size: 100 KiB |
BIN
res/Smiley1.ico
Before Width: | Height: | Size: 92 KiB |
BIN
res/Smiley2.ico
Before Width: | Height: | Size: 97 KiB |
BIN
res/Smiley3.ico
Before Width: | Height: | Size: 46 KiB |
BIN
res/Tile.ico
Before Width: | Height: | Size: 61 KiB |
BIN
res/Tile2.ico
Before Width: | Height: | Size: 60 KiB |
BIN
res/Tile3.ico
Before Width: | Height: | Size: 71 KiB |
BIN
res/Tile4.ico
Before Width: | Height: | Size: 58 KiB |
BIN
res/flag.png
Normal file
After Width: | Height: | Size: 6.1 KiB |
BIN
res/mine.png
Normal file
After Width: | Height: | Size: 9.9 KiB |
BIN
res/mine2.png
Normal file
After Width: | Height: | Size: 8.9 KiB |
BIN
res/mine3.png
Normal file
After Width: | Height: | Size: 7.0 KiB |
BIN
res/mine4.png
Normal file
After Width: | Height: | Size: 7.6 KiB |
BIN
res/smiley.png
Normal file
After Width: | Height: | Size: 8.1 KiB |
BIN
res/smiley1.png
Normal file
After Width: | Height: | Size: 6.5 KiB |
BIN
res/smiley2.png
Normal file
After Width: | Height: | Size: 7.2 KiB |
BIN
res/smiley3.png
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
res/tile.png
Normal file
After Width: | Height: | Size: 6.2 KiB |
BIN
res/tile2.png
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
res/tile3.png
Normal file
After Width: | Height: | Size: 7.2 KiB |
BIN
res/tile4.png
Normal file
After Width: | Height: | Size: 4.9 KiB |
@ -1,12 +1,17 @@
|
||||
package dev.kske.minesweeper;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Font;
|
||||
import java.awt.FontMetrics;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Image;
|
||||
import java.awt.Point;
|
||||
import java.awt.Rectangle;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JPanel;
|
||||
|
||||
/**
|
||||
* Project: <strong>Minesweeper</strong><br>
|
||||
@ -14,23 +19,34 @@ import javax.swing.JFrame;
|
||||
* Created: <strong>22.03.2019</strong><br>
|
||||
* Author: <strong>Kai S. K. Engelbart</strong>
|
||||
*/
|
||||
public class Board {
|
||||
public class Board extends JPanel {
|
||||
|
||||
private int tileSize, width, height;
|
||||
private static final long serialVersionUID = -279269871397851420L;
|
||||
private static final int tileSize = 32;
|
||||
|
||||
private static Map<String, Image> icons;
|
||||
|
||||
private int width, height;
|
||||
private Rectangle screen;
|
||||
|
||||
private GameState gameState;
|
||||
private int mines, activeTiles, flaggedTiles;
|
||||
private Tile[][] board;
|
||||
|
||||
private final JFrame viewport;
|
||||
static {
|
||||
icons = new HashMap<>();
|
||||
final String[] names = { "mine2", "mine4", "tile", "tile3", "flag" };
|
||||
for (String name : names) {
|
||||
icons.put(name, TextureLoader.loadScaledImage(name, tileSize));
|
||||
}
|
||||
}
|
||||
|
||||
public Board(int x, int y, int width, int height, int tileSize, int mines, JFrame viewport) {
|
||||
this.tileSize = tileSize;
|
||||
public Board(int x, int y, int width, int height, int mines) {
|
||||
// Not using a layout manager
|
||||
super(null);
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
screen = new Rectangle(x, y, x + width * tileSize, y + height * tileSize);
|
||||
this.viewport = viewport;
|
||||
|
||||
gameState = GameState.ACTIVE;
|
||||
this.mines = mines;
|
||||
@ -43,6 +59,56 @@ public class Board {
|
||||
board[i][j] = new Tile();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paintComponent(Graphics g) {
|
||||
super.paintComponent(g);
|
||||
for (int i = 0; i < width; i++)
|
||||
for (int j = 0; j < height; j++) {
|
||||
Tile tile = board[i][j];
|
||||
int x = screen.x + i * tileSize, y = screen.y + j * tileSize;
|
||||
|
||||
// Draw background with grid
|
||||
g.setColor(Color.gray);
|
||||
g.fillRect(x, y, x + tileSize, y + tileSize);
|
||||
g.setColor(Color.black);
|
||||
g.drawRect(x, y, x + tileSize, y + tileSize);
|
||||
|
||||
// Draw all mines when the game is won or lost
|
||||
switch (gameState) {
|
||||
case LOST:
|
||||
// Draw tile with normal mine
|
||||
g.drawImage(icons.get("mine2"), x, y, this);
|
||||
break;
|
||||
case WON:
|
||||
// Draw tile with diffused mine
|
||||
g.drawImage(icons.get("mine4"), x, y, this);
|
||||
break;
|
||||
default:
|
||||
if (tile.isTouched()) {
|
||||
|
||||
// Draw tile with mine
|
||||
if (tile.isMine()) g.drawImage(icons.get("mine2"), x, y, this);
|
||||
|
||||
// Draw flagged tile
|
||||
else if (tile.isDrawSurroundingMines() && tile.getSurroundingMines() > 0) {
|
||||
// Draw number of surrounding mines
|
||||
String numStr = String.valueOf(tile.getSurroundingMines());
|
||||
FontMetrics fm = g.getFontMetrics();
|
||||
int w = fm.stringWidth(numStr), h = fm.getHeight();
|
||||
g.setFont(new Font("Helvetica", Font.PLAIN, 12));
|
||||
g.drawString(numStr, x + tileSize / 2 - w / 2, y + tileSize / 2 - h / 2);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw flagged tile
|
||||
else if (tile.isFlagged()) g.drawImage(icons.get("flag"), x, y, this);
|
||||
|
||||
// Draw normal tile
|
||||
else g.drawImage(icons.get("tile"), x, y, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Point getTilePos(int x, int y) {
|
||||
return new Point((x - screen.x) / tileSize, (y - screen.y) / tileSize);
|
||||
}
|
||||
@ -95,7 +161,7 @@ public class Board {
|
||||
touchTile(i, j);
|
||||
|
||||
// Redraw the touched tile
|
||||
drawTile(n, m);
|
||||
repaint(n * tileSize, m * tileSize, (n + 1) * tileSize, (n + 1) * tileSize);
|
||||
}
|
||||
}
|
||||
|
||||
@ -109,28 +175,7 @@ public class Board {
|
||||
tile.setFlagged(true);
|
||||
flaggedTiles++;
|
||||
}
|
||||
drawTile(n, m);
|
||||
repaint(n * tileSize, m * tileSize, (n + 1) * tileSize, (n + 1) * tileSize);
|
||||
}
|
||||
}
|
||||
|
||||
public void draw() {
|
||||
for(int i = 0; i < width; i++)
|
||||
for (int j = 0; j < height; j++)
|
||||
drawTile(i, j);
|
||||
}
|
||||
|
||||
public void drawTile(int n, int m) {
|
||||
Tile tile = board[n][m];
|
||||
int x = screen.x + n * tileSize, y = screen.y + m * tileSize;
|
||||
Graphics g = viewport.getGraphics();
|
||||
|
||||
// Draw background with grid
|
||||
g.setColor(Color.gray);
|
||||
g.fillRect(x, y, x + tileSize, y + tileSize);
|
||||
g.setColor(Color.black);
|
||||
g.drawRect(x, y, x + tileSize, y + tileSize);
|
||||
|
||||
// Draw all mines if the game is lost
|
||||
// if(gameState == GameState.LOST)
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,15 @@
|
||||
package dev.kske.minesweeper;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.EventQueue;
|
||||
import java.awt.event.KeyEvent;
|
||||
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JMenu;
|
||||
import javax.swing.JMenuBar;
|
||||
import javax.swing.JMenuItem;
|
||||
import javax.swing.JOptionPane;
|
||||
|
||||
/**
|
||||
* Project: <strong>Minesweeper</strong><br>
|
||||
@ -12,6 +19,8 @@ import javax.swing.JFrame;
|
||||
*/
|
||||
public class Minesweeper {
|
||||
|
||||
private static final String VERSION = "1.0 JE";
|
||||
|
||||
private JFrame mframe;
|
||||
|
||||
private Board board;
|
||||
@ -50,8 +59,44 @@ public class Minesweeper {
|
||||
mframe.setBounds(100, 100, 450, 300);
|
||||
mframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
|
||||
board = new Board(0, 0, 10, 10, 32, 10, mframe);
|
||||
board.draw();
|
||||
createMenuBar();
|
||||
|
||||
board = new Board(0, 0, 10, 10, 10);
|
||||
mframe.getContentPane().add(board);
|
||||
|
||||
JButton btnRestart = new JButton("Restart");
|
||||
mframe.getContentPane().add(btnRestart, BorderLayout.NORTH);
|
||||
}
|
||||
|
||||
private void createMenuBar() {
|
||||
var menubar = new JMenuBar();
|
||||
|
||||
var gameMenu = new JMenu("Game");
|
||||
var aboutMenuItem = new JMenuItem("About");
|
||||
|
||||
var easyMenuItem = new JMenuItem("Easy");
|
||||
var mediumMenuItem = new JMenuItem("Medium");
|
||||
var hardMenuItem = new JMenuItem("Hard");
|
||||
var customMenuItem = new JMenuItem("Custom");
|
||||
|
||||
gameMenu.setMnemonic(KeyEvent.VK_G);
|
||||
easyMenuItem.setMnemonic(KeyEvent.VK_E);
|
||||
mediumMenuItem.setMnemonic(KeyEvent.VK_M);
|
||||
hardMenuItem.setMnemonic(KeyEvent.VK_H);
|
||||
customMenuItem.setMnemonic(KeyEvent.VK_C);
|
||||
|
||||
aboutMenuItem.addActionListener((event) -> {
|
||||
JOptionPane.showMessageDialog(board, "Minesweeper version " + VERSION + "\nby Kai S. K. Engelbart");
|
||||
});
|
||||
|
||||
gameMenu.add(easyMenuItem);
|
||||
gameMenu.add(mediumMenuItem);
|
||||
gameMenu.add(hardMenuItem);
|
||||
gameMenu.addSeparator();
|
||||
gameMenu.add(customMenuItem);
|
||||
menubar.add(gameMenu);
|
||||
menubar.add(aboutMenuItem);
|
||||
|
||||
mframe.setJMenuBar(menubar);
|
||||
}
|
||||
}
|
||||
|
38
src/dev/kske/minesweeper/TextureLoader.java
Normal file
@ -0,0 +1,38 @@
|
||||
package dev.kske.minesweeper;
|
||||
|
||||
import java.awt.Image;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
/**
|
||||
* Project: <strong>Minesweeper</strong><br>
|
||||
* File: <strong>TextureLoader.java</strong><br>
|
||||
* Created: <strong>25.03.2019</strong><br>
|
||||
* Author: <strong>Kai S. K. Engelbart</strong>
|
||||
*/
|
||||
public class TextureLoader {
|
||||
|
||||
private TextureLoader() {}
|
||||
|
||||
/**
|
||||
* Loads an image from the resource folder and scales it to a square.
|
||||
*
|
||||
* @param name The name of the file without the PNG extension in the resource
|
||||
* folder
|
||||
* @param scale The side length of the square to which the image will be scaled
|
||||
* @return The scaled image
|
||||
*/
|
||||
public static Image loadScaledImage(String name, int scale) {
|
||||
BufferedImage in = null;
|
||||
try {
|
||||
in = ImageIO.read(new File("res" + File.separator + name + ".png"));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
Image scaled = in.getScaledInstance(scale, scale, Image.SCALE_SMOOTH);
|
||||
return scaled;
|
||||
}
|
||||
}
|
@ -1,13 +1,5 @@
|
||||
package dev.kske.minesweeper;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
/**
|
||||
* Project: <strong>Minesweeper</strong><br>
|
||||
* File: <strong>Tile.java</strong><br>
|
||||
@ -16,27 +8,11 @@ import javax.imageio.ImageIO;
|
||||
*/
|
||||
public class Tile {
|
||||
|
||||
private static Map<String, BufferedImage> icons;
|
||||
|
||||
private boolean mine, flagged, touched;
|
||||
|
||||
private boolean drawSurroundingMines;
|
||||
private int surroundingMines;
|
||||
|
||||
static {
|
||||
icons = new HashMap<>();
|
||||
final String[] names = { "Mine2", "Mine4", "Tile", "Tile3" };
|
||||
for (String name : names) {
|
||||
URL file = Tile.class.getResource(name + ".ico");
|
||||
try {
|
||||
icons.put(name, ImageIO.read(file));
|
||||
} catch (IOException e) {
|
||||
System.err.println("Error loading texture: " + name);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Tile() {
|
||||
mine = flagged = touched = drawSurroundingMines = false;
|
||||
|
||||
|