Package au.com.schmick.game.cxn

Source Code of au.com.schmick.game.cxn.GameMessager

package au.com.schmick.game.cxn;

import java.awt.AlphaComposite;
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.SplashScreen;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.Point2D;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.swing.JFrame;
import javax.swing.JPanel;

import org.apache.commons.io.IOUtils;

/**
* @author mesketh
*/
public class GameManager implements Runnable, GameState {

    private final static String REACTION_SOUND = "beep-02.wav";
    private final static String LEVEL_SOUND = "todo";
    private byte[] reactorEffectRawData;

    private volatile boolean gameOver;
    private volatile boolean isRunning;
    private volatile boolean isPaused;
    protected volatile boolean isReactorAdded;
    protected volatile boolean isBetweenLevels;
    protected volatile boolean isGameInProgress;
    protected volatile boolean isOnFirstLevel = true;

    private volatile Object lock = new Object();

    private volatile int score;
    private volatile int levelScore;

    private JFrame gameFrame;

    private GameCanvas gameCanvas;

    private GameMessager gameMessager;

    private CardLayout cardManager;

    private volatile List<Ball> ballList = new ArrayList<Ball>();

    private List<Reactor> reactorList;

    private Thread animatorThread;

    private enum SplashScreenStages {

        INIT_UI_START("Initialising UI...", 0), INIT_EFFECTS_START(
                "Loading effects...", 0);

        private String desc;
        private int progress;

        /**
         * Splash screen msg details to display - progress == %progress.
         *
         * @param desc     Msg on LHS of the progress
         * @param progress (0-100)
         */
        SplashScreenStages(String desc, int progress) {
            this.desc = desc;
            this.progress = progress;
        }

        public String getDesc() {
            return desc;
        }

        public int getProgress() {
            return progress;
        }

        public String getMessage() {
            return getDesc();
        }

    }

    // Game layout tags
    private enum GameScreen {

        GAME_LEVEL, INFO
    }

    private void quitGame() {
        LevelManager.resetLevels();
        this.score = 0;
        this.levelScore = 0;
    }

    private void initaliseEffects() {
        try {
            this.reactorEffectRawData = IOUtils.toByteArray(GameManager.class
                    .getResourceAsStream("/sounds/" + REACTION_SOUND));
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public void run() {

        this.isRunning = true;
        do {
            if (!isBetweenLevels) {

                // System.out.println("Playing");
                updateGameState();
                this.gameCanvas.renderGameState();

                purgeReactors();
                purgeBalls();
            }

            if (!isBetweenLevels && isLevelOver()) {
                this.isBetweenLevels = true;
                this.isReactorAdded = false;
                updateScore();
                changeScreen(GameScreen.INFO);
                if (isGameFinished()) {
                    try {
                        changeScreen(GameScreen.INFO);
                        synchronized (this.lock) {
                            this.lock.wait();
                        }
                        isBetweenLevels = false;
                        changeScreen(GameScreen.GAME_LEVEL);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }

                } else {
                    try {
                        synchronized (this.lock) {
                            this.lock.wait();
                        }
                        changeScreen(GameScreen.GAME_LEVEL);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }
        } while (isRunning);

    }

    private void updateScore() {
        this.score += this.levelScore;
    }

    private void changeScreen(GameScreen targetScreen) {
        this.cardManager.show(this.gameFrame.getContentPane(),
                targetScreen.name());

        switch (targetScreen) {

            case GAME_LEVEL:
                this.gameCanvas.setFocusable(true);
                this.gameCanvas.requestFocus();
                break;
            case INFO:
                this.gameMessager.setFocusable(true);
                this.gameMessager.requestFocus();
                break;
        }
    }

    @Override
    public boolean isLevelOver() {

        boolean levelDone = false;

        if (this.isReactorAdded) { // level has begun (and may have finished)
            if (LevelManager.isLevelPassed(this)) {
                if (this.reactorList != null && this.reactorList.isEmpty()) {
                    levelDone = true; // level complete - advance to next level
                }
            } else if (this.reactorList != null && this.reactorList.isEmpty()) {
                levelDone = true; // level is done but, will have to be repeated
            }
        }

        return levelDone;
    }

    private void updateGameState() {
        if (!isBetweenLevels && !isPaused) {
            this.gameCanvas.moveBalls();
            this.checkCollisions();
        }
    }

    public boolean isGameStarted() {
        return isGameInProgress;
    }

    private void purgeReactors() {

        if (this.getReactors() != null) {

            Iterator<Reactor> ri = this.getReactors().iterator();

            while (ri.hasNext()) {
                Reactor nextReactor = ri.next();
                if (!nextReactor.isAlive()) {
                    ri.remove();
                }
            }
        }
    }

    private void purgeBalls() {

        if (this.getBalls() != null) {

            Iterator<Ball> bi = this.getBalls().iterator();

            while (bi.hasNext()) {
                Ball nextBall = bi.next();
                if (!nextBall.isAlive()) {
                    bi.remove();
                }
            }
        }
    }

    public void initialiseUI() {

        this.gameFrame = new JFrame(
                "Chain Rxn v1.0.0b - Copyright \u00a9 Schmick Software 2012");

        intialiseScreens(this.gameFrame.getContentPane());
        // TODO: Log point for splash

        gameFrame.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent we) {
                System.exit(0);
            }
        });

        changeScreen(GameScreen.INFO);

        this.gameFrame.pack();
        this.gameFrame.setVisible(true);
    }

    private void startGame() {

        if (this.animatorThread == null) {
            this.animatorThread = new Thread(this, "CHAINRXN-THREAD");
            this.animatorThread.start();
        } else {
            synchronized (this.lock) {
                lock.notify();
            }
        }

        changeScreen(GameScreen.GAME_LEVEL);
    }

    private void intialiseScreens(Container parentContainer) {
        this.cardManager = new CardLayout(10, 10);
        parentContainer.setLayout(this.cardManager);

        this.gameMessager = new GameMessager(this);
        this.gameMessager.addFocusListener(new FocusAdapter() {

            @Override
            public void focusLost(FocusEvent e) {
                // System.out.println("**** GameMessager has lost focus ****");
            }

        });

        parentContainer.add(this.gameMessager, GameScreen.INFO.name());
        parentContainer.add(this.gameCanvas = new GameCanvas(this),
                GameScreen.GAME_LEVEL.name());

        this.gameCanvas.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                GameManager.this.gameCanvas.requestFocusInWindow();
                if (!isReactorAdded && !isPaused) {
                    addReactor(e.getX(), e.getY());
                    GameManager.this.isReactorAdded = true;
                }
            }
        });

        this.gameCanvas.addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                if (e.getKeyCode() == KeyEvent.VK_P) {
                    if (GameManager.this.isGameInProgress
                            && !GameManager.this.isBetweenLevels) {
                        GameManager.this.isPaused = !isPaused;
                    }
                }
            }
        });

        this.gameMessager.addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                if (e.getKeyCode() == KeyEvent.VK_N) {
                    if (GameManager.this.isBetweenLevels
                            && LevelManager.isLevelPassed(GameManager.this)
                            && !GameManager.this.isGameFinished()) {
                        GameManager.this.loadNextLevel();
                        changeScreen(GameScreen.GAME_LEVEL);
                        GameManager.this.isBetweenLevels = false;
                        GameManager.this.isReactorAdded = false;
                        GameManager.this.levelScore = 0;
                        // TODO - should ask for confirmation here
                        synchronized (GameManager.this.lock) {
                            GameManager.this.lock.notify();
                        }
                    }
                } else if (e.getKeyCode() == KeyEvent.VK_X) {
                    stopGame();
                    quitGame();
                    prepareGame();
                    synchronized (GameManager.this.lock) {
                        GameManager.this.lock.notify();
                    }
                } else if (e.getKeyCode() == KeyEvent.VK_S) {
                    if (!GameManager.this.isGameInProgress) {
                        prepareGame();
                        startGame();
                    }
                } else if (e.getKeyCode() == KeyEvent.VK_A) {
                    if (GameManager.this.isGameInProgress
                            && GameManager.this.isBetweenLevels
                            && !LevelManager.isLevelPassed(GameManager.this)) {
                        GameManager.this.restartLevel();
                        changeScreen(GameScreen.GAME_LEVEL);
                        GameManager.this.isBetweenLevels = false;
                        GameManager.this.isReactorAdded = false;
                        synchronized (GameManager.this.lock) {
                            GameManager.this.lock.notify();
                        }
                    }
                }
            }

        });

    }

    /**
     * TODO
     */
    public void checkCollisions() {

        if (this.reactorList != null && !this.reactorList.isEmpty()) {

            List<Reactor> newReactorList = new ArrayList<Reactor>();
            for (Ball nextBall : this.ballList) {
                Iterator<Reactor> reactorIter = this.reactorList.iterator();
                while (reactorIter.hasNext()) {
                    Reactor nextReactor = reactorIter.next();
                    if (nextReactor.isCollidingWith(nextBall)) {
                        Point2D intersection = calculatePointOfIntersection(
                                nextReactor, nextBall);
                        newReactorList.add(new Reactor(intersection,
                                calculateScore()));
                        nextBall.setAlive(false);

                        playSound();
                    }
                }
            }

            if (!newReactorList.isEmpty()) {
                this.reactorList.addAll(newReactorList);
            }
        }
    }

    private int calculateScore() {
        if (this.reactorList.isEmpty())
            return levelScore;

        if (this.reactorList.size() == 1) {
            levelScore += LevelManager.getCurrentLevel().getBallScore();
        } else {
            // peg the score to the length of the chain (chain length * level
            // ball score)
            levelScore += (this.reactorList.size())
                    * LevelManager.getCurrentLevel().getBallScore();
        }
        return levelScore;
    }

    private final void addReactor(int x, int y) {

        if (this.reactorList == null) {
            this.reactorList = new ArrayList<Reactor>();
        }

        this.reactorList.add(new Reactor(x, y));

    }

    private Point2D calculatePointOfIntersection(Reactor reactor, Ball ball) {

        Point2D reactorCentre = reactor.getCentre();
        Point2D ballCentre = ball.getCentre();

        double ballToReactorRatio = ((double) ball.getRadius() / (double) reactor
                .getRadius());
        double dx = (Math.abs(reactorCentre.getX() - ball.getX()))
                - ballToReactorRatio
                * Math.abs(reactorCentre.getX() - ball.getX());
        double dy = (Math.abs(reactorCentre.getY() - ball.getY()))
                - ballToReactorRatio
                * Math.abs(reactorCentre.getY() - ball.getY());

        if (ballCentre.getX() <= reactorCentre.getX()) {
            dx = -dx;
        }

        if (ballCentre.getY() <= reactorCentre.getY()) {
            dy = -dy;
        }

        Point intersection = new Point((Point) reactorCentre);
        intersection.translate(BigDecimal.valueOf(dx).intValue(), BigDecimal
                .valueOf(dy).intValue());
        return intersection;
    }

    @Override
    public boolean isPaused() {
        return this.isPaused;
    }

    @Override
    public List<Reactor> getReactors() {
        return this.reactorList;
    }

    @Override
    public List<Ball> getBalls() {
        return this.ballList;
    }

    // game lifecycle //

    private void prepareGame() {

        loadNextLevel();
        this.isGameInProgress = true;
        // startGame();
    }

    void loadNextLevel() {
        LevelManager.nextLevel();
        loadLevel();
    }

    private void loadLevel() {
        this.ballList.clear();
        for (int i = 0; i < LevelManager.getCurrentLevel().getMaxBalls(); i++) {
            this.ballList.add(new Ball());
        }
    }

    /**
     * {@inheritDoc}
     */
    public void restartLevel() {
        this.reactorList.clear();
        this.loadLevel();
        this.score -= this.levelScore;
        this.levelScore = 0;
        // this.loadNextLevel();
    }

    private void stopGame() {
        // this.isRunning = false;
        this.isGameInProgress = false;
    }

    @Override
    public int getCurrentScore() {
        return this.score;
    }

    @Override
    public int getBallsLeft() {
        return LevelManager.getCurrentLevel().getMinBallsToPass()
                - (LevelManager.getCurrentLevel().getMaxBalls() - this.ballList
                .size());
    }

    private void init() {

        updateProgress(SplashScreenStages.INIT_EFFECTS_START);
        initaliseEffects();

        try {
            Thread.sleep(1500);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        updateProgress(SplashScreenStages.INIT_UI_START);
        initialiseUI();
    }

    public static void main(String[] args) {
        GameManager mgr = new GameManager();
        mgr.init();
    }

    @Override
    public boolean isAdvancing() {
        boolean advancing = false;

        int ballsToAdvance = getBallsLeft();
        if (ballsToAdvance > 0) {
            advancing = false;
        } else {
            advancing = true;
        }

        return advancing;
    }

    @Override
    public boolean isGameFinished() {
        return LevelManager.isLastLevel() && LevelManager.isLevelPassed(this);
    }

    public synchronized void playSound() {
        new Thread(new Runnable() {
            public void run() {
                try {
                    Clip clip = AudioSystem.getClip();
                    AudioInputStream effectStream = AudioSystem
                            .getAudioInputStream(new ByteArrayInputStream(
                                    GameManager.this.reactorEffectRawData));
                    clip.open(effectStream);
                    clip.start();
                } catch (Exception e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }).start();
    }

    private void updateProgress(SplashScreenStages stage) {
        SplashScreen ss = SplashScreen.getSplashScreen();
        if (ss != null) {
            Graphics2D ssg = ss.createGraphics();
            ssg.setComposite(AlphaComposite.Clear);
            ssg.fillRect(450, GameCanvas.PREFERRED_SIZE.height - 70,
                    GameCanvas.PREFERRED_SIZE.width,
                    GameCanvas.PREFERRED_SIZE.height);
            ssg.setPaintMode();

            Font f = new Font("Courier", Font.BOLD, 14);
            ssg.setFont(f);
            FontMetrics fm = ssg.getFontMetrics(f);

            ssg.drawString(stage.getMessage(), 450,
                    GameCanvas.PREFERRED_SIZE.height - 40
                            - (fm.getMaxAscent() + fm.getMaxDescent()));
            ss.update();
        }
    }
}

@SuppressWarnings("serial")
class GameMessager extends JPanel {

    GameState gameState;

    GameMessager(GameState gameState) {
        this.gameState = gameState;
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(640, 480);
    }

    public void paintComponent(Graphics g) {
        clearBackground((Graphics2D) g);
        renderMessage((Graphics2D) g);
    }

    private void clearBackground(Graphics2D g) {
        g.setBackground(Color.BLACK);
        g.clearRect(0, 0, getSize().width, getSize().height);
    }

    private String getLevelMessage() {

        // display the splash screen before game starts
        if (!this.gameState.isGameStarted()) {
            // TODO: Check
            return String.format("Welcome - Press S to start");
        } else if (this.gameState.isAdvancing()) {
            if (!this.gameState.isGameFinished()) {
                return String
                        .format("Current Score: %d\n\n Press N to proceed to next level",
                                this.gameState.getCurrentScore());
            } else {
                return String
                        .format("Game finished - your score was: %d - Press S to play again",
                                this.gameState.getCurrentScore());
            }
        } else {
            return String
                    .format("Failed to complete level - Press A to try again");
        }
    }

    public void renderMessage(Graphics2D g) {
        Color currColor = g.getColor();
        g.setColor(Color.WHITE);
        g.drawString(getLevelMessage(), 50, 50);
        g.setColor(currColor);
    }

}
TOP

Related Classes of au.com.schmick.game.cxn.GameMessager

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.