Fixed image loading, added rendering, added JMenu
@ -1,11 +1,11 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<classpath>
|
<classpath>
|
||||||
<classpathentry kind="src" path="src"/>
|
<classpathentry kind="src" path="src"/>
|
||||||
<classpathentry kind="src" path="res"/>
|
|
||||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER">
|
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER">
|
||||||
<attributes>
|
<attributes>
|
||||||
<attribute name="module" value="true"/>
|
<attribute name="module" value="true"/>
|
||||||
</attributes>
|
</attributes>
|
||||||
</classpathentry>
|
</classpathentry>
|
||||||
|
<classpathentry kind="lib" path="res"/>
|
||||||
<classpathentry kind="output" path="bin"/>
|
<classpathentry kind="output" path="bin"/>
|
||||||
</classpath>
|
</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;
|
package dev.kske.minesweeper;
|
||||||
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
|
import java.awt.Font;
|
||||||
|
import java.awt.FontMetrics;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
|
import java.awt.Image;
|
||||||
import java.awt.Point;
|
import java.awt.Point;
|
||||||
import java.awt.Rectangle;
|
import java.awt.Rectangle;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
import javax.swing.JFrame;
|
import javax.swing.JPanel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Project: <strong>Minesweeper</strong><br>
|
* Project: <strong>Minesweeper</strong><br>
|
||||||
@ -14,23 +19,34 @@ import javax.swing.JFrame;
|
|||||||
* Created: <strong>22.03.2019</strong><br>
|
* Created: <strong>22.03.2019</strong><br>
|
||||||
* Author: <strong>Kai S. K. Engelbart</strong>
|
* 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 Rectangle screen;
|
||||||
|
|
||||||
private GameState gameState;
|
private GameState gameState;
|
||||||
private int mines, activeTiles, flaggedTiles;
|
private int mines, activeTiles, flaggedTiles;
|
||||||
private Tile[][] board;
|
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) {
|
public Board(int x, int y, int width, int height, int mines) {
|
||||||
this.tileSize = tileSize;
|
// Not using a layout manager
|
||||||
this.width = width;
|
super(null);
|
||||||
this.height = height;
|
this.width = width;
|
||||||
screen = new Rectangle(x, y, x + width * tileSize, y + height * tileSize);
|
this.height = height;
|
||||||
this.viewport = viewport;
|
screen = new Rectangle(x, y, x + width * tileSize, y + height * tileSize);
|
||||||
|
|
||||||
gameState = GameState.ACTIVE;
|
gameState = GameState.ACTIVE;
|
||||||
this.mines = mines;
|
this.mines = mines;
|
||||||
@ -43,6 +59,56 @@ public class Board {
|
|||||||
board[i][j] = new Tile();
|
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) {
|
public Point getTilePos(int x, int y) {
|
||||||
return new Point((x - screen.x) / tileSize, (y - screen.y) / tileSize);
|
return new Point((x - screen.x) / tileSize, (y - screen.y) / tileSize);
|
||||||
}
|
}
|
||||||
@ -95,7 +161,7 @@ public class Board {
|
|||||||
touchTile(i, j);
|
touchTile(i, j);
|
||||||
|
|
||||||
// Redraw the touched tile
|
// 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);
|
tile.setFlagged(true);
|
||||||
flaggedTiles++;
|
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;
|
package dev.kske.minesweeper;
|
||||||
|
|
||||||
|
import java.awt.BorderLayout;
|
||||||
import java.awt.EventQueue;
|
import java.awt.EventQueue;
|
||||||
|
import java.awt.event.KeyEvent;
|
||||||
|
|
||||||
|
import javax.swing.JButton;
|
||||||
import javax.swing.JFrame;
|
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>
|
* Project: <strong>Minesweeper</strong><br>
|
||||||
@ -12,6 +19,8 @@ import javax.swing.JFrame;
|
|||||||
*/
|
*/
|
||||||
public class Minesweeper {
|
public class Minesweeper {
|
||||||
|
|
||||||
|
private static final String VERSION = "1.0 JE";
|
||||||
|
|
||||||
private JFrame mframe;
|
private JFrame mframe;
|
||||||
|
|
||||||
private Board board;
|
private Board board;
|
||||||
@ -50,8 +59,44 @@ public class Minesweeper {
|
|||||||
mframe.setBounds(100, 100, 450, 300);
|
mframe.setBounds(100, 100, 450, 300);
|
||||||
mframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
mframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||||
|
|
||||||
board = new Board(0, 0, 10, 10, 32, 10, mframe);
|
createMenuBar();
|
||||||
board.draw();
|
|
||||||
|
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;
|
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>
|
* Project: <strong>Minesweeper</strong><br>
|
||||||
* File: <strong>Tile.java</strong><br>
|
* File: <strong>Tile.java</strong><br>
|
||||||
@ -16,27 +8,11 @@ import javax.imageio.ImageIO;
|
|||||||
*/
|
*/
|
||||||
public class Tile {
|
public class Tile {
|
||||||
|
|
||||||
private static Map<String, BufferedImage> icons;
|
|
||||||
|
|
||||||
private boolean mine, flagged, touched;
|
private boolean mine, flagged, touched;
|
||||||
|
|
||||||
private boolean drawSurroundingMines;
|
private boolean drawSurroundingMines;
|
||||||
private int surroundingMines;
|
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() {
|
public Tile() {
|
||||||
mine = flagged = touched = drawSurroundingMines = false;
|
mine = flagged = touched = drawSurroundingMines = false;
|
||||||
|
|
||||||
|