Original author: jmtrivi Merged from: //branches/cupcake/... Original author: android-build Automated import of CL 147304
1435 lines
49 KiB
Java
Executable File
1435 lines
49 KiB
Java
Executable File
/*
|
|
* Copyright (C) 2009 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
*/
|
|
|
|
// Android JET demonstration code:
|
|
// All inline comments related to the use of the JetPlayer class are preceded by "JET info:"
|
|
|
|
package com.example.android.jetboy;
|
|
|
|
import android.content.Context;
|
|
import android.content.res.Resources;
|
|
import android.graphics.Bitmap;
|
|
import android.graphics.BitmapFactory;
|
|
import android.graphics.Canvas;
|
|
import android.media.JetPlayer;
|
|
import android.media.JetPlayer.OnJetEventListener;
|
|
import android.os.Bundle;
|
|
import android.os.Handler;
|
|
import android.os.Message;
|
|
import android.util.AttributeSet;
|
|
import android.util.Log;
|
|
import android.view.KeyEvent;
|
|
import android.view.SurfaceHolder;
|
|
import android.view.SurfaceView;
|
|
import android.view.View;
|
|
import android.widget.Button;
|
|
import android.widget.TextView;
|
|
|
|
import java.util.Random;
|
|
import java.util.Timer;
|
|
import java.util.TimerTask;
|
|
import java.util.Vector;
|
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
|
|
|
public class JetBoyView extends SurfaceView implements SurfaceHolder.Callback {
|
|
|
|
// the number of asteroids that must be destroyed
|
|
public static final int mSuccessThreshold = 50;
|
|
|
|
// used to calculate level for mutes and trigger clip
|
|
public int mHitStreak = 0;
|
|
|
|
// total number asteroids you need to hit.
|
|
public int mHitTotal = 0;
|
|
|
|
// which music bed is currently playing?
|
|
public int mCurrentBed = 0;
|
|
|
|
// a lazy graphic fudge for the initial title splash
|
|
private Bitmap mTitleBG;
|
|
|
|
private Bitmap mTitleBG2;
|
|
|
|
/**
|
|
* Base class for any external event passed to the JetBoyThread. This can
|
|
* include user input, system events, network input, etc.
|
|
*/
|
|
class GameEvent {
|
|
public GameEvent() {
|
|
eventTime = System.currentTimeMillis();
|
|
}
|
|
|
|
long eventTime;
|
|
}
|
|
|
|
/**
|
|
* A GameEvent subclass for key based user input. Values are those used by
|
|
* the standard onKey
|
|
*/
|
|
class KeyGameEvent extends GameEvent {
|
|
/**
|
|
* Simple constructor to make populating this event easier.
|
|
*/
|
|
public KeyGameEvent(int keyCode, boolean up, KeyEvent msg) {
|
|
this.keyCode = keyCode;
|
|
this.msg = msg;
|
|
this.up = up;
|
|
}
|
|
|
|
public int keyCode;
|
|
public KeyEvent msg;
|
|
public boolean up;
|
|
}
|
|
|
|
/**
|
|
* A GameEvent subclass for events from the JetPlayer.
|
|
*/
|
|
class JetGameEvent extends GameEvent {
|
|
/**
|
|
* Simple constructor to make populating this event easier.
|
|
*/
|
|
public JetGameEvent(JetPlayer player, short segment, byte track, byte channel,
|
|
byte controller, byte value) {
|
|
this.player = player;
|
|
this.segment = segment;
|
|
this.track = track;
|
|
this.channel = channel;
|
|
this.controller = controller;
|
|
this.value = value;
|
|
}
|
|
|
|
public JetPlayer player;
|
|
public short segment;
|
|
public byte track;
|
|
public byte channel;
|
|
public byte controller;
|
|
public byte value;
|
|
}
|
|
|
|
// JET info: the JetBoyThread receives all the events from the JET player
|
|
// JET info: through the OnJetEventListener interface.
|
|
class JetBoyThread extends Thread implements OnJetEventListener {
|
|
|
|
/**
|
|
* State-tracking constants.
|
|
*/
|
|
public static final int STATE_START = -1;
|
|
public static final int STATE_PLAY = 0;
|
|
public static final int STATE_LOSE = 1;
|
|
public static final int STATE_PAUSE = 2;
|
|
public static final int STATE_RUNNING = 3;
|
|
|
|
// how many frames per beat? The basic animation can be changed for
|
|
// instance to 3/4 by changing this to 3.
|
|
// untested is the impact on other parts of game logic for non 4/4 time.
|
|
private static final int ANIMATION_FRAMES_PER_BEAT = 4;
|
|
|
|
public boolean mInitialized = false;
|
|
|
|
/** Queue for GameEvents */
|
|
protected ConcurrentLinkedQueue<GameEvent> mEventQueue = new ConcurrentLinkedQueue<GameEvent>();
|
|
|
|
/** Context for processKey to maintain state accross frames * */
|
|
protected Object mKeyContext = null;
|
|
|
|
// the timer display in seconds
|
|
public int mTimerLimit;
|
|
|
|
// used for internal timing logic.
|
|
public final int TIMER_LIMIT = 72;
|
|
|
|
// string value for timer display
|
|
private String mTimerValue = "1:12";
|
|
|
|
// start, play, running, lose are the states we use
|
|
public int mState;
|
|
|
|
// has laser been fired and for how long?
|
|
// user for fx logic on laser fire
|
|
boolean mLaserOn = false;
|
|
|
|
long mLaserFireTime = 0;
|
|
|
|
/** The drawable to use as the far background of the animation canvas */
|
|
private Bitmap mBackgroundImageFar;
|
|
|
|
/** The drawable to use as the close background of the animation canvas */
|
|
private Bitmap mBackgroundImageNear;
|
|
|
|
// JET info: event IDs within the JET file.
|
|
// JET info: in this game 80 is used for sending asteroid across the screen
|
|
// JET info: 82 is used as game time for 1/4 note beat.
|
|
private final byte NEW_ASTEROID_EVENT = 80;
|
|
private final byte TIMER_EVENT = 82;
|
|
|
|
// used to track beat for synch of mute/unmute actions
|
|
private int mBeatCount = 1;
|
|
|
|
// our intrepid space boy
|
|
private Bitmap[] mShipFlying = new Bitmap[4];
|
|
|
|
// the twinkly bit
|
|
private Bitmap[] mBeam = new Bitmap[4];
|
|
|
|
// the things you are trying to hit
|
|
private Bitmap[] mAsteroids = new Bitmap[12];
|
|
|
|
// hit animation
|
|
private Bitmap[] mExplosions = new Bitmap[4];
|
|
|
|
private Bitmap mTimerShell;
|
|
|
|
private Bitmap mLaserShot;
|
|
|
|
// used to save the beat event system time.
|
|
private long mLastBeatTime;
|
|
|
|
private long mPassedTime;
|
|
|
|
// how much do we move the asteroids per beat?
|
|
private int mPixelMoveX = 25;
|
|
|
|
// the asteroid send events are generated from the Jet File.
|
|
// but which land they start in is random.
|
|
private Random mRandom = new Random();
|
|
|
|
// JET info: the star of our show, a reference to the JetPlayer object.
|
|
private JetPlayer mJet = null;
|
|
|
|
private boolean mJetPlaying = false;
|
|
|
|
/** Message handler used by thread to interact with TextView */
|
|
private Handler mHandler;
|
|
|
|
/** Handle to the surface manager object we interact with */
|
|
private SurfaceHolder mSurfaceHolder;
|
|
|
|
/** Handle to the application context, used to e.g. fetch Drawables. */
|
|
private Context mContext;
|
|
|
|
/** Indicate whether the surface has been created & is ready to draw */
|
|
private boolean mRun = false;
|
|
|
|
// updates the screen clock. Also used for tempo timing.
|
|
private Timer mTimer = null;
|
|
|
|
private TimerTask mTimerTask = null;
|
|
|
|
// one second - used to update timer
|
|
private int mTaskIntervalInMillis = 1000;
|
|
|
|
/**
|
|
* Current height of the surface/canvas.
|
|
*
|
|
* @see #setSurfaceSize
|
|
*/
|
|
private int mCanvasHeight = 1;
|
|
|
|
/**
|
|
* Current width of the surface/canvas.
|
|
*
|
|
* @see #setSurfaceSize
|
|
*/
|
|
private int mCanvasWidth = 1;
|
|
|
|
// used to track the picture to draw for ship animation
|
|
private int mShipIndex = 0;
|
|
|
|
// stores all of the asteroid objects in order
|
|
private Vector<Asteroid> mDangerWillRobinson;
|
|
|
|
private Vector<Explosion> mExplosion;
|
|
|
|
// right to left scroll tracker for near and far BG
|
|
private int mBGFarMoveX = 0;
|
|
private int mBGNearMoveX = 0;
|
|
|
|
// how far up (close to top) jet boy can fly
|
|
private int mJetBoyYMin = 40;
|
|
private int mJetBoyX = 0;
|
|
private int mJetBoyY = 0;
|
|
|
|
// this is the pixel position of the laser beam guide.
|
|
private int mAsteroidMoveLimitX = 110;
|
|
|
|
// how far up asteroid can be painted
|
|
private int mAsteroidMinY = 40;
|
|
|
|
|
|
Resources mRes;
|
|
|
|
// array to store the mute masks that are applied during game play to respond to
|
|
// the player's hit streaks
|
|
private boolean muteMask[][] = new boolean[9][32];
|
|
|
|
/**
|
|
* This is the constructor for the main worker bee
|
|
*
|
|
* @param surfaceHolder
|
|
* @param context
|
|
* @param handler
|
|
*/
|
|
public JetBoyThread(SurfaceHolder surfaceHolder, Context context, Handler handler) {
|
|
|
|
mSurfaceHolder = surfaceHolder;
|
|
mHandler = handler;
|
|
mContext = context;
|
|
mRes = context.getResources();
|
|
|
|
// JET info: this are the mute arrays associated with the music beds in the
|
|
// JET info: JET file
|
|
for (int ii = 0; ii < 8; ii++) {
|
|
for (int xx = 0; xx < 32; xx++) {
|
|
muteMask[ii][xx] = true;
|
|
}
|
|
}
|
|
|
|
muteMask[0][2] = false;
|
|
muteMask[0][3] = false;
|
|
muteMask[0][4] = false;
|
|
muteMask[0][5] = false;
|
|
|
|
muteMask[1][2] = false;
|
|
muteMask[1][3] = false;
|
|
muteMask[1][4] = false;
|
|
muteMask[1][5] = false;
|
|
muteMask[1][8] = false;
|
|
muteMask[1][9] = false;
|
|
|
|
muteMask[2][2] = false;
|
|
muteMask[2][3] = false;
|
|
muteMask[2][6] = false;
|
|
muteMask[2][7] = false;
|
|
muteMask[2][8] = false;
|
|
muteMask[2][9] = false;
|
|
|
|
muteMask[3][2] = false;
|
|
muteMask[3][3] = false;
|
|
muteMask[3][6] = false;
|
|
muteMask[3][11] = false;
|
|
muteMask[3][12] = false;
|
|
|
|
muteMask[4][2] = false;
|
|
muteMask[4][3] = false;
|
|
muteMask[4][10] = false;
|
|
muteMask[4][11] = false;
|
|
muteMask[4][12] = false;
|
|
muteMask[4][13] = false;
|
|
|
|
muteMask[5][2] = false;
|
|
muteMask[5][3] = false;
|
|
muteMask[5][10] = false;
|
|
muteMask[5][12] = false;
|
|
muteMask[5][15] = false;
|
|
muteMask[5][17] = false;
|
|
|
|
muteMask[6][2] = false;
|
|
muteMask[6][3] = false;
|
|
muteMask[6][14] = false;
|
|
muteMask[6][15] = false;
|
|
muteMask[6][16] = false;
|
|
muteMask[6][17] = false;
|
|
|
|
muteMask[7][2] = false;
|
|
muteMask[7][3] = false;
|
|
muteMask[7][6] = false;
|
|
muteMask[7][14] = false;
|
|
muteMask[7][15] = false;
|
|
muteMask[7][16] = false;
|
|
muteMask[7][17] = false;
|
|
muteMask[7][18] = false;
|
|
|
|
// set all tracks to play
|
|
for (int xx = 0; xx < 32; xx++) {
|
|
muteMask[8][xx] = false;
|
|
}
|
|
|
|
// always set state to start, ensure we come in from front door if
|
|
// app gets tucked into background
|
|
mState = STATE_START;
|
|
|
|
setInitialGameState();
|
|
|
|
mTitleBG = BitmapFactory.decodeResource(mRes, R.drawable.title_hori);
|
|
|
|
// load background image as a Bitmap instead of a Drawable b/c
|
|
// we don't need to transform it and it's faster to draw this
|
|
// way...thanks lunar lander :)
|
|
|
|
// two background since we want them moving at different speeds
|
|
mBackgroundImageFar = BitmapFactory.decodeResource(mRes, R.drawable.background_a);
|
|
|
|
mLaserShot = BitmapFactory.decodeResource(mRes, R.drawable.laser);
|
|
|
|
mBackgroundImageNear = BitmapFactory.decodeResource(mRes, R.drawable.background_b);
|
|
|
|
mShipFlying[0] = BitmapFactory.decodeResource(mRes, R.drawable.ship2_1);
|
|
mShipFlying[1] = BitmapFactory.decodeResource(mRes, R.drawable.ship2_2);
|
|
mShipFlying[2] = BitmapFactory.decodeResource(mRes, R.drawable.ship2_3);
|
|
mShipFlying[3] = BitmapFactory.decodeResource(mRes, R.drawable.ship2_4);
|
|
|
|
mBeam[0] = BitmapFactory.decodeResource(mRes, R.drawable.intbeam_1);
|
|
mBeam[1] = BitmapFactory.decodeResource(mRes, R.drawable.intbeam_2);
|
|
mBeam[2] = BitmapFactory.decodeResource(mRes, R.drawable.intbeam_3);
|
|
mBeam[3] = BitmapFactory.decodeResource(mRes, R.drawable.intbeam_4);
|
|
|
|
mTimerShell = BitmapFactory.decodeResource(mRes, R.drawable.int_timer);
|
|
|
|
// I wanted them to rotate in a certain way
|
|
// so I loaded them backwards from the way created.
|
|
mAsteroids[11] = BitmapFactory.decodeResource(mRes, R.drawable.asteroid01);
|
|
mAsteroids[10] = BitmapFactory.decodeResource(mRes, R.drawable.asteroid02);
|
|
mAsteroids[9] = BitmapFactory.decodeResource(mRes, R.drawable.asteroid03);
|
|
mAsteroids[8] = BitmapFactory.decodeResource(mRes, R.drawable.asteroid04);
|
|
mAsteroids[7] = BitmapFactory.decodeResource(mRes, R.drawable.asteroid05);
|
|
mAsteroids[6] = BitmapFactory.decodeResource(mRes, R.drawable.asteroid06);
|
|
mAsteroids[5] = BitmapFactory.decodeResource(mRes, R.drawable.asteroid07);
|
|
mAsteroids[4] = BitmapFactory.decodeResource(mRes, R.drawable.asteroid08);
|
|
mAsteroids[3] = BitmapFactory.decodeResource(mRes, R.drawable.asteroid09);
|
|
mAsteroids[2] = BitmapFactory.decodeResource(mRes, R.drawable.asteroid10);
|
|
mAsteroids[1] = BitmapFactory.decodeResource(mRes, R.drawable.asteroid11);
|
|
mAsteroids[0] = BitmapFactory.decodeResource(mRes, R.drawable.asteroid12);
|
|
|
|
mExplosions[0] = BitmapFactory.decodeResource(mRes, R.drawable.asteroid_explode1);
|
|
mExplosions[1] = BitmapFactory.decodeResource(mRes, R.drawable.asteroid_explode2);
|
|
mExplosions[2] = BitmapFactory.decodeResource(mRes, R.drawable.asteroid_explode3);
|
|
mExplosions[3] = BitmapFactory.decodeResource(mRes, R.drawable.asteroid_explode4);
|
|
|
|
}
|
|
|
|
/**
|
|
* Does the grunt work of setting up initial jet requirements
|
|
*/
|
|
private void initializeJetPlayer() {
|
|
|
|
// JET info: let's create our JetPlayer instance using the factory.
|
|
// JET info: if we already had one, the same singleton is returned.
|
|
mJet = JetPlayer.getJetPlayer();
|
|
|
|
mJetPlaying = false;
|
|
|
|
// JET info: make sure we flush the queue,
|
|
// JET info: otherwise left over events from previous gameplay can hang around.
|
|
// JET info: ok, here we don't really need that but if you ever reuse a JetPlayer
|
|
// JET info: instance, clear the queue before reusing it, this will also clear any
|
|
// JET info: trigger clips that have been triggered but not played yet.
|
|
mJet.clearQueue();
|
|
|
|
// JET info: we are going to receive in this example all the JET callbacks
|
|
// JET info: inthis animation thread object.
|
|
mJet.setEventListener(this);
|
|
|
|
Log.d(TAG, "opening jet file");
|
|
|
|
// JET info: load the actual JET content the game will be playing,
|
|
// JET info: it's stored as a raw resource in our APK, and is labeled "level1"
|
|
mJet.loadJetFile(mContext.getResources().openRawResourceFd(R.raw.level1));
|
|
// JET info: if our JET file was stored on the sdcard for instance, we would have used
|
|
// JET info: mJet.loadJetFile("/sdcard/level1.jet");
|
|
|
|
Log.d(TAG, "opening jet file DONE");
|
|
|
|
mCurrentBed = 0;
|
|
byte sSegmentID = 0;
|
|
|
|
Log.d(TAG, " start queuing jet file");
|
|
|
|
// JET info: now we're all set to prepare queuing the JET audio segments for the game.
|
|
// JET info: in this example, the game uses segment 0 for the duration of the game play,
|
|
// JET info: and plays segment 1 several times as the "outro" music, so we're going to
|
|
// JET info: queue everything upfront, but with more complex JET compositions, we could
|
|
// JET info: also queue the segments during the game play.
|
|
|
|
// JET info: this is the main game play music
|
|
// JET info: it is located at segment 0
|
|
// JET info: it uses the first DLS lib in the .jet resource, which is at index 0
|
|
// JET info: index -1 means no DLS
|
|
mJet.queueJetSegment(0, 0, 0, 0, 0, sSegmentID);
|
|
|
|
// JET info: end game music, loop 4 times normal pitch
|
|
mJet.queueJetSegment(1, 0, 4, 0, 0, sSegmentID);
|
|
|
|
// JET info: end game music loop 4 times up an octave
|
|
mJet.queueJetSegment(1, 0, 4, 1, 0, sSegmentID);
|
|
|
|
// JET info: set the mute mask as designed for the beginning of the game, when the
|
|
// JET info: the player hasn't scored yet.
|
|
mJet.setMuteArray(muteMask[0], true);
|
|
|
|
Log.d(TAG, " start queuing jet file DONE");
|
|
|
|
}
|
|
|
|
|
|
private void doDraw(Canvas canvas) {
|
|
|
|
if (mState == STATE_RUNNING) {
|
|
doDrawRunning(canvas);
|
|
} else if (mState == STATE_START) {
|
|
doDrawReady(canvas);
|
|
} else if (mState == STATE_PLAY || mState == STATE_LOSE) {
|
|
if (mTitleBG2 == null) {
|
|
mTitleBG2 = BitmapFactory.decodeResource(mRes, R.drawable.title_bg_hori);
|
|
}
|
|
doDrawPlay(canvas);
|
|
}// end state play block
|
|
}
|
|
|
|
|
|
/**
|
|
* Draws current state of the game Canvas.
|
|
*/
|
|
private void doDrawRunning(Canvas canvas) {
|
|
|
|
// decrement the far background
|
|
mBGFarMoveX = mBGFarMoveX - 1;
|
|
|
|
// decrement the near background
|
|
mBGNearMoveX = mBGNearMoveX - 4;
|
|
|
|
// calculate the wrap factor for matching image draw
|
|
int newFarX = mBackgroundImageFar.getWidth() - (-mBGFarMoveX);
|
|
|
|
// if we have scrolled all the way, reset to start
|
|
if (newFarX <= 0) {
|
|
mBGFarMoveX = 0;
|
|
// only need one draw
|
|
canvas.drawBitmap(mBackgroundImageFar, mBGFarMoveX, 0, null);
|
|
|
|
} else {
|
|
// need to draw original and wrap
|
|
canvas.drawBitmap(mBackgroundImageFar, mBGFarMoveX, 0, null);
|
|
canvas.drawBitmap(mBackgroundImageFar, newFarX, 0, null);
|
|
}
|
|
|
|
// same story different image...
|
|
// TODO possible method call
|
|
int newNearX = mBackgroundImageNear.getWidth() - (-mBGNearMoveX);
|
|
|
|
if (newNearX <= 0) {
|
|
mBGNearMoveX = 0;
|
|
canvas.drawBitmap(mBackgroundImageNear, mBGNearMoveX, 0, null);
|
|
|
|
} else {
|
|
canvas.drawBitmap(mBackgroundImageNear, mBGNearMoveX, 0, null);
|
|
canvas.drawBitmap(mBackgroundImageNear, newNearX, 0, null);
|
|
}
|
|
|
|
doAsteroidAnimation(canvas);
|
|
|
|
canvas.drawBitmap(mBeam[mShipIndex], 51 + 20, 0, null);
|
|
|
|
mShipIndex++;
|
|
|
|
if (mShipIndex == 4)
|
|
mShipIndex = 0;
|
|
|
|
// draw the space ship in the same lane as the next asteroid
|
|
canvas.drawBitmap(mShipFlying[mShipIndex], mJetBoyX, mJetBoyY, null);
|
|
|
|
if (mLaserOn) {
|
|
canvas.drawBitmap(mLaserShot, mJetBoyX + mShipFlying[0].getWidth(), mJetBoyY
|
|
+ (mShipFlying[0].getHeight() / 2), null);
|
|
}
|
|
|
|
// tick tock
|
|
canvas.drawBitmap(mTimerShell, mCanvasWidth - mTimerShell.getWidth(), 0, null);
|
|
|
|
}
|
|
|
|
private void setInitialGameState() {
|
|
mTimerLimit = TIMER_LIMIT;
|
|
|
|
mJetBoyY = mJetBoyYMin;
|
|
|
|
// set up jet stuff
|
|
initializeJetPlayer();
|
|
|
|
mTimer = new Timer();
|
|
|
|
mDangerWillRobinson = new Vector<Asteroid>();
|
|
|
|
mExplosion = new Vector<Explosion>();
|
|
|
|
mInitialized = true;
|
|
|
|
mHitStreak = 0;
|
|
mHitTotal = 0;
|
|
}
|
|
|
|
private void doAsteroidAnimation(Canvas canvas) {
|
|
if ((mDangerWillRobinson == null | mDangerWillRobinson.size() == 0)
|
|
&& (mExplosion != null && mExplosion.size() == 0))
|
|
return;
|
|
|
|
// Compute what percentage through a beat we are and adjust
|
|
// animation and position based on that. This assumes 140bpm(428ms/beat).
|
|
// This is just inter-beat interpolation, no game state is updated
|
|
long frameDelta = System.currentTimeMillis() - mLastBeatTime;
|
|
|
|
int animOffset = (int)(ANIMATION_FRAMES_PER_BEAT * frameDelta / 428);
|
|
|
|
for (int i = (mDangerWillRobinson.size() - 1); i >= 0; i--) {
|
|
Asteroid asteroid = mDangerWillRobinson.elementAt(i);
|
|
|
|
if (!asteroid.mMissed)
|
|
mJetBoyY = asteroid.mDrawY;
|
|
|
|
// Log.d(TAG, " drawing asteroid " + ii + " at " +
|
|
// asteroid.mDrawX );
|
|
|
|
canvas.drawBitmap(
|
|
mAsteroids[(asteroid.mAniIndex + animOffset) % mAsteroids.length],
|
|
asteroid.mDrawX, asteroid.mDrawY, null);
|
|
}
|
|
|
|
for (int i = (mExplosion.size() - 1); i >= 0; i--) {
|
|
Explosion ex = mExplosion.elementAt(i);
|
|
|
|
canvas.drawBitmap(mExplosions[(ex.mAniIndex + animOffset) % mExplosions.length],
|
|
ex.mDrawX, ex.mDrawY, null);
|
|
}
|
|
}
|
|
|
|
private void doDrawReady(Canvas canvas) {
|
|
canvas.drawBitmap(mTitleBG, 0, 0, null);
|
|
}
|
|
|
|
private void doDrawPlay(Canvas canvas) {
|
|
canvas.drawBitmap(mTitleBG2, 0, 0, null);
|
|
}
|
|
|
|
|
|
/**
|
|
* the heart of the worker bee
|
|
*/
|
|
public void run() {
|
|
// while running do stuff in this loop...bzzz!
|
|
while (mRun) {
|
|
Canvas c = null;
|
|
|
|
if (mState == STATE_RUNNING) {
|
|
// Process any input and apply it to the game state
|
|
updateGameState();
|
|
|
|
if (!mJetPlaying) {
|
|
|
|
mInitialized = false;
|
|
Log.d(TAG, "------> STARTING JET PLAY");
|
|
mJet.play();
|
|
|
|
mJetPlaying = true;
|
|
|
|
}
|
|
|
|
mPassedTime = System.currentTimeMillis();
|
|
|
|
// kick off the timer task for counter update if not already
|
|
// initialized
|
|
if (mTimerTask == null) {
|
|
mTimerTask = new TimerTask() {
|
|
public void run() {
|
|
doCountDown();
|
|
}
|
|
};
|
|
|
|
mTimer.schedule(mTimerTask, mTaskIntervalInMillis);
|
|
|
|
}// end of TimerTask init block
|
|
|
|
}// end of STATE_RUNNING block
|
|
else if (mState == STATE_PLAY && !mInitialized)
|
|
{
|
|
setInitialGameState();
|
|
} else if (mState == STATE_LOSE) {
|
|
mInitialized = false;
|
|
}
|
|
|
|
try {
|
|
c = mSurfaceHolder.lockCanvas(null);
|
|
// synchronized (mSurfaceHolder) {
|
|
doDraw(c);
|
|
// }
|
|
} finally {
|
|
// do this in a finally so that if an exception is thrown
|
|
// during the above, we don't leave the Surface in an
|
|
// inconsistent state
|
|
if (c != null) {
|
|
mSurfaceHolder.unlockCanvasAndPost(c);
|
|
}
|
|
}// end finally block
|
|
}// end while mrun block
|
|
}
|
|
|
|
|
|
/**
|
|
* This method handles updating the model of the game state. No
|
|
* rendering is done here only processing of inputs and update of state.
|
|
* This includes positons of all game objects (asteroids, player,
|
|
* explosions), their state (animation frame, hit), creation of new
|
|
* objects, etc.
|
|
*/
|
|
protected void updateGameState() {
|
|
// Process any game events and apply them
|
|
while (true) {
|
|
GameEvent event = mEventQueue.poll();
|
|
if (event == null)
|
|
break;
|
|
|
|
// Log.d(TAG,"*** EVENT = " + event);
|
|
|
|
// Process keys tracking the input context to pass in to later
|
|
// calls
|
|
if (event instanceof KeyGameEvent) {
|
|
// Process the key for affects other then asteroid hits
|
|
mKeyContext = processKeyEvent((KeyGameEvent)event, mKeyContext);
|
|
|
|
// Update laser state. Having this here allows the laser to
|
|
// be triggered right when the key is
|
|
// pressed. If we comment this out the laser will only be
|
|
// turned on when updateLaser is called
|
|
// when processing a timer event below.
|
|
updateLaser(mKeyContext);
|
|
|
|
}
|
|
// JET events trigger a state update
|
|
else if (event instanceof JetGameEvent) {
|
|
JetGameEvent jetEvent = (JetGameEvent)event;
|
|
|
|
// Only update state on a timer event
|
|
if (jetEvent.value == TIMER_EVENT) {
|
|
// Note the time of the last beat
|
|
mLastBeatTime = System.currentTimeMillis();
|
|
|
|
// Update laser state, turning it on if a key has been
|
|
// pressed or off if it has been
|
|
// on for too long.
|
|
updateLaser(mKeyContext);
|
|
|
|
// Update explosions before we update asteroids because
|
|
// updateAsteroids may add
|
|
// new explosions that we do not want updated until next
|
|
// frame
|
|
updateExplosions(mKeyContext);
|
|
|
|
// Update asteroid positions, hit status and animations
|
|
updateAsteroids(mKeyContext);
|
|
}
|
|
|
|
processJetEvent(jetEvent.player, jetEvent.segment, jetEvent.track,
|
|
jetEvent.channel, jetEvent.controller, jetEvent.value);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* This method handles the state updates that can be caused by key press
|
|
* events. Key events may mean different things depending on what has
|
|
* come before, to support this concept this method takes an opaque
|
|
* context object as a parameter and returns an updated version. This
|
|
* context should be set to null for the first event then should be set
|
|
* to the last value returned for subsequent events.
|
|
*/
|
|
protected Object processKeyEvent(KeyGameEvent event, Object context) {
|
|
// Log.d(TAG, "key code is " + event.keyCode + " " + (event.up ?
|
|
// "up":"down"));
|
|
|
|
// If it is a key up on the fire key make sure we mute the
|
|
// associated sound
|
|
if (event.up) {
|
|
if (event.keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
|
|
return null;
|
|
}
|
|
}
|
|
// If it is a key down on the fire key start playing the sound and
|
|
// update the context
|
|
// to indicate that a key has been pressed and to ignore further
|
|
// presses
|
|
else {
|
|
if (event.keyCode == KeyEvent.KEYCODE_DPAD_CENTER && (context == null)) {
|
|
return event;
|
|
}
|
|
}
|
|
|
|
// Return the context unchanged
|
|
return context;
|
|
}
|
|
|
|
|
|
/**
|
|
* This method updates the laser status based on user input and shot
|
|
* duration
|
|
*/
|
|
protected void updateLaser(Object inputContext) {
|
|
// Lookup the time of the fire event if there is one
|
|
long keyTime = inputContext == null ? 0 : ((GameEvent)inputContext).eventTime;
|
|
|
|
// Log.d(TAG,"keyTime delta = " +
|
|
// (System.currentTimeMillis()-keyTime) + ": obj = " +
|
|
// inputContext);
|
|
|
|
// If the laser has been on too long shut it down
|
|
if (mLaserOn && System.currentTimeMillis() - mLaserFireTime > 400) {
|
|
mLaserOn = false;
|
|
}
|
|
|
|
// trying to tune the laser hit timing
|
|
else if (System.currentTimeMillis() - mLaserFireTime > 300) {
|
|
// JET info: the laser sound is on track 23, we mute it (true) right away (false)
|
|
mJet.setMuteFlag(23, true, false);
|
|
|
|
}
|
|
|
|
// Now check to see if we should turn the laser on. We do this after
|
|
// the above shutdown
|
|
// logic so it can be turned back on in the same frame it was turned
|
|
// off in. If we want
|
|
// to add a cooldown period this may change.
|
|
if (!mLaserOn && System.currentTimeMillis() - keyTime <= 400) {
|
|
|
|
mLaserOn = true;
|
|
mLaserFireTime = keyTime;
|
|
|
|
// JET info: unmute the laser track (false) right away (false)
|
|
mJet.setMuteFlag(23, false, false);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update asteroid state including position and laser hit status.
|
|
*/
|
|
protected void updateAsteroids(Object inputContext) {
|
|
if (mDangerWillRobinson == null | mDangerWillRobinson.size() == 0)
|
|
return;
|
|
|
|
for (int i = (mDangerWillRobinson.size() - 1); i >= 0; i--) {
|
|
Asteroid asteroid = mDangerWillRobinson.elementAt(i);
|
|
|
|
// If the asteroid is within laser range but not already missed
|
|
// check if the key was pressed close enough to the beat to make a hit
|
|
if (asteroid.mDrawX <= mAsteroidMoveLimitX + 20 && !asteroid.mMissed)
|
|
{
|
|
// If the laser was fired on the beat destroy the asteroid
|
|
if (mLaserOn) {
|
|
// Track hit streak for adjusting music
|
|
mHitStreak++;
|
|
mHitTotal++;
|
|
|
|
// replace the asteroid with an explosion
|
|
Explosion ex = new Explosion();
|
|
ex.mAniIndex = 0;
|
|
ex.mDrawX = asteroid.mDrawX;
|
|
ex.mDrawY = asteroid.mDrawY;
|
|
mExplosion.add(ex);
|
|
|
|
mJet.setMuteFlag(24, false, false);
|
|
|
|
mDangerWillRobinson.removeElementAt(i);
|
|
|
|
// This asteroid has been removed process the next one
|
|
continue;
|
|
} else {
|
|
// Sorry, timing was not good enough, mark the asteroid
|
|
// as missed so on next frame it cannot be hit even if it is still
|
|
// within range
|
|
asteroid.mMissed = true;
|
|
|
|
mHitStreak = mHitStreak - 1;
|
|
|
|
if (mHitStreak < 0)
|
|
mHitStreak = 0;
|
|
|
|
}
|
|
}
|
|
|
|
// Update the asteroids position, even missed ones keep moving
|
|
asteroid.mDrawX -= mPixelMoveX;
|
|
|
|
// Update asteroid animation frame
|
|
asteroid.mAniIndex = (asteroid.mAniIndex + ANIMATION_FRAMES_PER_BEAT)
|
|
% mAsteroids.length;
|
|
|
|
// if we have scrolled off the screen
|
|
if (asteroid.mDrawX < 0) {
|
|
mDangerWillRobinson.removeElementAt(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This method updates explosion animation and removes them once they
|
|
* have completed.
|
|
*/
|
|
protected void updateExplosions(Object inputContext) {
|
|
if (mExplosion == null | mExplosion.size() == 0)
|
|
return;
|
|
|
|
for (int i = mExplosion.size() - 1; i >= 0; i--) {
|
|
Explosion ex = mExplosion.elementAt(i);
|
|
|
|
ex.mAniIndex += ANIMATION_FRAMES_PER_BEAT;
|
|
|
|
// When the animation completes remove the explosion
|
|
if (ex.mAniIndex > 3) {
|
|
mJet.setMuteFlag(24, true, false);
|
|
mJet.setMuteFlag(23, true, false);
|
|
|
|
mExplosion.removeElementAt(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This method handles the state updates that can be caused by JET
|
|
* events.
|
|
*/
|
|
protected void processJetEvent(JetPlayer player, short segment, byte track, byte channel,
|
|
byte controller, byte value) {
|
|
|
|
//Log.d(TAG, "onJetEvent(): seg=" + segment + " track=" + track + " chan=" + channel
|
|
// + " cntrlr=" + controller + " val=" + value);
|
|
|
|
|
|
// Check for an event that triggers a new asteroid
|
|
if (value == NEW_ASTEROID_EVENT) {
|
|
doAsteroidCreation();
|
|
}
|
|
|
|
mBeatCount++;
|
|
|
|
if (mBeatCount > 4) {
|
|
mBeatCount = 1;
|
|
|
|
}
|
|
|
|
// Scale the music based on progress
|
|
|
|
// it was a game requirement to change the mute array on 1st beat of
|
|
// the next measure when needed
|
|
// and so we track beat count, after that we track hitStreak to
|
|
// determine the music "intensity"
|
|
// if the intensity has go gone up, call a corresponding trigger clip, otherwise just
|
|
// execute the rest of the music bed change logic.
|
|
if (mBeatCount == 1) {
|
|
|
|
// do it back wards so you fall into the correct one
|
|
if (mHitStreak > 28) {
|
|
|
|
// did the bed change?
|
|
if (mCurrentBed != 7) {
|
|
// did it go up?
|
|
if (mCurrentBed < 7) {
|
|
mJet.triggerClip(7);
|
|
}
|
|
|
|
mCurrentBed = 7;
|
|
// JET info: change the mute mask to update the way the music plays based
|
|
// JET info: on the player's skills.
|
|
mJet.setMuteArray(muteMask[7], false);
|
|
|
|
}
|
|
} else if (mHitStreak > 24) {
|
|
if (mCurrentBed != 6) {
|
|
if (mCurrentBed < 6) {
|
|
// JET info: quite a few asteroids hit, trigger the clip with the guy's
|
|
// JET info: voice that encourages the player.
|
|
mJet.triggerClip(6);
|
|
}
|
|
|
|
mCurrentBed = 6;
|
|
mJet.setMuteArray(muteMask[6], false);
|
|
}
|
|
} else if (mHitStreak > 20) {
|
|
if (mCurrentBed != 5) {
|
|
if (mCurrentBed < 5) {
|
|
mJet.triggerClip(5);
|
|
}
|
|
|
|
mCurrentBed = 5;
|
|
mJet.setMuteArray(muteMask[5], false);
|
|
}
|
|
} else if (mHitStreak > 16) {
|
|
if (mCurrentBed != 4) {
|
|
|
|
if (mCurrentBed < 4) {
|
|
mJet.triggerClip(4);
|
|
}
|
|
mCurrentBed = 4;
|
|
mJet.setMuteArray(muteMask[4], false);
|
|
}
|
|
} else if (mHitStreak > 12) {
|
|
if (mCurrentBed != 3) {
|
|
if (mCurrentBed < 3) {
|
|
mJet.triggerClip(3);
|
|
}
|
|
mCurrentBed = 3;
|
|
mJet.setMuteArray(muteMask[3], false);
|
|
}
|
|
} else if (mHitStreak > 8) {
|
|
if (mCurrentBed != 2) {
|
|
if (mCurrentBed < 2) {
|
|
mJet.triggerClip(2);
|
|
}
|
|
|
|
mCurrentBed = 2;
|
|
mJet.setMuteArray(muteMask[2], false);
|
|
}
|
|
} else if (mHitStreak > 4) {
|
|
if (mCurrentBed != 1) {
|
|
|
|
if (mCurrentBed < 1) {
|
|
mJet.triggerClip(1);
|
|
}
|
|
|
|
mJet.setMuteArray(muteMask[1], false);
|
|
|
|
mCurrentBed = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
private void doAsteroidCreation() {
|
|
// Log.d(TAG, "asteroid created");
|
|
|
|
Asteroid _as = new Asteroid();
|
|
|
|
int drawIndex = mRandom.nextInt(4);
|
|
|
|
// TODO Remove hard coded value
|
|
_as.mDrawY = mAsteroidMinY + (drawIndex * 63);
|
|
|
|
_as.mDrawX = (mCanvasWidth - mAsteroids[0].getWidth());
|
|
|
|
_as.mStartTime = System.currentTimeMillis();
|
|
|
|
mDangerWillRobinson.add(_as);
|
|
}
|
|
|
|
|
|
/**
|
|
* Used to signal the thread whether it should be running or not.
|
|
* Passing true allows the thread to run; passing false will shut it
|
|
* down if it's already running. Calling start() after this was most
|
|
* recently called with false will result in an immediate shutdown.
|
|
*
|
|
* @param b true to run, false to shut down
|
|
*/
|
|
public void setRunning(boolean b) {
|
|
mRun = b;
|
|
|
|
if (mRun == false) {
|
|
if (mTimerTask != null)
|
|
mTimerTask.cancel();
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* returns the current int value of game state as defined by state
|
|
* tracking constants
|
|
*
|
|
* @return
|
|
*/
|
|
public int getGameState() {
|
|
synchronized (mSurfaceHolder) {
|
|
return mState;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Sets the game mode. That is, whether we are running, paused, in the
|
|
* failure state, in the victory state, etc.
|
|
*
|
|
* @see #setState(int, CharSequence)
|
|
* @param mode one of the STATE_* constants
|
|
*/
|
|
public void setGameState(int mode) {
|
|
synchronized (mSurfaceHolder) {
|
|
setGameState(mode, null);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Sets state based on input, optionally also passing in a text message.
|
|
*
|
|
* @param state
|
|
* @param message
|
|
*/
|
|
public void setGameState(int state, CharSequence message) {
|
|
|
|
synchronized (mSurfaceHolder) {
|
|
|
|
// change state if needed
|
|
if (mState != state) {
|
|
mState = state;
|
|
}
|
|
|
|
if (mState == STATE_PLAY) {
|
|
Resources res = mContext.getResources();
|
|
mBackgroundImageFar = BitmapFactory
|
|
.decodeResource(res, R.drawable.background_a);
|
|
|
|
// don't forget to resize the background image
|
|
mBackgroundImageFar = Bitmap.createScaledBitmap(mBackgroundImageFar,
|
|
mCanvasWidth * 2, mCanvasHeight, true);
|
|
|
|
mBackgroundImageNear = BitmapFactory.decodeResource(res,
|
|
R.drawable.background_b);
|
|
|
|
// don't forget to resize the background image
|
|
mBackgroundImageNear = Bitmap.createScaledBitmap(mBackgroundImageNear,
|
|
mCanvasWidth * 2, mCanvasHeight, true);
|
|
|
|
} else if (mState == STATE_RUNNING) {
|
|
// When we enter the running state we should clear any old
|
|
// events in the queue
|
|
mEventQueue.clear();
|
|
|
|
// And reset the key state so we don't think a button is pressed when it isn't
|
|
mKeyContext = null;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Add key press input to the GameEvent queue
|
|
*/
|
|
public boolean doKeyDown(int keyCode, KeyEvent msg) {
|
|
mEventQueue.add(new KeyGameEvent(keyCode, false, msg));
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/**
|
|
* Add key press input to the GameEvent queue
|
|
*/
|
|
public boolean doKeyUp(int keyCode, KeyEvent msg) {
|
|
mEventQueue.add(new KeyGameEvent(keyCode, true, msg));
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/* Callback invoked when the surface dimensions change. */
|
|
public void setSurfaceSize(int width, int height) {
|
|
// synchronized to make sure these all change atomically
|
|
synchronized (mSurfaceHolder) {
|
|
mCanvasWidth = width;
|
|
mCanvasHeight = height;
|
|
|
|
// don't forget to resize the background image
|
|
mBackgroundImageFar = Bitmap.createScaledBitmap(mBackgroundImageFar, width * 2,
|
|
height, true);
|
|
|
|
// don't forget to resize the background image
|
|
mBackgroundImageNear = Bitmap.createScaledBitmap(mBackgroundImageNear, width * 2,
|
|
height, true);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Pauses the physics update & animation.
|
|
*/
|
|
public void pause() {
|
|
synchronized (mSurfaceHolder) {
|
|
if (mState == STATE_RUNNING)
|
|
setGameState(STATE_PAUSE);
|
|
if (mTimerTask != null) {
|
|
mTimerTask.cancel();
|
|
}
|
|
|
|
if (mJet != null) {
|
|
mJet.pause();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Does the work of updating timer
|
|
*
|
|
*/
|
|
private void doCountDown() {
|
|
//Log.d(TAG,"Time left is " + mTimerLimit);
|
|
|
|
mTimerLimit = mTimerLimit - 1;
|
|
try {
|
|
//subtract one minute and see what the result is.
|
|
int moreThanMinute = mTimerLimit - 60;
|
|
|
|
if (moreThanMinute >= 0) {
|
|
|
|
if (moreThanMinute > 9) {
|
|
mTimerValue = "1:" + moreThanMinute;
|
|
|
|
}
|
|
//need an extra '0' for formatting
|
|
else {
|
|
mTimerValue = "1:0" + moreThanMinute;
|
|
}
|
|
} else {
|
|
if (mTimerLimit > 9) {
|
|
mTimerValue = "0:" + mTimerLimit;
|
|
} else {
|
|
mTimerValue = "0:0" + mTimerLimit;
|
|
}
|
|
}
|
|
} catch (Exception e1) {
|
|
Log.e(TAG, "doCountDown threw " + e1.toString());
|
|
}
|
|
|
|
Message msg = mHandler.obtainMessage();
|
|
|
|
Bundle b = new Bundle();
|
|
b.putString("text", mTimerValue);
|
|
|
|
//time's up
|
|
if (mTimerLimit == 0) {
|
|
b.putString("STATE_LOSE", "" + STATE_LOSE);
|
|
mTimerTask = null;
|
|
|
|
mState = STATE_LOSE;
|
|
|
|
} else {
|
|
|
|
mTimerTask = new TimerTask() {
|
|
public void run() {
|
|
doCountDown();
|
|
}
|
|
};
|
|
|
|
mTimer.schedule(mTimerTask, mTaskIntervalInMillis);
|
|
}
|
|
|
|
//this is how we send data back up to the main JetBoyView thread.
|
|
//if you look in constructor of JetBoyView you will see code for
|
|
//Handling of messages. This is borrowed directly from lunar lander.
|
|
//Thanks again!
|
|
msg.setData(b);
|
|
mHandler.sendMessage(msg);
|
|
|
|
}
|
|
|
|
|
|
// JET info: JET event listener interface implementation:
|
|
/**
|
|
* required OnJetEventListener method. Notifications for queue updates
|
|
*
|
|
* @param player
|
|
* @param nbSegments
|
|
*/
|
|
public void onJetNumQueuedSegmentUpdate(JetPlayer player, int nbSegments) {
|
|
//Log.i(TAG, "onJetNumQueuedUpdate(): nbSegs =" + nbSegments);
|
|
|
|
}
|
|
|
|
|
|
// JET info: JET event listener interface implementation:
|
|
/**
|
|
* The method which receives notification from event listener.
|
|
* This is where we queue up events 80 and 82.
|
|
*
|
|
* Most of this data passed is unneeded for JetBoy logic but shown
|
|
* for code sample completeness.
|
|
*
|
|
* @param player
|
|
* @param segment
|
|
* @param track
|
|
* @param channel
|
|
* @param controller
|
|
* @param value
|
|
*/
|
|
public void onJetEvent(JetPlayer player, short segment, byte track, byte channel,
|
|
byte controller, byte value) {
|
|
|
|
//Log.d(TAG, "jet got event " + value);
|
|
|
|
//events fire outside the animation thread. This can cause timing issues.
|
|
//put in queue for processing by animation thread.
|
|
mEventQueue.add(new JetGameEvent(player, segment, track, channel, controller, value));
|
|
}
|
|
|
|
|
|
// JET info: JET event listener interface implementation:
|
|
public void onJetPauseUpdate(JetPlayer player, int paused) {
|
|
//Log.i(TAG, "onJetPauseUpdate(): paused =" + paused);
|
|
|
|
}
|
|
|
|
// JET info: JET event listener interface implementation:
|
|
public void onJetUserIdUpdate(JetPlayer player, int userId, int repeatCount) {
|
|
//Log.i(TAG, "onJetUserIdUpdate(): userId =" + userId + " repeatCount=" + repeatCount);
|
|
|
|
}
|
|
|
|
}//end thread class
|
|
|
|
public static final String TAG = "JetBoy";
|
|
|
|
/** The thread that actually draws the animation */
|
|
private JetBoyThread thread;
|
|
|
|
private TextView mTimerView;
|
|
|
|
private Button mButtonRetry;
|
|
|
|
// private Button mButtonRestart;
|
|
private TextView mTextView;
|
|
|
|
/**
|
|
* The constructor called from the main JetBoy activity
|
|
*
|
|
* @param context
|
|
* @param attrs
|
|
*/
|
|
public JetBoyView(Context context, AttributeSet attrs) {
|
|
super(context, attrs);
|
|
|
|
// register our interest in hearing about changes to our surface
|
|
SurfaceHolder holder = getHolder();
|
|
holder.addCallback(this);
|
|
|
|
// create thread only; it's started in surfaceCreated()
|
|
// except if used in the layout editor.
|
|
if (isInEditMode() == false) {
|
|
thread = new JetBoyThread(holder, context, new Handler() {
|
|
|
|
public void handleMessage(Message m) {
|
|
|
|
mTimerView.setText(m.getData().getString("text"));
|
|
|
|
if (m.getData().getString("STATE_LOSE") != null) {
|
|
//mButtonRestart.setVisibility(View.VISIBLE);
|
|
mButtonRetry.setVisibility(View.VISIBLE);
|
|
|
|
mTimerView.setVisibility(View.INVISIBLE);
|
|
|
|
mTextView.setVisibility(View.VISIBLE);
|
|
|
|
Log.d(TAG, "the total was " + mHitTotal);
|
|
|
|
if (mHitTotal >= mSuccessThreshold) {
|
|
mTextView.setText(R.string.winText);
|
|
} else {
|
|
mTextView.setText("Sorry, You Lose! You got " + mHitTotal
|
|
+ ". You need 50 to win.");
|
|
}
|
|
|
|
mTimerView.setText("1:12");
|
|
mTextView.setHeight(20);
|
|
|
|
}
|
|
}//end handle msg
|
|
});
|
|
}
|
|
|
|
setFocusable(true); // make sure we get key events
|
|
|
|
Log.d(TAG, "@@@ done creating view!");
|
|
}
|
|
|
|
|
|
/**
|
|
* Pass in a reference to the timer view widget so we can update it from here.
|
|
*
|
|
* @param tv
|
|
*/
|
|
public void setTimerView(TextView tv) {
|
|
mTimerView = tv;
|
|
}
|
|
|
|
|
|
/**
|
|
* Standard window-focus override. Notice focus lost so we can pause on
|
|
* focus lost. e.g. user switches to take a call.
|
|
*/
|
|
@Override
|
|
public void onWindowFocusChanged(boolean hasWindowFocus) {
|
|
if (!hasWindowFocus) {
|
|
if (thread != null)
|
|
thread.pause();
|
|
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Fetches the animation thread corresponding to this LunarView.
|
|
*
|
|
* @return the animation thread
|
|
*/
|
|
public JetBoyThread getThread() {
|
|
return thread;
|
|
}
|
|
|
|
|
|
/* Callback invoked when the surface dimensions change. */
|
|
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
|
|
thread.setSurfaceSize(width, height);
|
|
}
|
|
|
|
|
|
public void surfaceCreated(SurfaceHolder arg0) {
|
|
// start the thread here so that we don't busy-wait in run()
|
|
// waiting for the surface to be created
|
|
thread.setRunning(true);
|
|
thread.start();
|
|
}
|
|
|
|
|
|
public void surfaceDestroyed(SurfaceHolder arg0) {
|
|
boolean retry = true;
|
|
thread.setRunning(false);
|
|
while (retry) {
|
|
try {
|
|
thread.join();
|
|
retry = false;
|
|
|
|
} catch (InterruptedException e) {
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* A reference to the button to start game over.
|
|
*
|
|
* @param _buttonRetry
|
|
*
|
|
*/
|
|
public void SetButtonView(Button _buttonRetry) {
|
|
mButtonRetry = _buttonRetry;
|
|
// mButtonRestart = _buttonRestart;
|
|
}
|
|
|
|
|
|
//we reuse the help screen from the end game screen.
|
|
public void SetTextView(TextView textView) {
|
|
mTextView = textView;
|
|
|
|
}
|
|
}
|