322 lines
13 KiB
Java
322 lines
13 KiB
Java
import javax.swing.*;
|
||
import java.awt.*;
|
||
import java.awt.event.*;
|
||
import java.awt.geom.*;
|
||
import java.awt.image.*;
|
||
|
||
public class MainMenu extends JPanel
|
||
{
|
||
public JPanel container;
|
||
private CardLayout cardLayout;
|
||
|
||
// -------------------------------------------------------------------------
|
||
// LevelButton — a custom JButton that paints its own normal / rollover icon.
|
||
//
|
||
// Normal state : small rounded tile, muted colour, level number centred.
|
||
// Hover state : tile scales up (~1.4×), bright glow ring appears around
|
||
// the tile, the label changes from "Level N" to the full
|
||
// level name, and the background darkens so the button pops.
|
||
// The change is purely painted — no external image files needed.
|
||
// -------------------------------------------------------------------------
|
||
private static class LevelButton extends JButton
|
||
{
|
||
private static final int BASE_SIZE = 100; // icon canvas (px)
|
||
private static final int INNER = 80; // tile size at rest
|
||
private static final int INNER_HOV = 110; // tile size on hover (drawn into same canvas)
|
||
|
||
private final int level;
|
||
private final String levelName;
|
||
private final Color tileColour;
|
||
private final Color hoverColour;
|
||
private boolean hovered = false;
|
||
|
||
LevelButton(int level, String levelName, Color tile, Color hover)
|
||
{
|
||
this.level = level;
|
||
this.levelName = levelName;
|
||
this.tileColour = tile;
|
||
this.hoverColour = hover;
|
||
|
||
setContentAreaFilled(false);
|
||
setBorderPainted(false);
|
||
setFocusPainted(false);
|
||
setOpaque(false);
|
||
|
||
// Build and set the two icons
|
||
setIcon(buildIcon(false));
|
||
setRolloverEnabled(true);
|
||
setRolloverIcon(buildIcon(true));
|
||
|
||
// Size the button to comfortably hold the larger hover icon + text
|
||
setPreferredSize(new Dimension(160, 160));
|
||
setSize(new Dimension(160, 160));
|
||
|
||
// Track hover so we can repaint the button text colour
|
||
addMouseListener(new MouseAdapter() {
|
||
@Override public void mouseEntered(MouseEvent e) { hovered = true; repaint(); }
|
||
@Override public void mouseExited (MouseEvent e) { hovered = false; repaint(); }
|
||
});
|
||
|
||
// Text sits below the icon, painted by paintComponent
|
||
setVerticalTextPosition(SwingConstants.BOTTOM);
|
||
setHorizontalTextPosition(SwingConstants.CENTER);
|
||
setFont(new Font("Arial", Font.BOLD, 13));
|
||
setForeground(Color.WHITE);
|
||
}
|
||
|
||
// Paints the button — lets us switch label text and colour dynamically
|
||
@Override
|
||
protected void paintComponent(Graphics g)
|
||
{
|
||
super.paintComponent(g);
|
||
Graphics2D g2 = (Graphics2D) g.create();
|
||
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||
|
||
// Dynamic label below the icon
|
||
String label = hovered ? levelName : "Level " + level;
|
||
Color fg = hovered ? hoverColour.brighter().brighter() : Color.LIGHT_GRAY;
|
||
|
||
FontMetrics fm = g2.getFontMetrics(getFont());
|
||
int tx = (getWidth() - fm.stringWidth(label)) / 2;
|
||
int ty = getHeight() - 8;
|
||
|
||
g2.setFont(getFont());
|
||
// Drop shadow
|
||
g2.setColor(Color.BLACK);
|
||
g2.drawString(label, tx + 1, ty + 1);
|
||
g2.setColor(fg);
|
||
g2.drawString(label, tx, ty);
|
||
|
||
g2.dispose();
|
||
}
|
||
|
||
// ── Icon factory ──────────────────────────────────────────────────────
|
||
private ImageIcon buildIcon(boolean hover)
|
||
{
|
||
int canvasSize = BASE_SIZE + 40; // 140 px canvas so hover tile fits
|
||
BufferedImage img = new BufferedImage(canvasSize, canvasSize,
|
||
BufferedImage.TYPE_INT_ARGB);
|
||
Graphics2D g = img.createGraphics();
|
||
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
|
||
RenderingHints.VALUE_ANTIALIAS_ON);
|
||
|
||
int cx = canvasSize / 2;
|
||
int cy = canvasSize / 2;
|
||
|
||
if (hover) {
|
||
// ── HOVER: large glowing tile ─────────────────────────────
|
||
|
||
// Outer glow rings (three, fading outward)
|
||
for (int i = 3; i >= 1; i--) {
|
||
int ringR = INNER_HOV / 2 + i * 10;
|
||
float alpha = 0.15f * i;
|
||
g.setColor(new Color(
|
||
hoverColour.getRed() / 255f,
|
||
hoverColour.getGreen()/ 255f,
|
||
hoverColour.getBlue() / 255f,
|
||
alpha));
|
||
g.fillOval(cx - ringR, cy - ringR, ringR * 2, ringR * 2);
|
||
}
|
||
|
||
// Tile background (bright)
|
||
int hs = INNER_HOV;
|
||
g.setColor(hoverColour);
|
||
g.fillRoundRect(cx - hs/2, cy - hs/2, hs, hs, 22, 22);
|
||
|
||
// Bright border stroke
|
||
g.setColor(Color.WHITE);
|
||
g.setStroke(new BasicStroke(3f));
|
||
g.drawRoundRect(cx - hs/2, cy - hs/2, hs, hs, 22, 22);
|
||
|
||
// Big level number (white, centred in tile)
|
||
g.setColor(Color.WHITE);
|
||
g.setFont(new Font("Arial", Font.BOLD, 42));
|
||
FontMetrics fm = g.getFontMetrics();
|
||
String num = String.valueOf(level);
|
||
g.drawString(num,
|
||
cx - fm.stringWidth(num) / 2,
|
||
cy + fm.getAscent() / 2 - 2);
|
||
|
||
// Stars drawn in upper-right corner of tile (difficulty indicator)
|
||
drawStars(g, cx + hs/2 - 28, cy - hs/2 + 4, level);
|
||
|
||
} else {
|
||
// ── NORMAL: small muted tile ──────────────────────────────
|
||
|
||
int ns = INNER;
|
||
g.setColor(tileColour);
|
||
g.fillRoundRect(cx - ns/2, cy - ns/2, ns, ns, 16, 16);
|
||
|
||
// Subtle border
|
||
g.setColor(tileColour.brighter());
|
||
g.setStroke(new BasicStroke(1.5f));
|
||
g.drawRoundRect(cx - ns/2, cy - ns/2, ns, ns, 16, 16);
|
||
|
||
// Level number
|
||
g.setColor(Color.WHITE);
|
||
g.setFont(new Font("Arial", Font.BOLD, 30));
|
||
FontMetrics fm = g.getFontMetrics();
|
||
String num = String.valueOf(level);
|
||
g.drawString(num,
|
||
cx - fm.stringWidth(num) / 2,
|
||
cy + fm.getAscent() / 2 - 2);
|
||
|
||
// Stars (smaller)
|
||
drawStars(g, cx + ns/2 - 22, cy - ns/2 + 2, level);
|
||
}
|
||
|
||
g.dispose();
|
||
return new ImageIcon(img);
|
||
}
|
||
|
||
/** Draw N filled gold stars in a row, starting at (x, y). */
|
||
private void drawStars(Graphics2D g, int x, int y, int count)
|
||
{
|
||
g.setColor(new Color(255, 220, 50));
|
||
int starSize = 10;
|
||
for (int i = 0; i < count; i++) {
|
||
drawStar(g, x - i * (starSize + 2), y, starSize);
|
||
}
|
||
}
|
||
|
||
private void drawStar(Graphics2D g, int cx, int cy, int size)
|
||
{
|
||
int pts = 5;
|
||
double outerR = size / 2.0;
|
||
double innerR = outerR * 0.4;
|
||
int[] xs = new int[pts * 2];
|
||
int[] ys = new int[pts * 2];
|
||
for (int i = 0; i < pts * 2; i++) {
|
||
double angle = Math.PI / pts * i - Math.PI / 2;
|
||
double r = (i % 2 == 0) ? outerR : innerR;
|
||
xs[i] = (int) (cx + r * Math.cos(angle));
|
||
ys[i] = (int) (cy + r * Math.sin(angle));
|
||
}
|
||
g.fillPolygon(xs, ys, pts * 2);
|
||
}
|
||
}
|
||
|
||
// =========================================================================
|
||
// MainMenu constructor
|
||
// =========================================================================
|
||
public MainMenu(GameFields gm)
|
||
{
|
||
setLayout(null);
|
||
setPreferredSize(new Dimension(640, 360));
|
||
setBackground(new Color(20, 20, 40)); // dark navy background
|
||
|
||
// ── Title label ──────────────────────────────────────────────────────
|
||
JLabel title = new JLabel("SELECT A LEVEL", SwingConstants.CENTER);
|
||
title.setFont(new Font("Arial", Font.BOLD, 28));
|
||
title.setForeground(Color.WHITE);
|
||
title.setBounds(0, 20, 640, 40);
|
||
add(title);
|
||
|
||
// ── Three level buttons ───────────────────────────────────────────────
|
||
// Colours: Level 1 = teal/cyan, Level 2 = orange, Level 3 = red
|
||
LevelButton lvl1 = new LevelButton(1, "The Crossing",
|
||
new Color(40, 100, 110), // muted teal (normal)
|
||
new Color(0, 200, 220)); // bright cyan (hover)
|
||
|
||
LevelButton lvl2 = new LevelButton(2, "Double Danger",
|
||
new Color(120, 70, 20), // muted orange (normal)
|
||
new Color(255, 150, 0)); // bright orange (hover)
|
||
|
||
LevelButton lvl3 = new LevelButton(3, "The Gauntlet",
|
||
new Color(110, 20, 20), // muted red (normal)
|
||
new Color(255, 60, 60)); // bright red (hover)
|
||
|
||
// Position buttons evenly across the panel (panel is 640 wide)
|
||
// Each button is 160 px wide; 3 buttons = 480 px; padding = 80 px each side
|
||
lvl1.setBounds(80, 100, 160, 160);
|
||
lvl2.setBounds(240, 100, 160, 160);
|
||
lvl3.setBounds(400, 100, 160, 160);
|
||
|
||
add(lvl1);
|
||
add(lvl2);
|
||
add(lvl3);
|
||
|
||
// Each button starts the corresponding level via levelStart()
|
||
lvl1.addActionListener(e -> gm.levelStart(gm.playerName1, gm.playerName2, "LEVEL1"));
|
||
lvl2.addActionListener(e -> gm.levelStart(gm.playerName1, gm.playerName2, "LEVEL2"));
|
||
lvl3.addActionListener(e -> gm.levelStart(gm.playerName1, gm.playerName2, "LEVEL3"));
|
||
|
||
|
||
// ── Difficulty radio buttons (unchanged from original) ────────────────
|
||
JPanel difficultyPanel = new JPanel();
|
||
difficultyPanel.setOpaque(false);
|
||
difficultyPanel.setLayout(new FlowLayout(FlowLayout.CENTER, 10, 0));
|
||
|
||
JRadioButton easy = new JRadioButton("Easy", true);
|
||
JRadioButton medium = new JRadioButton("Medium");
|
||
JRadioButton hard = new JRadioButton("Hard");
|
||
|
||
for (JRadioButton rb : new JRadioButton[]{easy, medium, hard}) {
|
||
rb.setForeground(Color.WHITE);
|
||
rb.setOpaque(false);
|
||
}
|
||
|
||
ButtonGroup difficultyGroup = new ButtonGroup();
|
||
difficultyGroup.add(easy);
|
||
difficultyGroup.add(medium);
|
||
difficultyGroup.add(hard);
|
||
|
||
difficultyPanel.add(new JLabel("Difficulty:") {{
|
||
setForeground(Color.LIGHT_GRAY);
|
||
}});
|
||
difficultyPanel.add(easy);
|
||
difficultyPanel.add(medium);
|
||
difficultyPanel.add(hard);
|
||
difficultyPanel.setBounds(160, 278, 320, 30);
|
||
add(difficultyPanel);
|
||
|
||
easy .addActionListener(e -> gm.setDifficulty("Easy"));
|
||
medium.addActionListener(e -> gm.setDifficulty("Medium"));
|
||
hard .addActionListener(e -> gm.setDifficulty("Hard"));
|
||
|
||
// ── Quit button ───────────────────────────────────────────────────────
|
||
JButton quit = new JButton("QUIT");
|
||
quit.setFont(new Font("Arial", Font.BOLD, 12));
|
||
quit.setForeground(Color.WHITE);
|
||
quit.setBackground(new Color(80, 20, 20));
|
||
quit.setBorder(BorderFactory.createLineBorder(new Color(180, 60, 60), 2));
|
||
quit.setFocusPainted(false);
|
||
quit.setBounds(275, 75, 90, 28);
|
||
add(quit);
|
||
|
||
quit.addActionListener(e -> {
|
||
String[] lines = {"", "", "", // Top padding
|
||
"GAME !!", "",
|
||
"Developed by","Aimee Azzahra","Ayan Mishra", "Jennifer Phan",
|
||
"Character Art by Jennifer Phan",
|
||
"Background Art by Aimee Azzahra",
|
||
"other credits ig idk",
|
||
"Thanks for playing!",
|
||
"", "", "", "","",""}; //bottom padding
|
||
CreditsPanel credits = new CreditsPanel(lines, () -> System.exit(0));
|
||
container.add(credits, "CREDITS");
|
||
cardLayout.show(container, "CREDITS");
|
||
credits.start();
|
||
});
|
||
}
|
||
|
||
// Paint the dark gradient background
|
||
@Override
|
||
protected void paintComponent(Graphics g)
|
||
{
|
||
Graphics2D g2 = (Graphics2D) g;
|
||
GradientPaint gp = new GradientPaint(0, 0, new Color(10, 10, 30),
|
||
0, getHeight(), new Color(30, 10, 50));
|
||
g2.setPaint(gp);
|
||
g2.fillRect(0, 0, getWidth(), getHeight());
|
||
super.paintComponent(g); // paint children
|
||
}
|
||
|
||
public void setContainer(JPanel container, CardLayout layout)
|
||
{
|
||
this.container = container;
|
||
this.cardLayout = layout;
|
||
}
|
||
}
|
||
|