view game/Engine.java @ 70:2a7fd02504fa

Disable music again.
author Matti Hamalainen <ccr@tnsp.org>
date Fri, 25 Feb 2011 08:29:22 +0200
parents 163232ec225b
children 855757ab8580
line wrap: on
line source

/*
 * Ristipolku Game Engine
 * (C) Copyright 2011 Matti 'ccr' Hämäläinen <ccr@tnsp.org>
 */
package game;

import java.awt.*;
import java.awt.geom.*;
import java.awt.event.*;
import java.awt.image.*;
import java.awt.event.*;
import java.awt.font.*;
import javax.imageio.*;
import javax.swing.*;
import java.util.*;
import java.io.*;
import game.*;
import javax.sound.sampled.*;


/*
class AnimatedElement
{
    float x, y, stime, value;
    Interpolate lerp;
    boolean active;
    
    public AnimatedElement(float x, float y, )
    {
        stime = 0;
        this.x = x;
        this.y = y;
        
    }
    
    public animate(float time)
    {
        if (!active)
        {
            active = true;
            stime = time;
        }
        
        float t = (time - stime) / 10.0f;
        if (t < 100)
            value = lerp.getValue(t);
        else
        {
            
        }
    }
    
    public paint(Graphics2D g, );
    {
    }
}
*/

class GameBoard extends IDMWidget
{
    public static final int boardSize = 9;
    public static final int boardMiddle = 4;
    Piece[][] board;
    float pscale;

    public boolean flagGameOver;
    public int gameScore;

    public Piece currPiece, nextPiece;
    int currX, currY, currPoint;


    SoundManager soundManager;
    Sound sndPlaced;
    
    public GameBoard(IDMPoint pos, SoundManager smgr, float pscale)
    {
        super(pos);
        this.pscale = pscale;

        soundManager = smgr;
//        sndPlaced = soundManager.getSound("sounds/placed.wav");

        startNewGame();
    }
    
    public void startNewGame()
    {
        board = new Piece[boardSize][boardSize];
        board[boardMiddle][boardMiddle] = new Piece(PieceType.START);

        currX = boardMiddle;
        currY = boardMiddle;
        currPoint = 0;

        currPiece = null;
        nextPiece = new Piece(PieceType.ACTIVE);

        flagGameOver = false;
        pieceFinishTurn();
        gameScore = 0;
    }

    public void paintBackPlate(Graphics2D g)
    {
        g.setPaint(new Color(0.0f, 0.0f, 0.0f, 0.2f));
        g.setStroke(new BasicStroke(5.0f));
        g.draw(new RoundRectangle2D.Float(getScaledX(), getScaledY(),
            boardSize * pscale, boardSize * pscale,
            pscale / 5, pscale / 5));
    }
    
    public void paint(Graphics2D g)
    {
        for (int y = 0; y < boardSize; y++)
        for (int x = 0; x < boardSize; x++)
        if (board[x][y] != null)
        {
            board[x][y].paint(g,
                getScaledX() + (x * pscale),
                getScaledY() + (y * pscale),
                pscale - pscale / 10);
        }
    }

    public boolean contains(Point where)
    {
        return (where.x >= getScaledX() && where.y >= getScaledY() &&
                where.x < getScaledX() + boardSize * pscale &&
                where.y < getScaledY() + boardSize * pscale);
    }
    
    public void animate(float time)
    {
        for (int y = 0; y < boardSize; y++)
        for (int x = 0; x < boardSize; x++)
        if (board[x][y] != null)
        {
            board[x][y].animate(time);
        }
    }
   
    public void pieceRotate(Piece.RotateDir dir)
    {
        if (currPiece != null && !flagGameOver)
        {
            currPiece.rotate(dir);
        }
    }

    private void pieceMoveTo(int point)
    {
        switch (point)
        {
            case 0: currY--; break;
            case 1: currY--; break;

            case 2: currX++; break;
            case 3: currX++; break;

            case 4: currY++; break;
            case 5: currY++; break;

            case 6: currX--; break;
            case 7: currX--; break;
        }
    }

    public void pieceCreateNew()
    {
        currPiece = nextPiece;
        nextPiece = new Piece(PieceType.ACTIVE);
    }
    
    public void pieceSwapCurrent()
    {
        if (!flagGameOver)
        {
            Piece tmp = currPiece;
            currPiece = nextPiece;
            nextPiece = tmp;
            board[currX][currY] = currPiece;
        }
    }
    
    public boolean pieceCheck(Piece piece)
    {
        if (piece == null)
        {
            // Create new piece
            pieceCreateNew();
            board[currX][currY] = currPiece;
            return true;
        }
        else
        if (piece.getType() == PieceType.START)
        {
            if (currPiece != null)
            {
                // Hit center starting piece, game over
                flagGameOver = true;
                return true;
            }
            else
            {
                // Start piece as first piece means game is starting
                pieceMoveTo(currPoint);
                pieceCreateNew();
                board[currX][currY] = currPiece;
                return true;
            }
        }

        // Mark the current piece as locked
        piece.setType(PieceType.LOCKED);

        // Solve connection (with rotations) through the piece
        currPoint = piece.getRotatedPoint(piece.getMatchingPoint(currPoint));

        // Mark connection as active
        piece.setConnectionState(currPoint, true);

        // Solve exit point (with rotations)
        currPoint = piece.getAntiRotatedPoint(piece.getConnection(currPoint));

        // Move to next position accordingly
        pieceMoveTo(currPoint);
        return false;
    }

    public void pieceFinishTurn()
    {
        boolean finished = false;
        int connections = 0;

        if (currPiece != null)
        {
            soundManager.play(sndPlaced);
        }
        
        while (!finished)
        {
            if (currX >= 0 && currX < boardSize && currY >= 0 && currY < boardSize)
            {
                connections++;
                finished = pieceCheck(board[currX][currY]);
            }
            else
            {
                // Outside of the board, game over
                finished = true;
                flagGameOver = true;
            }
        }
        
        // Compute and add score
        gameScore += connections * connections;
        
        // If game over, clear the game
        if (flagGameOver)
        {
            currPiece = null;
        }
    }

    public boolean mouseWheelMoved(MouseWheelEvent e)
    {
        int notches = e.getWheelRotation();

        if (notches < 0)
            pieceRotate(Piece.RotateDir.LEFT);
        else
            pieceRotate(Piece.RotateDir.RIGHT);

        return true;
    }
    
    public boolean mouseClicked(MouseEvent e)
    {
        if (flagGameOver)
            return false;

        if (contains(e.getPoint()))
        {
            pieceFinishTurn();
            return true;
        }
        else
            return false;
    }

    public boolean keyPressed(KeyEvent e)
    {
        if (flagGameOver)
            return false;

        switch (e.getKeyCode())
        {
            case KeyEvent.VK_LEFT:
            case KeyEvent.VK_UP:
                pieceRotate(Piece.RotateDir.LEFT);
                return true;

            case KeyEvent.VK_RIGHT:
            case KeyEvent.VK_DOWN:
                pieceRotate(Piece.RotateDir.RIGHT);
                return true;

            case KeyEvent.VK_ENTER:
                pieceFinishTurn();
                return true;
        }
        return false;
    }
}


public class Engine extends JPanel
                    implements Runnable, KeyListener,
                    MouseListener, MouseWheelListener
{
    long startTime;
    float gameClock, gameFrames;
    Thread animThread;
    boolean animEnable = false;

    Font fontMain, font1, font2;
    FontMetrics metrics1, metrics2;
    
    GameBoard lauta = null;
    BufferedImage lautaBG = null, lautaBGScaled = null;
    Dimension lautaDim;

    static final AudioFormat sfmt = new AudioFormat(22050, 16, 1, true, false);
    SoundManager soundManager;
    InputStream musa;

    boolean aboutEnabled = false, aboutSecret = false;
    BufferedImage aboutImg = null;

    IDMContainer widgets;

    public Engine()
    {
        // Initialize globals
        System.out.print("Engine() constructor\n");
        
        // Sound system
        soundManager = new SoundManager(sfmt, 16);

        // Load resources
        try
        {
            ResourceLoader res = new ResourceLoader("graphics/board.jpg");
            lautaBG = ImageIO.read(res.getStream());

            res = new ResourceLoader("graphics/lavina.jpg");
            aboutImg = ImageIO.read(res.getStream());

            try {
                res = new ResourceLoader("graphics/font.ttf");
                fontMain = Font.createFont(Font.TRUETYPE_FONT, res.getStream());
                
                font1 = fontMain.deriveFont(24f);
                font2 = fontMain.deriveFont(64f);
            }
            catch (FontFormatException e)
            {
                System.out.print("Could not initialize fonts.\n");
            }
            
            res = new ResourceLoader("sounds/gamemusic.wav");
            musa = res.getStream();
        }
        catch (IOException e)
        {
            JOptionPane.showMessageDialog(null,
                e.getMessage(),
                "Initialization error",
                JOptionPane.ERROR_MESSAGE);

            System.out.print(e.getMessage());
        }

        // Create IDM GUI widgets
        widgets = new IDMContainer();

        widgets.add(new BtnSwapPiece(0.75f, 0.60f));
        widgets.add(new BtnAbout(0.75f, 0.75f));
        widgets.add(new BtnNewGame(0.75f, 0.85f));

        lauta = new GameBoard(new IDMPoint(0.09f, 0.18f), soundManager, 63);
        widgets.add(lauta);

        // Game
        aboutEnabled = false;
        startNewGame();
        
        // Initialize event listeners
        addKeyListener(this);
        addMouseListener(this);
        addMouseWheelListener(this);

        // Get initial focus
        if (!hasFocus())
        {
            System.out.print("Engine(): requesting focus\n");
            requestFocus();
        }
        
//        soundManager.play(musa);
    }

    public void startNewGame()
    {
        gameClock = 0;
        gameFrames = 0;
        startTime = new Date().getTime();
        lauta.startNewGame();
    }

    public void paintAbout(Graphics2D g)
    {
        int width = lautaDim.width / 2,
            height = lautaDim.height / 4;
        
        int x = (lautaDim.width - width) / 2,
            y = (lautaDim.height - height) / 2;
        
        g.setPaint(new Color(0.0f, 0.0f, 0.0f, 0.6f));
        g.fill(new RoundRectangle2D.Float(x, y, width, height, 10, 10));


        g.setFont(font1);
        g.setPaint(Color.white);

        g.drawString("RistiPolku (CrossPaths) v0.5", x + 10, y + 25);
        if (aboutSecret)
        {
            g.drawImage(aboutImg, x + 15, y + 35, aboutImg.getWidth(), aboutImg.getHeight(), null); 
            g.drawString("Dedicated to my", x + 225, y + 55);
            g.drawString("favorite woman", x + 225, y + 85);
            g.drawString("in the world.", x + 225, y + 115);
            g.drawString("- Matti", x + 350, y + 155);
        }
        else
        {
            g.setPaint(Color.yellow);
            g.drawString("(c) Copyright 2011 Matti 'ccr' Hämäläinen", x + 10, y + 55);

            g.setPaint(Color.white);
            g.drawString("Programming project for Java-course", x + 10, y + 105);
            g.drawString("T740306 taught by Kari Laitinen.", x + 10, y + 135);
        }
    }

    public void paintComponent(Graphics g)
    {
        Graphics2D g2 = (Graphics2D) g;
        
        // Use antialiasing when rendering the game elements
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                            RenderingHints.VALUE_ANTIALIAS_ON);

        g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
                            RenderingHints.VALUE_TEXT_ANTIALIAS_ON);


        // Rescale if parent component size has changed
        Dimension dim = getSize();
        if (lautaDim == null || !dim.equals(lautaDim))
        {
            // Rescale IDM GUI widgets
            widgets.setScale(dim.width, dim.height);

            // Rescale background image
            lautaBGScaled = new BufferedImage(dim.width, dim.height, BufferedImage.TYPE_INT_ARGB);
            Graphics2D gimg = lautaBGScaled.createGraphics();
            gimg.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                                  RenderingHints.VALUE_INTERPOLATION_BICUBIC);

            gimg.drawImage(lautaBG, 0, 0, dim.width, dim.height, null); 
            lauta.paintBackPlate(gimg);
            lautaDim = dim;

            System.out.print("scale changed\n");
        }
        
        // Get font metrics against current Graphics2D context
        if (metrics1 == null)
            metrics1 = g2.getFontMetrics(font1);

        if (metrics2 == null)
            metrics2 = g2.getFontMetrics(font2);
        

        // Draw background image, pieces, widgets
        g2.drawImage(lautaBGScaled, 0, 0, null);
        widgets.paint(g2);

        if (!lauta.flagGameOver)
        {
            if (lauta.nextPiece != null)
            {
                // Draw next piece
                AffineTransform save = g2.getTransform();
                lauta.nextPiece.paint(g2, 830, 325, 90);
                g2.setTransform(save);
            }
        }
        else
        {
            // Game over text
            String text = "Game Over!";
            int textWidth = metrics2.stringWidth(text);
            g2.setFont(font2);

            g2.setPaint(new Color(0.0f, 0.0f, 0.0f, 0.5f));
            g2.drawString(text, (dim.width - textWidth) / 2 + 5, dim.height / 2 + 5);

            double f = Math.sin(gameClock * 0.1) * 4.0;
            g2.setPaint(Color.white);
            g2.drawString(text, (dim.width - textWidth) / 2 + (float) f, dim.height / 2 + (float) f);
        }
        
        // Scores, etc
        g2.setFont(font2);
        g2.setPaint(Color.white);

        g2.drawString(""+ String.format("%05d", lauta.gameScore), dim.width - 230, 220);

        g2.setFont(font1);
        long currTime = new Date().getTime();
        g2.drawString("fps = "+ ((gameFrames * 1000) / (currTime - startTime)), dim.width - 120, 20);
        
        if (aboutEnabled)
        {
            paintAbout(g2);
        }
    }

    public void startThreads()
    {
        System.out.print("startThreads()\n");
        if (animThread == null)
        {
            animThread = new Thread(this);
            animEnable = true;
            animThread.start();
        }
    }
    
    public void stopThreads()
    {
        System.out.print("stopThreads()\n");
        
        // Stop animations
        if (animThread != null)
        {
            animThread.interrupt();
            animEnable = false;
            animThread = null;
        }

        // Shut down sound manager
        soundManager.close();
    }

    public void mouseEntered(MouseEvent e) { }
    public void mouseExited(MouseEvent e) { }

    public void mousePressed(MouseEvent e)
    {
        if (!aboutEnabled)
            widgets.mousePressed(e);
    }

    public void mouseReleased(MouseEvent e)
    {
        if (!aboutEnabled)
            widgets.mouseReleased(e);
    }

    public void mouseClicked(MouseEvent e)
    {
        if (!hasFocus())
        {
            System.out.print("Requesting focus\n");
            requestFocus();
        }
        if (!aboutEnabled)
        {
            lauta.mouseClicked(e);
        }
    }

    public void mouseWheelMoved(MouseWheelEvent e)
    {
        lauta.mouseWheelMoved(e);
    }

    public void keyTyped(KeyEvent e)
    {
    }
   
    public void keyReleased(KeyEvent e)
    {
    }
   
    public void keyPressed(KeyEvent e)
    {
        if (aboutEnabled)
        {
            if (e.getKeyCode() == KeyEvent.VK_L)
            {
                aboutSecret = true;
            }
            else
            {
                aboutEnabled = false;
                aboutSecret = false;
            }
        }
        else
        {
            // Handle keyboard input
            if (lauta.keyPressed(e))
                return;

            widgets.keyPressed(e);
        }
    }
    
    public void run()
    {
        while (animEnable)
        {
            // Progress game animation clock
            gameClock++;

            // Animate components
            lauta.animate(gameClock);
            if (lauta.nextPiece != null)
                lauta.nextPiece.animate(gameClock);
            
            // Repaint with a frame limiter
            if (gameClock % 4 == 1)
            {
                repaint();
                gameFrames++;
            }
            
            // Sleep for a moment
            try {
                Thread.sleep(10);
            }
            catch (InterruptedException x) {
            }
        }
    }
    
    class BtnNewGame extends IDMButton
    {
        public BtnNewGame(float x, float y)
        {
            super(x, y, KeyEvent.VK_ESCAPE, font1, "New Game");
        }

        public void clicked()
        {
            startNewGame();
        }
    }

    class BtnSwapPiece extends IDMButton
    {
        public BtnSwapPiece(float x, float y)
        {
            super(x, y, KeyEvent.VK_SPACE, font1, "Swap");
        }

        public void clicked()
        {
            lauta.pieceSwapCurrent();
        }
    }

    class BtnAbout extends IDMButton
    {
        public BtnAbout(float x, float y)
        {
            super(x, y, KeyEvent.VK_A, font1, "About");
        }

        public void clicked()
        {
            aboutEnabled = true;
        }
    }
}