package dk.itu.mario.engine; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.GraphicsConfiguration; import java.awt.Point; import java.awt.Rectangle; import java.awt.Robot; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.image.BufferedImage; import java.awt.image.VolatileImage; import java.io.File; import java.util.Random; import javax.imageio.ImageIO; import javax.sound.sampled.LineUnavailableException; import javax.swing.JComponent; import dk.itu.mario.MarioInterface.LevelGenerator; import dk.itu.mario.engine.sonar.FakeSoundEngine; import dk.itu.mario.engine.sonar.SonarSoundEngine; import dk.itu.mario.engine.sprites.Mario; import dk.itu.mario.scene.LevelScene; import dk.itu.mario.scene.LevelSceneCustom; import dk.itu.mario.scene.LoseScene; import dk.itu.mario.scene.Scene; import dk.itu.mario.scene.WinScene; public class MarioComponent extends JComponent implements Runnable, KeyListener, FocusListener, MouseListener { private static final long serialVersionUID = 739318775993206607L; public static final int TICKS_PER_SECOND = 24; public static final int EVOLVE_VERSION = 4; public static final int GAME_VERSION = 4; private boolean running = false; private boolean screenshotTaken = false; private boolean useScale2x = false; private boolean isCustom = false; private int width, height; private GraphicsConfiguration graphicsConfiguration; private LevelGenerator levelGenerator; private Scene scene; private SonarSoundEngine sound; private Scale2x scale2x = new Scale2x(320, 240); public MarioComponent(int width, int height, boolean isCustomized, LevelGenerator levelGenerator) { addFocusListener(this); addMouseListener(this); addKeyListener(this); this.setFocusable(true); this.setEnabled(true); this.width = width; this.height = height; this.isCustom = isCustomized; Dimension size = new Dimension(width, height); setPreferredSize(size); setMinimumSize(size); setMaximumSize(size); try { sound = new SonarSoundEngine(64); } catch (LineUnavailableException e) { e.printStackTrace(); sound = new FakeSoundEngine(); } this.setFocusable(true); this.levelGenerator = levelGenerator; LevelScene.bothPlayed = false; } private void toggleKey(int keyCode, boolean isPressed) { if (scene == null) { return; } if (keyCode == KeyEvent.VK_LEFT) { scene.toggleKey(Mario.KEY_LEFT, isPressed); } if (keyCode == KeyEvent.VK_RIGHT) { scene.toggleKey(Mario.KEY_RIGHT, isPressed); } if (keyCode == KeyEvent.VK_DOWN) { scene.toggleKey(Mario.KEY_DOWN, isPressed); } if (keyCode == KeyEvent.VK_UP) { scene.toggleKey(Mario.KEY_UP, isPressed); } if (keyCode == KeyEvent.VK_A) { scene.toggleKey(Mario.KEY_SPEED, isPressed); } if (keyCode == KeyEvent.VK_S) { scene.toggleKey(Mario.KEY_JUMP, isPressed); } if (keyCode == KeyEvent.VK_ENTER) { scene.toggleKey(Mario.KEY_ENTER, isPressed); } if (isPressed && keyCode == KeyEvent.VK_F1) { useScale2x = !useScale2x; } if (keyCode == KeyEvent.VK_D) { if (!isPressed) { screenshotTaken = false; } else if (!screenshotTaken) { screenshotTaken = true; takeScreenShot(); } } if (isPressed && keyCode == KeyEvent.VK_ESCAPE) { try { System.exit(1); } catch (Exception e) { System.out.println("Unable to exit."); } } } private void takeScreenShot() { System.out.println("Taking screenshot."); Point loc = getLocationOnScreen(); Rectangle screenRect = new Rectangle(loc.x, loc.y, getWidth(), getHeight()); try { Robot robot = new Robot(); BufferedImage bufferedImg = robot.createScreenCapture(screenRect); File tempFile = File.createTempFile("screenshot", ".png", new File(".")); ImageIO.write(bufferedImg, "png", tempFile); System.out.println("Screeshot saved to current working directory: " + tempFile.getName()); } catch (Exception ex) { ex.printStackTrace(System.out); } } public void paint(Graphics g) { super.paint(g); } public void update(Graphics g) { } public void start() { if (!running) { running = true; new Thread(this, "Game Thread").start(); } } public void stop() { Art.stopMusic(); running = false; } public void run() { graphicsConfiguration = getGraphicsConfiguration(); Art.init(graphicsConfiguration, sound); VolatileImage image = createVolatileImage(320, 240); Graphics g = getGraphics(); Graphics og = image.getGraphics(); int lastTick = -1; long startTime = System.nanoTime(); float time = (System.nanoTime() - startTime) / 1000000000f; float now = time; float averagePassedTime = 0; boolean naiveTiming = true; // /Not sure I understand the weird dichotomy between LevelScene and // LevelInterface... randomLevel = new LevelSceneCustom(graphicsConfiguration, this, new Random().nextLong(), 0, 0, isCustom, levelGenerator); Mario.fire = false; Mario.large = false; Mario.coins = 0; Mario.lives = 3; randomLevel.init(); randomLevel.setSound(sound); scene = randomLevel; // / while (running) { float lastTime = time; time = (System.nanoTime() - startTime) / 1000000000f; float passedTime = time - lastTime; if (passedTime < 0) naiveTiming = false; // Stop relying on nanotime if it starts // skipping around in time (ie running // backwards at least once). This // sometimes happens on dual core amds. averagePassedTime = averagePassedTime * 0.9f + passedTime * 0.1f; if (naiveTiming) { now = time; } else { now += averagePassedTime; } int tick = (int) (now * TICKS_PER_SECOND); if (lastTick == -1) lastTick = tick; while (lastTick < tick) { scene.tick(); lastTick++; } float alpha = (float) (now * TICKS_PER_SECOND - tick); sound.clientTick(alpha); og.setColor(Color.WHITE); og.fillRect(0, 0, 320, 240); scene.render(og, alpha); if (!this.hasFocus() && tick / 4 % 2 == 0) { String msg = "CLICK TO PLAY"; drawString(og, msg, 160 - msg.length() * 4 + 1, 110 + 1, 0); drawString(og, msg, 160 - msg.length() * 4, 110, 7); } og.setColor(Color.BLACK); if (width != 320 || height != 240) { if (useScale2x) { g.drawImage(scale2x.scale(image), 0, 0, null); } else { g.drawImage(image, 0, 0, 640, 480, null); } } else { g.drawImage(image, 0, 0, null); } try { Thread.sleep(5); } catch (InterruptedException e) { } } Art.stopMusic(); } private void drawString(Graphics g, String text, int x, int y, int c) { char[] ch = text.toCharArray(); for (int i = 0; i < ch.length; i++) { g.drawImage(Art.font[ch[i] - 32][c], x + i * 8, y, null); } } public void keyPressed(KeyEvent arg0) { toggleKey(arg0.getKeyCode(), true); } public void keyReleased(KeyEvent arg0) { toggleKey(arg0.getKeyCode(), false); } public void keyTyped(KeyEvent arg0) { } public void focusGained(FocusEvent arg0) { } public void focusLost(FocusEvent arg0) { } public void levelWon() { } public static final int OPTIMIZED_FIRST = 0; public static final int MINIMIZED_FIRST = 1; private LevelScene randomLevel; public void lose() { scene = new LoseScene(); scene.setSound(sound); scene.init(); } public void win() { scene = new WinScene(); scene.setSound(sound); scene.init(); } public void mouseClicked(MouseEvent e) { } public void mouseEntered(MouseEvent e) { // TODO Auto-generated method stub } public void mouseExited(MouseEvent e) { // TODO Auto-generated method stub } public void mousePressed(MouseEvent e) { // TODO Auto-generated method stub } public void mouseReleased(MouseEvent e) { while (!hasFocus()) { System.out.println("FORCE IT"); requestFocus(); } } /** * Must return the actual fill of the viewable components */ public Dimension getPreferredSize() { return new Dimension(width, height); } }