org.bobstuff.bobball.ActivityStateEnum.java Source code

Java tutorial

Introduction

Here is the source code for org.bobstuff.bobball.ActivityStateEnum.java

Source

/*
  Copyright (c) 2015 Richard Martin. All rights reserved.
  Licensed under the terms of the BSD License, see LICENSE.txt
*/

package org.bobstuff.bobball;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.graphics.PointF;
import android.os.Bundle;
import android.os.Handler;
import android.os.Vibrator;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.text.Editable;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.style.ForegroundColorSpan;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

import org.bobstuff.bobball.GameLogic.GameEventStartBar;
import org.bobstuff.bobball.GameLogic.GameManager;
import org.bobstuff.bobball.GameLogic.GameState;
import org.bobstuff.bobball.GameLogic.Grid;
import org.bobstuff.bobball.Menus.menuHighScores;

enum ActivityStateEnum {
    GAMEINIT, GAMERUNNING, GAMEPAUSED, GAMELOST, GAMEWON, GAMELOST_TOPSCORE
}

public class BobBallActivity extends Activity implements SurfaceHolder.Callback, OnClickListener, OnTouchListener {
    static final int NUMBER_OF_FRAMES_PER_SECOND = 60;
    static final double TOUCH_DETECT_SQUARES = 2.5;
    static final int VIBRATE_LIVE_LOST_MS = 40;

    static final String STATE_GAME_MANAGER = "state_game_manager";
    static final String STATE_ACTIVITY = "state_activity_state";

    static final int playerId = 1; //fixme hardcoded playerid

    private int numPlayers = 1;
    private int level = 1;
    private int levelSeries = 0;

    private Handler handler = new Handler();
    private DisplayLoop displayLoop = new DisplayLoop();

    private SurfaceHolder surfaceHolder;
    private Scores scores;

    private int secretHandshake = 0;

    private PointF initialTouchPoint = null;

    private GameManager gameManager;
    private GameView gameView;
    private ActivityStateEnum activityState = ActivityStateEnum.GAMEINIT;

    private DrawerLayout drawerLayout;
    private TextView messageView;
    private TextView percentageCleared;
    private TextView totalPercentageCleared;
    private TextView bonusPoints;
    private TextView statusTopleft;
    private TextView statusBotleft;
    private TextView statusTopright;
    private TextView statusBotright;
    private Button button;
    private Button retryButton;
    private Button backToLevelSelectButton;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);

        setContentView(R.layout.main);

        SurfaceView surfaceView = (SurfaceView) findViewById(R.id.surface_view);
        surfaceView.setOnTouchListener(this);
        surfaceHolder = surfaceView.getHolder();
        surfaceHolder.setFormat(PixelFormat.RGB_565);
        surfaceHolder.addCallback(this);

        messageView = (TextView) findViewById(R.id.message_label);
        percentageCleared = (TextView) findViewById(R.id.percentageCleared);
        totalPercentageCleared = (TextView) findViewById(R.id.totalPercentageCleared);
        bonusPoints = (TextView) findViewById(R.id.bonusPoints);

        button = (Button) findViewById(R.id.continue_button);
        button.setOnClickListener(this);

        retryButton = (Button) findViewById(R.id.retryButton);
        backToLevelSelectButton = (Button) findViewById(R.id.backToLevelSelectButton);

        statusTopleft = (TextView) findViewById(R.id.status_topleft);
        statusTopright = (TextView) findViewById(R.id.status_topright);
        statusBotleft = (TextView) findViewById(R.id.status_botleft);
        statusBotright = (TextView) findViewById(R.id.status_botright);

        drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
        drawerLayout.setDrawerListener(new DrawerLayout.SimpleDrawerListener() {

            @Override
            public void onDrawerClosed(View drawerView) {
                super.onDrawerClosed(drawerView);
                if (gameManager.isPaused()) {
                    gameManager.togglePauseGameLoop();
                }
                changeToPauseScreen();
                onClick(drawerView);
            }

            @Override
            public void onDrawerOpened(View drawerView) {
                super.onDrawerOpened(drawerView);
                if (activityState == ActivityStateEnum.GAMERUNNING) {
                    if (!gameManager.isPaused()) {
                        gameManager.togglePauseGameLoop();
                    }
                    showPauseScreen();
                }
            }
        });

        drawerLayout.setFocusableInTouchMode(false); // if set to false, the drawer doesn't react to pressing the back key by default

        statusTopright.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                statusTopright.setTextColor(0xffCCCCFF);
                secretHandshake += 1;
            }
        });

        statusBotright.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                if (secretHandshake > 4) {
                    statusBotright.setTextColor(0xffCCCCFF);
                    //request a hidden service
                    Intent intent = new Intent("org.torproject.android.REQUEST_HS_PORT");
                    intent.setPackage("org.torproject.android");
                    intent.putExtra("hs_port", 8477);
                    startActivityForResult(intent, 9999);
                }
            }
        });

        Bundle extras = getIntent().getExtras();
        level = extras.getInt("level");
        numPlayers = extras.getInt("numPlayers");

        scores = new Scores(numPlayers);
        scores.loadScores();
    }

    public void onStart() {
        super.onStart();
        if (activityState == ActivityStateEnum.GAMEINIT) {
            resetGame(numPlayers, level);
            startGame();
        }
    }

    public void retryAction() {
        int retryAction = Settings.getRetryAction();

        if (retryAction == 0) { // Go back to level select
            finish();
        } else {
            if (retryAction == 1) { // restart last level lost, same numPlayers
                resetGame(numPlayers, Settings.getLastLevelFailed());
            }
            if (retryAction == 2) { // restart from last selected level
                resetGame(numPlayers, Settings.getSelectLevel() + 1);
            }
            if (retryAction == 3) { // restart from level 1
                resetGame(numPlayers, 1);
            }
            startGame();
            setMessageViewsVisible(false);
        }
    }

    public void retry(View view) {
        retryAction();
    }

    public void exit(View view) {
        if (gameManager.isPaused()) {
            gameManager.togglePauseGameLoop();
        }

        finish();
    }

    interface playstat {
        int call(Player p);
    }

    public SpannableStringBuilder formatPerPlayer(String fixed, playstat query) {
        SpannableStringBuilder sps = SpannableStringBuilder.valueOf(fixed);

        for (Player p : gameManager.getCurrGameState().getPlayers()) {
            if (p.getPlayerId() == 0)
                continue;
            SpannableString s = new SpannableString(String.valueOf(query.call(p)) + " ");
            s.setSpan(new ForegroundColorSpan(p.getColor()), 0, s.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
            sps.append(s);
        }
        return sps;
    }

    protected void update(final Canvas canvas, final GameState currGameState, long frameCounter) {

        final Player currPlayer = currGameState.getPlayer(playerId);

        if ((gameView != null)) {
            gameView.draw(canvas, currGameState);
        }

        if (frameCounter % displayLoop.ITERATIONS_PER_STATUSUPDATE == 0) {
            updateStatus(currGameState);
        }

        if (gameManager.hasWonLevel() && gameManager.allBarsFinished(currGameState)) {
            handler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    showWonScreen();
                }
            }, 100);
        } else if (gameManager.isGameLost()) {
            Settings.setLastLevelFailed(gameManager.getLevel());
            if (scores.isTopScore(currPlayer.getScore())) {
                promptUsername();
            } else {
                showDeadScreen();
            }
        }

    }

    private void updateStatus(final GameState currGameState) {

        SpannableStringBuilder timeLeftStr = SpannableStringBuilder
                .valueOf(getString(R.string.timeLeftLabel, gameManager.timeLeft() / 10));

        SpannableStringBuilder livesStr = formatPerPlayer(getString(R.string.livesLabel), new playstat() {
            @Override
            public int call(Player p) {
                return p.getLives();
            }
        });
        SpannableStringBuilder scoreStr = formatPerPlayer(getString(R.string.scoreLabel), new playstat() {
            @Override
            public int call(Player p) {
                return p.getScore();
            }
        });

        SpannableStringBuilder clearedStr = formatPerPlayer(getString(R.string.areaClearedLabel), new playstat() {
            @Override
            public int call(Player p) {
                Grid grid = currGameState.getGrid();
                if (grid != null)
                    return currGameState.getGrid().getPercentComplete(p.getPlayerId());
                else
                    return 0;
            }
        });

        //display fps
        if (secretHandshake >= 3) {

            float fps = displayLoop.getFPS();
            int color = (fps < NUMBER_OF_FRAMES_PER_SECOND * 0.98f ? Color.RED : Color.GREEN);
            SpannableString s = new SpannableString(String.format(" FPS: %2.1f", fps));

            s.setSpan(new ForegroundColorSpan(color), 0, s.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
            timeLeftStr.append(s);

            color = (gameManager.getUPS() < gameManager.NUMBER_OF_UPDATES_PER_SECOND * 0.98f ? Color.RED
                    : Color.GREEN);
            s = new SpannableString(String.format(" UPS: %3.1f", gameManager.getUPS()));

            s.setSpan(new ForegroundColorSpan(color), 0, s.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
            timeLeftStr.append(s);
        }

        statusTopleft.setText(timeLeftStr);
        statusTopright.setText(livesStr);
        statusBotleft.setText(scoreStr);
        statusBotright.setText(clearedStr);
    }

    private void promptUsername() {
        activityState = ActivityStateEnum.GAMELOST_TOPSCORE;
        final EditText input = new EditText(this);
        final String defaultName = Preferences.loadValue("defaultName", getString(R.string.defaultName));
        input.setHint(defaultName);
        new AlertDialog.Builder(this).setTitle(R.string.namePrompt).setMessage(R.string.highScoreAchieved)
                .setView(input).setPositiveButton(R.string.confirm, new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int whichButton) {
                        Editable value = input.getText();
                        String valueString = value.toString().trim();
                        if (valueString.isEmpty()) {
                            valueString = defaultName;
                        }
                        scores.addScore(valueString, gameManager.getCurrGameState().getPlayer(playerId).getScore());
                        showTopScores();
                    }
                }).show();
    }

    private void showTopScores() {
        finish();
        Intent intent = new Intent(this, menuHighScores.class);
        int currentScore = gameManager.getCurrGameState().getPlayer(playerId).getScore();
        intent.putExtra("rank", scores.getRank(currentScore));
        intent.putExtra("numPlayers", numPlayers);
        startActivity(intent);
    }

    private void showPauseScreen() {
        activityState = ActivityStateEnum.GAMEPAUSED;

        setMessageViewsVisible(true);
    }

    private void changeToPauseScreen() { // change drawer layout to pause screen
        messageView.setText(R.string.pausedText);
        button.setText(R.string.bttnTextResume);

        retryButton.setVisibility(View.GONE);
        button.setVisibility(View.VISIBLE);
        backToLevelSelectButton.setVisibility(View.VISIBLE);
        percentageCleared.setVisibility(View.GONE);
        totalPercentageCleared.setVisibility(View.GONE);
        bonusPoints.setVisibility(View.GONE);
    }

    private void showWonScreen() {
        activityState = ActivityStateEnum.GAMEWON;

        Statistics.saveHighestLevel(numPlayers, gameManager.getLevel() + 1);
        levelSeries++;
        Statistics.saveLongestSeries(levelSeries);

        GameState gs = gameManager.getCurrGameState();
        Grid currGrid = gs.getGrid();

        SpannableStringBuilder percentageCleared = new SpannableStringBuilder();

        String percentageString = currGrid.getPercentCompleteFloat(1) + "%";

        String totalPercentageString = getString(R.string.percentageCleared, percentageString);

        int player1Color = gs.getPlayer(1).getColor();

        SpannableString percentage = new SpannableString(totalPercentageString);
        int percentageStart = totalPercentageString.length() - percentageString.length();
        int percentageEnd = totalPercentageString.length();

        percentage.setSpan(new ForegroundColorSpan(player1Color), percentageStart, percentageEnd,
                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        percentageCleared.append(percentage);

        messageView.setText(getString(R.string.levelCompleted, gameManager.getLevel()));
        button.setText(R.string.nextLevel);
        retryButton.setVisibility(View.GONE);
        button.setVisibility(View.VISIBLE);
        backToLevelSelectButton.setVisibility(View.GONE);
        this.percentageCleared.setText(percentageCleared);
        this.percentageCleared.setVisibility(View.VISIBLE);

        if (numPlayers > 1) {
            totalPercentageCleared
                    .setText(getString(R.string.totalPercentageCleared, currGrid.getPercentCompleteFloat() + "%"));
            totalPercentageCleared.setVisibility(View.VISIBLE);
        }

        bonusPoints.setText(getString(R.string.bonusPoints, gameManager.getBonusPoints(gs, gs.getPlayer(1))));
        bonusPoints.setVisibility(View.VISIBLE);

        setMessageViewsVisible(true);
    }

    private void showDeadScreen() {
        activityState = ActivityStateEnum.GAMELOST;
        levelSeries = 0;
        messageView.setText(R.string.dead);

        button.setVisibility(View.GONE);
        retryButton.setVisibility(View.VISIBLE);
        backToLevelSelectButton.setVisibility(View.VISIBLE);
        percentageCleared.setVisibility(View.GONE);
        totalPercentageCleared.setVisibility(View.GONE);
        bonusPoints.setVisibility(View.GONE);

        setMessageViewsVisible(true);
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        if (gameView == null || gameManager == null)
            return true;

        PointF evPoint = gameView.transformPix2Coords(new PointF(event.getX(), event.getY()));

        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            initialTouchPoint = evPoint;
        } else if (event.getAction() == MotionEvent.ACTION_UP) {
            initialTouchPoint = null;
        } else if (event.getAction() == MotionEvent.ACTION_MOVE) {
            if (initialTouchPoint != null
                    && gameManager.getGrid().validPoint(initialTouchPoint.x, initialTouchPoint.y)) {
                Direction dir = null;
                if (evPoint.x > (initialTouchPoint.x + TOUCH_DETECT_SQUARES))
                    dir = Direction.RIGHT;
                else if (evPoint.x < initialTouchPoint.x - TOUCH_DETECT_SQUARES)
                    dir = Direction.LEFT;
                else if (evPoint.y > (initialTouchPoint.y + TOUCH_DETECT_SQUARES))
                    dir = Direction.DOWN;
                else if (evPoint.y < initialTouchPoint.y - TOUCH_DETECT_SQUARES)
                    dir = Direction.UP;

                if (dir != null && !gameManager.hasWonLevel()) {
                    gameManager.addEvent(
                            new GameEventStartBar(gameManager.getGameTime(), initialTouchPoint, dir, playerId));
                    initialTouchPoint = null;
                }
            }
        }

        return true;
    }

    private void reinitGame() {
        displayLoop.reset();
        if (gameView != null && gameManager != null)
            gameView.reset(gameManager.getCurrGameState());
    }

    private void resetGame(int numberPlayers, int level) {
        handler.removeCallbacks(displayLoop);
        if (gameManager != null)
            gameManager.stopGameLoop();
        gameManager = new GameManager();
        gameManager.newGame(numberPlayers, level);
        reinitGame();
    }

    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        gameView = new GameView(width, height);
        reinitGame();
    }

    public void surfaceCreated(SurfaceHolder holder) {
        //no-op
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
        handler.removeCallbacks(displayLoop);
        if (gameManager != null)
            gameManager.stopGameLoop();
        gameView = null;
    }

    @Override
    protected void onPause() {
        super.onPause();
        if (activityState == ActivityStateEnum.GAMERUNNING)
            showPauseScreen();
    }

    @Override
    protected void onResume() {
        super.onResume();
        drawGamePaused();
        if (activityState == ActivityStateEnum.GAMEPAUSED) {
            showPauseScreen();
        } else if (activityState == ActivityStateEnum.GAMELOST) {
            showDeadScreen();
        } else if (activityState == ActivityStateEnum.GAMELOST_TOPSCORE) {
            promptUsername();
        } else if (activityState == ActivityStateEnum.GAMEWON) {
            showWonScreen();
        }
    }

    private void drawGamePaused() {
        final GameState currGameState = new GameState(gameManager.getCurrGameState());

        Canvas canvas = surfaceHolder.lockCanvas();

        if (canvas != null) {
            update(canvas, currGameState, 0);
            surfaceHolder.unlockCanvasAndPost(canvas);
        }

        else {
            handler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    drawGamePaused();
                }
            }, 100);
        }
    }

    @Override
    public void onBackPressed() {
        if (activityState == ActivityStateEnum.GAMERUNNING)
            showPauseScreen();
        else { // finish when pressed on won, lost - or pause screen
            finish();
        }
    }

    @Override
    public void onSaveInstanceState(Bundle savedInstanceState) {
        // Save the user's current game state
        savedInstanceState.putParcelable(STATE_GAME_MANAGER, gameManager);
        savedInstanceState.putInt(STATE_ACTIVITY, activityState.ordinal());

        // Always call the superclass so it can save the view hierarchy state
        super.onSaveInstanceState(savedInstanceState);
    }

    @Override
    public void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
        // Restore value of members from saved state
        gameManager = savedInstanceState.getParcelable(STATE_GAME_MANAGER);
        activityState = ActivityStateEnum.values()[savedInstanceState.getInt(STATE_ACTIVITY, 0)];
        reinitGame();
    }

    @Override
    public void onClick(View v) { // called when the message button is clicked or the drawer closed
        setMessageViewsVisible(false);

        if (activityState == ActivityStateEnum.GAMEWON) {
            reinitGame();
            gameManager.nextLevel();
            startGame();
        } else if (activityState == ActivityStateEnum.GAMEPAUSED) {
            startGame();
        } else if (activityState == ActivityStateEnum.GAMELOST) {
            retryAction();
        }
    }

    private void startGame() {
        if (activityState == ActivityStateEnum.GAMELOST || activityState == ActivityStateEnum.GAMEINIT) {
            Statistics.increasePlayedGames();
        }

        activityState = ActivityStateEnum.GAMERUNNING;
        handler.post(displayLoop);
        if (gameManager != null)
            gameManager.startGameLoop();
        displayLoop.reset();
    }

    public void setMessageViewsVisible(boolean visible) {

        if (visible) {
            drawerLayout.openDrawer(GravityCompat.START);
        } else {
            drawerLayout.closeDrawer(GravityCompat.START);
        }
    }

    private class DisplayLoop implements Runnable {
        static final int ITERATIONS_PER_STATUSUPDATE = 10;

        private long fpsStatsLastTS = 0;
        private long frameCounter = 0;
        private int lastLives = 0;
        private int lastGameTime = -1;
        private float fps = 0;

        public float getFPS() {
            return fps;
        }

        public void reset() {
            fpsStatsLastTS = 0;
            lastGameTime = -1;
            frameCounter = 0;
            lastLives = 0;
        }

        @Override
        public void run() {
            long startTime = System.nanoTime();

            final GameState currGameState = new GameState(gameManager.getCurrGameState()); //make a copy of the state so its immutable
            final Player currPlayer = currGameState.getPlayer(playerId);
            //vibrate if we lost a live

            int livesLost = lastLives - currPlayer.getLives();
            if (livesLost > 0) {
                Vibrator vibs = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
                vibs.vibrate(VIBRATE_LIVE_LOST_MS);
            }
            lastLives = currPlayer.getLives();

            if (gameManager.getGameTime() > lastGameTime) {
                frameCounter++;
                lastGameTime = gameManager.getGameTime();

                Canvas canvas = surfaceHolder.lockCanvas();

                if (canvas != null) {
                    update(canvas, currGameState, frameCounter);
                    surfaceHolder.unlockCanvasAndPost(canvas);
                }

                //update FPS
                if (frameCounter % ITERATIONS_PER_STATUSUPDATE == 0) {
                    long currTime = System.nanoTime();
                    fps = (float) ITERATIONS_PER_STATUSUPDATE / (currTime - displayLoop.fpsStatsLastTS) * 1e9f;
                    displayLoop.fpsStatsLastTS = currTime;
                }
            }

            long updateTime = System.nanoTime() - startTime;
            long timeLeft = (long) ((1000L / NUMBER_OF_FRAMES_PER_SECOND) - (updateTime / 1000000.0));
            if (timeLeft < 5)
                timeLeft = 5;

            if (activityState == ActivityStateEnum.GAMERUNNING) {
                handler.postDelayed(this, timeLeft);
            }
        }
    }
}