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; } }