Finished everything minus comments
This commit is contained in:
parent
5e9d532b95
commit
1d5ade9136
23
Canvas.java
23
Canvas.java
|
@ -67,10 +67,11 @@ public class Canvas extends JPanel {
|
|||
TIMER_DISPLAY.setNum(time);
|
||||
}
|
||||
|
||||
public Canvas(int rows, int cols, int mines, File skin) {
|
||||
ROWS = rows;
|
||||
COLS = cols;
|
||||
MINES = mines;
|
||||
public Canvas(File skin) {
|
||||
Difficulty difficulty = Options.getDifficulty();
|
||||
ROWS = difficulty.getRows();
|
||||
COLS = difficulty.getCols();
|
||||
MINES = difficulty.getMines();
|
||||
WIDTH = Tile.SIZE * COLS + 2 * BORDER_WIDTH;
|
||||
HEIGHT = Tile.SIZE * ROWS + 2 * BORDER_HEIGHT + BOTTOM_HEIGHT + TOP_BOX_HEIGHT;
|
||||
|
||||
|
@ -127,7 +128,7 @@ public class Canvas extends JPanel {
|
|||
TIMER_DISPLAY = new NumberDisplay(numberDisplayBackdrop, digits);
|
||||
FACE = new Face(this, getImageSet(tileset, Face.PRESSED_INDEX, TS_FACES_Y, Face.SIZE, Face.SIZE));
|
||||
|
||||
SHIFT_KEY_LISTENER = new KeyListener() {
|
||||
SHIFT_KEY_LISTENER = new KeyAdapter() {
|
||||
// Shift+LMB chords, so we need some way of determining if the shift
|
||||
// key is being held.
|
||||
@Override
|
||||
|
@ -146,10 +147,6 @@ public class Canvas extends JPanel {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyTyped(KeyEvent e) {
|
||||
}
|
||||
|
||||
private void updateCurrentTile() {
|
||||
if (currentTile != null)
|
||||
currentTile.updatePressedState();
|
||||
|
@ -196,6 +193,7 @@ public class Canvas extends JPanel {
|
|||
|
||||
stop();
|
||||
init();
|
||||
|
||||
for (Tile[] row : BOARD)
|
||||
for (Tile tile : row)
|
||||
tile.restart();
|
||||
|
@ -323,7 +321,7 @@ public class Canvas extends JPanel {
|
|||
Image bottomRightCorner = tileset.getSubimage(BORDER_WIDTH + 3, tsY, BORDER_WIDTH, BOTTOM_HEIGHT);
|
||||
|
||||
BufferedImage img = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
|
||||
Graphics g = img.getGraphics();
|
||||
Graphics2D g = (Graphics2D) img.getGraphics();
|
||||
|
||||
int rightBorderX = WIDTH - BORDER_WIDTH;
|
||||
int middleBorderY = BORDER_HEIGHT + TOP_BOX_HEIGHT;
|
||||
|
@ -365,8 +363,8 @@ public class Canvas extends JPanel {
|
|||
// Helper for placing components in the null layout at specific coordinates
|
||||
private void placeComponent(JComponent c, int x, int y, Insets insets) {
|
||||
add(c);
|
||||
Dimension size = c.getPreferredSize();
|
||||
c.setBounds(x + insets.left, y + insets.top, size.width, size.height);
|
||||
c.setLocation(x + insets.left, y + insets.top);
|
||||
c.setSize(c.getPreferredSize());
|
||||
}
|
||||
|
||||
private void stopGame() {
|
||||
|
@ -380,6 +378,7 @@ public class Canvas extends JPanel {
|
|||
// Override the paintComponent method in order to add the background image
|
||||
@Override
|
||||
public void paintComponent(Graphics g) {
|
||||
super.paintComponent(g);
|
||||
g.drawImage(BACKGROUND_IMAGE, 0, 0, this);
|
||||
}
|
||||
}
|
||||
|
|
70
CustomTextField.java
Normal file
70
CustomTextField.java
Normal file
|
@ -0,0 +1,70 @@
|
|||
import java.awt.event.*;
|
||||
import javax.swing.*;
|
||||
|
||||
public class CustomTextField extends JTextField {
|
||||
private static final int COLS = 4;
|
||||
|
||||
private final int MIN_VALUE;
|
||||
private final int MAX_VALUE;
|
||||
|
||||
public CustomTextField(int defaultValue, int minValue, int maxValue) {
|
||||
super(Integer.toString(defaultValue), COLS);
|
||||
|
||||
MIN_VALUE = minValue;
|
||||
MAX_VALUE = maxValue;
|
||||
|
||||
addFocusListener(new FocusAdapter() {
|
||||
// Auto select when focused
|
||||
@Override
|
||||
public void focusGained(FocusEvent e) {
|
||||
selectAll();
|
||||
}
|
||||
|
||||
// Auto format input when unfocused
|
||||
@Override
|
||||
public void focusLost(FocusEvent e) {
|
||||
formatText();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected void formatText() {
|
||||
String text = getText();
|
||||
|
||||
// If tried to input a negative number, set it to the minimum
|
||||
if (text.isEmpty() || text.charAt(0) == '-') {
|
||||
setValue(getMinValue());
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove all the nondigit characters and all leading zeros
|
||||
String filteredText = text.replaceAll("(^[^1-9]+|\\D)", "");
|
||||
if (filteredText.isEmpty()) {
|
||||
setValue(getMinValue());
|
||||
return;
|
||||
}
|
||||
|
||||
// To prevent integer overflow when parsing, just set it to max
|
||||
// when it's more than 4 digits
|
||||
if (filteredText.length() > 4) {
|
||||
setValue(getMaxValue());
|
||||
return;
|
||||
}
|
||||
|
||||
// Now that we finally have the intended int, clamp and set it
|
||||
setValue(Difficulty.clampInt(Integer.parseInt(filteredText), getMinValue(), getMaxValue()));
|
||||
}
|
||||
|
||||
// Use protected getters and setters so we can override them if necessary
|
||||
protected int getMinValue() {
|
||||
return MIN_VALUE;
|
||||
}
|
||||
|
||||
protected int getMaxValue() {
|
||||
return MAX_VALUE;
|
||||
}
|
||||
|
||||
private void setValue(int value) {
|
||||
setText(Integer.toString(value));
|
||||
}
|
||||
}
|
179
Difficulty.java
Normal file
179
Difficulty.java
Normal file
|
@ -0,0 +1,179 @@
|
|||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import javax.swing.*;
|
||||
|
||||
public enum Difficulty {
|
||||
BEGINNER (9, 9, 10),
|
||||
INTERMEDIATE (16, 16, 40),
|
||||
EXPERT (16, 30, 99),
|
||||
CUSTOM;
|
||||
|
||||
// Custom difficulty constraints
|
||||
//
|
||||
// In actual minesweeper these numbers are 9, 24, 30, 10
|
||||
private static final int MIN_SIZE = 8;
|
||||
private static final int MAX_ROWS = 40;
|
||||
private static final int MAX_COLS = 50;
|
||||
private static final int MIN_MINES = 10;
|
||||
// In regular minesweeper the mines are capped at just (rows-1)*(cols-1),
|
||||
// but we additionally cap it at 999 because the expanded board size means
|
||||
// it's possible to hit the number display cap
|
||||
private static final int MAX_MINES = 999;
|
||||
|
||||
private int rows;
|
||||
private int cols;
|
||||
private int mines;
|
||||
|
||||
private Difficulty() {
|
||||
}
|
||||
|
||||
private Difficulty(int rows, int cols, int mines) {
|
||||
setStats(rows, cols, mines);
|
||||
}
|
||||
|
||||
private void setStats(int rows, int cols, int mines) {
|
||||
this.rows = rows;
|
||||
this.cols = cols;
|
||||
this.mines = mines;
|
||||
}
|
||||
|
||||
public int getRows() {
|
||||
return rows;
|
||||
}
|
||||
|
||||
public int getCols() {
|
||||
return cols;
|
||||
}
|
||||
|
||||
public int getMines() {
|
||||
return mines;
|
||||
}
|
||||
|
||||
public static boolean setCustom(JFrame frame) {
|
||||
Difficulty current = Options.getDifficulty();
|
||||
|
||||
// Use single-item array to store a reference to minesField, because
|
||||
// minesField needs to needs to be initialized after rowsField and
|
||||
// colsField but RowsColsField needs minesField
|
||||
CustomTextField[] minesRef = new CustomTextField[1];
|
||||
// Override CustomTextField in order to make it format minesField after
|
||||
// editing (when the board size is decreased, so does the max mine
|
||||
// count, so we may need to update it)
|
||||
class RowsColsField extends CustomTextField {
|
||||
private RowsColsField(int defaultValue, int minValue, int maxValue) {
|
||||
super(defaultValue, minValue, maxValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void formatText() {
|
||||
super.formatText();
|
||||
minesRef[0].formatText();
|
||||
}
|
||||
}
|
||||
|
||||
CustomTextField rowsField = new RowsColsField(current.rows, MIN_SIZE, MAX_ROWS);
|
||||
CustomTextField colsField = new RowsColsField(current.cols, MIN_SIZE, MAX_COLS);
|
||||
// Override CustomTextField in order to make it dynamically determine
|
||||
// the maximum value based on the number of rows and columns
|
||||
CustomTextField minesField = minesRef[0] = new CustomTextField(current.mines, MIN_MINES, MAX_MINES) {
|
||||
@Override
|
||||
protected int getMaxValue() {
|
||||
try {
|
||||
int rows = Integer.parseInt(rowsField.getText());
|
||||
int cols = Integer.parseInt(colsField.getText());
|
||||
return getMaxMines(rows, cols);
|
||||
} catch (NumberFormatException e) {
|
||||
return super.getMaxValue();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Because Java is lame and doesn't have builtin tuples we make do with
|
||||
// casting an JComponent[][]
|
||||
JComponent[][] items = {
|
||||
{ new JLabel("Height:"), rowsField },
|
||||
{ new JLabel("Width:"), colsField },
|
||||
{ new JLabel("Mines:"), minesField },
|
||||
};
|
||||
|
||||
int option = JOptionPane.showConfirmDialog(frame, getMessagePanel(items, 6),
|
||||
"Custom Board", JOptionPane.OK_CANCEL_OPTION);
|
||||
if (option != JOptionPane.OK_OPTION)
|
||||
return false;
|
||||
|
||||
int rows, cols, mines;
|
||||
try {
|
||||
rows = Integer.parseInt(rowsField.getText());
|
||||
cols = Integer.parseInt(colsField.getText());
|
||||
mines = Integer.parseInt(minesField.getText());
|
||||
} catch (NumberFormatException e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Clamp values to the allowed range, just in case something slipped
|
||||
// through the focusLost events
|
||||
rows = clampInt(rows, MIN_SIZE, MAX_ROWS);
|
||||
cols = clampInt(cols, MIN_SIZE, MAX_COLS);
|
||||
mines = clampInt(mines, MIN_MINES, getMaxMines(rows, cols));
|
||||
|
||||
// If we nothing changed, return false
|
||||
if (current == CUSTOM && CUSTOM.rows == rows && CUSTOM.cols == cols && CUSTOM.mines == mines)
|
||||
return false;
|
||||
|
||||
CUSTOM.setStats(rows, cols, mines);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static int clampInt(int value, int minValue, int maxValue) {
|
||||
return Math.max(Math.min(value, maxValue), minValue);
|
||||
}
|
||||
|
||||
private static int getMaxMines(int rows, int cols) {
|
||||
return Math.min((rows - 1) * (cols - 1), MAX_MINES);
|
||||
}
|
||||
|
||||
private static JPanel getMessagePanel(JComponent[][] items, int padding) {
|
||||
final int COLS = 2;
|
||||
|
||||
JPanel messagePanel = new JPanel(null);
|
||||
|
||||
for (JComponent[] row : items) {
|
||||
JLabel label = (JLabel) row[0];
|
||||
label.setLabelFor(row[1]);
|
||||
label.setHorizontalAlignment(JLabel.TRAILING);
|
||||
messagePanel.add(label);
|
||||
messagePanel.add(row[1]);
|
||||
}
|
||||
|
||||
int[] widths = new int[COLS];
|
||||
int[] heights = new int[items.length];
|
||||
int[] x = new int[COLS + 1];
|
||||
int[] y = new int[items.length + 1];
|
||||
|
||||
x[0] = padding;
|
||||
for (int c = 0; c < COLS; c++) {
|
||||
int maxWidth = Integer.MIN_VALUE;
|
||||
for (int r = 0; r < items.length; r++)
|
||||
maxWidth = Math.max(maxWidth, (int) items[r][c].getPreferredSize().getWidth());
|
||||
widths[c] = maxWidth;
|
||||
x[c + 1] = x[c] + maxWidth + padding;
|
||||
}
|
||||
|
||||
for (int r = 0; r < items.length; r++) {
|
||||
int maxHeight = Integer.MIN_VALUE;
|
||||
for (int c = 0; c < COLS; c++)
|
||||
maxHeight = Math.max(maxHeight, (int) items[r][c].getPreferredSize().getHeight());
|
||||
heights[r] = maxHeight;
|
||||
y[r + 1] = y[r] + maxHeight + padding;
|
||||
}
|
||||
|
||||
for (int r = 0; r < items.length; r++)
|
||||
for (int c = 0; c < COLS; c++)
|
||||
items[r][c].setBounds(x[c], y[r], widths[c], heights[r]);
|
||||
|
||||
messagePanel.setPreferredSize(new Dimension(x[COLS], y[items.length]));
|
||||
|
||||
return messagePanel;
|
||||
}
|
||||
}
|
11
Face.java
11
Face.java
|
@ -24,7 +24,7 @@ public class Face extends JComponent {
|
|||
pressed = false;
|
||||
mouseOver = false;
|
||||
setPreferredSize(new Dimension(SIZE, SIZE));
|
||||
addMouseListener(new MouseListener() {
|
||||
addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mousePressed(MouseEvent e) {
|
||||
pressed = true;
|
||||
|
@ -54,10 +54,6 @@ public class Face extends JComponent {
|
|||
if (pressed)
|
||||
setFace(unpressedFace);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -67,7 +63,10 @@ public class Face extends JComponent {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void paintComponent(Graphics g) {
|
||||
public void paintComponent(Graphics gr) {
|
||||
super.paintComponent(gr);
|
||||
Graphics2D g = (Graphics2D) gr;
|
||||
|
||||
g.drawImage(FACES[face], 0, 0, this);
|
||||
}
|
||||
}
|
||||
|
|
108
Main.java
108
Main.java
|
@ -7,22 +7,6 @@ import javax.swing.*;
|
|||
import javax.swing.event.*;
|
||||
|
||||
public class Main {
|
||||
private static enum Difficulty {
|
||||
BEGINNER (9, 9, 10),
|
||||
INTERMEDIATE (16, 16, 40),
|
||||
EXPERT (16, 30, 99);
|
||||
|
||||
public final int ROWS;
|
||||
public final int COLS;
|
||||
public final int MINES;
|
||||
|
||||
Difficulty(int rows, int cols, int mines) {
|
||||
this.ROWS = rows;
|
||||
this.COLS = cols;
|
||||
this.MINES = mines;
|
||||
}
|
||||
}
|
||||
|
||||
public static final String DEFAULT_SKIN = "winxpskin.bmp";
|
||||
public static final String SKINS_DIR = "Skins/";
|
||||
public static final String HELP_FILE = "help.html";
|
||||
|
@ -33,36 +17,41 @@ public class Main {
|
|||
private static final JFrame FRAME = new JFrame("Minesweeper");
|
||||
|
||||
private static Canvas canvas;
|
||||
private static Difficulty difficulty;
|
||||
private static String skin;
|
||||
private static boolean protectedStart;
|
||||
private static boolean sound;
|
||||
|
||||
public static void main(String[] args) {
|
||||
difficulty = Difficulty.INTERMEDIATE;
|
||||
skin = DEFAULT_SKIN;
|
||||
protectedStart = false;
|
||||
sound = false;
|
||||
setCanvas();
|
||||
|
||||
FRAME.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
FRAME.setResizable(false);
|
||||
FRAME.setJMenuBar(getMenuBar());
|
||||
FRAME.add(canvas);
|
||||
FRAME.pack();
|
||||
setCanvas();
|
||||
|
||||
FRAME.setVisible(true);
|
||||
}
|
||||
|
||||
public static boolean isProtectedStart() {
|
||||
return protectedStart;
|
||||
}
|
||||
|
||||
public static boolean hasSound() {
|
||||
return sound;
|
||||
}
|
||||
|
||||
private static JMenuBar getMenuBar() {
|
||||
// Button group for the difficulty buttons
|
||||
ButtonGroup difficultyButtons = new ButtonGroup();
|
||||
Difficulty difficulty = Options.getDifficulty();
|
||||
JRadioButtonMenuItem beginnerItem = new JRadioButtonMenuItem("Beginner",
|
||||
difficulty == Difficulty.BEGINNER);
|
||||
JRadioButtonMenuItem intermediateItem = new JRadioButtonMenuItem("Intermediate",
|
||||
difficulty == Difficulty.INTERMEDIATE);
|
||||
JRadioButtonMenuItem expertItem = new JRadioButtonMenuItem("Expert",
|
||||
difficulty == Difficulty.EXPERT);
|
||||
JRadioButtonMenuItem customItem = new JRadioButtonMenuItem("Custom...");
|
||||
difficultyButtons.add(beginnerItem);
|
||||
difficultyButtons.add(intermediateItem);
|
||||
difficultyButtons.add(expertItem);
|
||||
difficultyButtons.add(customItem);
|
||||
|
||||
ActionListener listener = new ActionListener() {
|
||||
// Keep track of the previously selected difficulty button in case a
|
||||
// Custom press is canceled so we can revert back to whatever it was
|
||||
// before
|
||||
private ButtonModel lastDifficultyButton = difficultyButtons.getSelection();
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
switch (e.getActionCommand()) {
|
||||
|
@ -71,21 +60,29 @@ public class Main {
|
|||
break;
|
||||
case "beginner":
|
||||
setDifficulty(Difficulty.BEGINNER);
|
||||
lastDifficultyButton = beginnerItem.getModel();
|
||||
break;
|
||||
case "intermediate":
|
||||
setDifficulty(Difficulty.INTERMEDIATE);
|
||||
lastDifficultyButton = intermediateItem.getModel();
|
||||
break;
|
||||
case "expert":
|
||||
setDifficulty(Difficulty.EXPERT);
|
||||
lastDifficultyButton = expertItem.getModel();
|
||||
break;
|
||||
case "custom":
|
||||
setDifficulty(null);
|
||||
if (Difficulty.setCustom(FRAME)) {
|
||||
setDifficulty(Difficulty.CUSTOM);
|
||||
lastDifficultyButton = customItem.getModel();
|
||||
} else {
|
||||
lastDifficultyButton.setSelected(true);
|
||||
}
|
||||
break;
|
||||
case "protectedstart":
|
||||
protectedStart = !protectedStart;
|
||||
Options.toggleProtectedStart();
|
||||
break;
|
||||
case "sound":
|
||||
sound = !sound;
|
||||
Options.toggleSound();
|
||||
Sound.initSounds();
|
||||
break;
|
||||
case "exit":
|
||||
|
@ -111,25 +108,17 @@ public class Main {
|
|||
newGameItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F2, 0));
|
||||
addMenuItem(newGameItem, gameMenu, KeyEvent.VK_N, "new", listener);
|
||||
gameMenu.addSeparator();
|
||||
ButtonGroup difficultyButtons = new ButtonGroup();
|
||||
JRadioButtonMenuItem beginnerItem = new JRadioButtonMenuItem("Beginner");
|
||||
JRadioButtonMenuItem intermediateItem = new JRadioButtonMenuItem("Intermediate", true);
|
||||
JRadioButtonMenuItem expertItem = new JRadioButtonMenuItem("Advanced");
|
||||
JRadioButtonMenuItem customItem = new JRadioButtonMenuItem("Custom...");
|
||||
difficultyButtons.add(beginnerItem);
|
||||
difficultyButtons.add(intermediateItem);
|
||||
difficultyButtons.add(expertItem);
|
||||
difficultyButtons.add(customItem);
|
||||
addMenuItem(beginnerItem, gameMenu, KeyEvent.VK_B, "beginner", listener);
|
||||
addMenuItem(intermediateItem, gameMenu, KeyEvent.VK_I, "intermediate", listener);
|
||||
addMenuItem(expertItem, gameMenu, KeyEvent.VK_E, "expert", listener);
|
||||
addMenuItem(customItem, gameMenu, KeyEvent.VK_C, "custom", listener);
|
||||
gameMenu.addSeparator();
|
||||
JCheckBoxMenuItem protectedStartItem = new JCheckBoxMenuItem("Protected Start", protectedStart);
|
||||
JCheckBoxMenuItem protectedStartItem = new JCheckBoxMenuItem("Protected Start",
|
||||
Options.isProtectedStart());
|
||||
protectedStartItem.setToolTipText("Guarantees the starting tile has no mines next to it");
|
||||
addMenuItem(protectedStartItem, gameMenu, KeyEvent.VK_P, "protectedstart", listener);
|
||||
JCheckBoxMenuItem soundItem = new JCheckBoxMenuItem("Sound", sound);
|
||||
addMenuItem(soundItem, gameMenu, KeyEvent.VK_O, "sound", listener);
|
||||
JCheckBoxMenuItem soundItem = new JCheckBoxMenuItem("Sound", Options.hasSound());
|
||||
addMenuItem(soundItem, gameMenu, KeyEvent.VK_U, "sound", listener);
|
||||
gameMenu.addSeparator();
|
||||
addMenuItem("Exit", gameMenu, KeyEvent.VK_X, "exit", listener);
|
||||
menuBar.add(gameMenu);
|
||||
|
@ -167,7 +156,7 @@ public class Main {
|
|||
try (Stream<Path> dirStream = Files.list(Paths.get(SKINS_DIR))) {
|
||||
dirStream
|
||||
// Filter for only regular files
|
||||
.filter(file -> Files.isRegularFile(file))
|
||||
.filter(Files::isRegularFile)
|
||||
// Cut off the directory name from the resulting string
|
||||
.map(Path::getFileName)
|
||||
.map(Path::toString)
|
||||
|
@ -220,11 +209,11 @@ public class Main {
|
|||
}
|
||||
|
||||
private static void setDifficulty(Difficulty newDifficulty) {
|
||||
if (newDifficulty == null);
|
||||
else if (difficulty == newDifficulty)
|
||||
// Reject switching difficulty to the already-existing one unless Custom
|
||||
if (Options.getDifficulty() == newDifficulty && newDifficulty != Difficulty.CUSTOM)
|
||||
return;
|
||||
|
||||
difficulty = newDifficulty;
|
||||
Options.setDifficulty(newDifficulty);
|
||||
replaceCanvas();
|
||||
}
|
||||
|
||||
|
@ -250,24 +239,17 @@ public class Main {
|
|||
canvas.stop();
|
||||
canvas.removeAll();
|
||||
FRAME.remove(canvas);
|
||||
|
||||
setCanvas();
|
||||
}
|
||||
canvas.requestFocusInWindow();
|
||||
}
|
||||
|
||||
private static void setCanvas() {
|
||||
canvas = getCanvas();
|
||||
canvas = new Canvas(new File(SKINS_DIR, skin));
|
||||
FRAME.add(canvas);
|
||||
FRAME.pack();
|
||||
}
|
||||
|
||||
private static Canvas getCanvas() {
|
||||
File skinFile = new File(SKINS_DIR, skin);
|
||||
if (difficulty == null) {
|
||||
// TODO: Make the difficulty actually change
|
||||
return new Canvas(40, 50, 500, skinFile);
|
||||
}
|
||||
return new Canvas(difficulty.ROWS, difficulty.COLS, difficulty.MINES, skinFile);
|
||||
}
|
||||
|
||||
// No construction >:(
|
||||
private Main() {
|
||||
}
|
||||
|
|
|
@ -40,7 +40,10 @@ public class NumberDisplay extends JComponent {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void paintComponent(Graphics g) {
|
||||
public void paintComponent(Graphics gr) {
|
||||
super.paintComponent(gr);
|
||||
Graphics2D g = (Graphics2D) gr;
|
||||
|
||||
g.drawImage(BACKDROP, 0, 0, this);
|
||||
// Preserve the original num for divison, in case we repaint twice
|
||||
// without updating the number
|
||||
|
|
37
Options.java
Normal file
37
Options.java
Normal file
|
@ -0,0 +1,37 @@
|
|||
// Public class that stores all the global static options
|
||||
public class Options {
|
||||
// Whether or not sound is enabled
|
||||
private static boolean sound = false;
|
||||
// Whether or not to force starting at 0
|
||||
private static boolean protectedStart = false;
|
||||
// The difficulty
|
||||
private static Difficulty difficulty = Difficulty.INTERMEDIATE;
|
||||
|
||||
public static boolean hasSound() {
|
||||
return sound;
|
||||
}
|
||||
|
||||
public static void toggleSound() {
|
||||
sound = !sound;
|
||||
}
|
||||
|
||||
public static boolean isProtectedStart() {
|
||||
return protectedStart;
|
||||
}
|
||||
|
||||
public static void toggleProtectedStart() {
|
||||
protectedStart = !protectedStart;
|
||||
}
|
||||
|
||||
public static Difficulty getDifficulty() {
|
||||
return difficulty;
|
||||
}
|
||||
|
||||
public static void setDifficulty(Difficulty difficulty) {
|
||||
Options.difficulty = difficulty;
|
||||
}
|
||||
|
||||
// No constructing >:(
|
||||
private Options() {
|
||||
}
|
||||
}
|
23
Sound.java
23
Sound.java
|
@ -14,7 +14,8 @@ public enum Sound {
|
|||
// Named pipe to send audio requests to on Replit
|
||||
public static final File REPLIT_PIPE = new File("/tmp/audio");
|
||||
|
||||
// Use env vars and the existance of the above pipe to determine if running inside Replit
|
||||
// Use env vars and the existance of the above pipe to determine if we are
|
||||
// running inside Replit and should use its sound API
|
||||
public static final boolean REPLIT_API = System.getenv("REPL_ID") != null && REPLIT_PIPE.exists();
|
||||
|
||||
// Whether the clips have been initialized yet
|
||||
|
@ -29,15 +30,15 @@ public enum Sound {
|
|||
// Because Replit does audio totally differently from desktop Java (it
|
||||
// supposedly supports PulseAudio over VNC but I just could not get that
|
||||
// working), and I would like to still be able to hear things when I play
|
||||
// the game directly on my laptop, we have a SoundBackend interface that is
|
||||
// implemented by both a builtin javax.sound.sampled-powered backend and a
|
||||
// Replit specific one
|
||||
private interface SoundBackend {
|
||||
// plays the specific sound
|
||||
public void play();
|
||||
// the game directly on my laptop, we have an abstract SoundBackend class
|
||||
// that is extended by both a builtin javax.sound.sampled-powered backend
|
||||
// and a Replit specific one
|
||||
private abstract class SoundBackend {
|
||||
// Plays the specific sound
|
||||
abstract public void play();
|
||||
}
|
||||
|
||||
private class NativeBackend implements SoundBackend {
|
||||
private class NativeBackend extends SoundBackend {
|
||||
// The audio clip associated with the sound
|
||||
private final Clip CLIP;
|
||||
|
||||
|
@ -68,7 +69,7 @@ public enum Sound {
|
|||
}
|
||||
}
|
||||
|
||||
private class ReplitBackend implements SoundBackend {
|
||||
private class ReplitBackend extends SoundBackend {
|
||||
// The JSON format to send requests to Replit with; the volume is 0.1
|
||||
// because my ears got blown out the first time I exploded when it was
|
||||
// at 100% volume (Replit is LOUD)
|
||||
|
@ -99,7 +100,7 @@ public enum Sound {
|
|||
}
|
||||
}
|
||||
|
||||
Sound(String fileName) {
|
||||
private Sound(String fileName) {
|
||||
FILE = new File(AUDIO_DIR, fileName);
|
||||
}
|
||||
|
||||
|
@ -135,7 +136,7 @@ public enum Sound {
|
|||
// The actual method that users outside this file will call to play sound
|
||||
public void play() {
|
||||
// Here is the check for if the option for sound is actually enabled
|
||||
if (Main.hasSound())
|
||||
if (Options.hasSound())
|
||||
soundBackend.play();
|
||||
}
|
||||
}
|
||||
|
|
13
Tile.java
13
Tile.java
|
@ -52,7 +52,7 @@ public class Tile extends JComponent {
|
|||
SPECIAL_TILES = specialTiles;
|
||||
ADJACENT_TILES = new HashSet<Tile>();
|
||||
|
||||
TILE_MOUSE_LISTENER = new MouseListener() {
|
||||
TILE_MOUSE_LISTENER = new MouseAdapter() {
|
||||
@Override
|
||||
public void mousePressed(MouseEvent e) {
|
||||
boolean rightClicked = false;
|
||||
|
@ -115,10 +115,6 @@ public class Tile extends JComponent {
|
|||
mouseOver = false;
|
||||
updatePressedState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
}
|
||||
};
|
||||
|
||||
setPreferredSize(new Dimension(SIZE, SIZE));
|
||||
|
@ -139,7 +135,7 @@ public class Tile extends JComponent {
|
|||
// 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))
|
||||
if (mine || this == startTile || Options.isProtectedStart() && ADJACENT_TILES.contains(startTile))
|
||||
return false;
|
||||
|
||||
mine = true;
|
||||
|
@ -248,7 +244,10 @@ public class Tile extends JComponent {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void paintComponent(Graphics g) {
|
||||
public void paintComponent(Graphics gr) {
|
||||
super.paintComponent(gr);
|
||||
Graphics2D g = (Graphics2D) gr;
|
||||
|
||||
g.drawImage(getImage(), 0, 0, this);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue