
// Copyright 2001-2003, Danger, Inc.  All Rights Reserved.
// This file is subject to the Danger, Inc. Sample Code License,
// which is provided in the file SAMPLE_CODE_LICENSE.
// Copies are also available from http://developer.danger.com/

/* football, based on etcha! */

/*
	The Mattel Football game has a few idiosyncracies, and hiptop Football tries to
	duplicate them.  (Like, the 9-yard field, and the Home/Visitor ball swap at the
	end of the 2nd and 4th.)

	In real football:

	Field is 100 yards long, with the 50-yard-line in the middle.  The lines are
	labelled every 10 yards, descending in both directions from the 50 yard line.
	There are 2 40 yard lines and no 60 yard lines.

	In Mattel football, the rules are super-simple:

	Offense player starts on the 20-yard-line.

	You get 4 downs to go at least 10 yards.  If you don't make 10 yards in 4 downs,
	the Home/Visitor ball state flips (2 whistles!).

	The goal is to go all the way down the field for a touchdown.

	If the Offense hasn't covered 10 yards by the end of the 3rd down, the Offense
	may KICK the ball in the 4th down.  KICK is only valid if:

		- 4th down (so, 10 yards not yet covered)
		- Offense player *has not moved*.  Must KICK *instead* of moving.

	When you KICK, the ball will go between 1 and 65 yards.  The computer decides
	whether to PUNT or make a FIELD GOAL.  The closer to the goal the Offense player
	is, the better the chance for a field goal.

	7 points for a touchdown.
	3 points for a field goal.

    More details:
    -------------

    Each 15-minute Quarter really lasts 2.5 minutes, so a full game lasts 10 minutes.

    1st Quarter:            Initially, HOME has ball, but can lose it
    2nd Quarter:            Ball possession & field position SAME AS AT END OF 1st Quarter
    3rd Quarter:            VISITOR has ball, 1st & 10 on its own 20 yard line
    4th Quarter:            Ball possession & field position SAME AS AT END OF 3rd Quarter






*/

package com.danger.football;

import danger.app.*;
import danger.ui.*;
import danger.audio.*;
import java.util.Random;

/**
 * Implements the main application class.
 *
 */
public class football extends Application
{
	//*	--------------------	football

    /**
     * Creates the main application class.
     *
     */
	public
	football()
	{
		mMe = this;
		//*	Create our app's main window
		mWindow = new FootballWindow();
		mWindow.setTitle( kGameName );
        mWindow.setIcon( mMe.getBitmap( AppResources.ID_SMALL_ICON ) );
	}

	//*	--------------------	ReceiveEvent

    /**
     * Receives OS events.
     *
     * @param e  The details of the event.
     */

	public boolean
            receiveEvent(Event e)
	{
		switch (e.type)
		{
			case football.kCmdNewGame:
				mWindow.InitGame();
				mWindow.invalidate();
				break;
            case football.kCmdPauseGame:
                mWindow.HandlePauseGame();
                break;
			case football.kCmdBeginner:
				mWindow.SetDifficulty( 1 );
				break;
			case football.kCmdAdvanced:
				mWindow.SetDifficulty( 2 );
				break;
			case football.kCmdHelp:
                mWindow.ShowHelp();
				break;
			case football.kCmdSound:
				mWindow.ToggleSound ( mSoundOn );
				break;
			case football.kCmdQuit:
				returnToLauncher();
				break;
			case football.kCmdContinue:
				int mPauseUntil = danger.system.Hardware.getSystemTime() + 1500;
				while (mPauseUntil > danger.system.Hardware.getSystemTime())
					{
					}
				mPauseUntil = 0;
				break;
			case Event.EVENT_TIMER:
                if ( ( mWindow.mGameState != mWindow.kPaused )
                        && !Screen.isWindowActive( mWindow )
                        && !mWindow.isActionMenuVisible() )
                {
                    mWindow.HandlePauseGame();
                    return true;
                }
                else
                {
                    mWindow.TimerTick();
                    return true;
                }
		}
		return (super.receiveEvent(e));
	}

	//*	--------------------	Resume

	public void
            resume()
	{
		if (null == mTimer)
			mTimer = new Timer(100, true, this);

        mWindow.LoadBitmaps();

		mWindow.show();
		mTimer.start();

		if (!football.mSoundOn) {
		    Meta.muteID(Meta.FEEDBACK_KEY_ONCE);
			Meta.muteID(Meta.FEEDBACK_KEY_REPEAT);
			Meta.muteID(Meta.SCROLL_WHEEL_UP);
			Meta.muteID(Meta.SCROLL_WHEEL_DOWN);
			Meta.muteID(Meta.FEEDBACK_WHEEL_BUTTON);
		}

		if (mWindow.mGameState == mWindow.kGameOver)
			mWindow.GameOver();
	}

	//*	--------------------	Suspend

	public void
            suspend()
	{
		if(mTimer != null) mTimer.stop();

		if (!football.mSoundOn) {
			Meta.unmuteID(Meta.FEEDBACK_KEY_ONCE);
			Meta.unmuteID(Meta.FEEDBACK_KEY_REPEAT);
			Meta.unmuteID(Meta.SCROLL_WHEEL_UP);
			Meta.unmuteID(Meta.SCROLL_WHEEL_DOWN);
			Meta.unmuteID(Meta.FEEDBACK_WHEEL_BUTTON);
		}

        mWindow.FreeBitmaps();
	}

	/* strings */
    static final String kGameName           =   "LED Football";

    static final String	kStrNewGame	   	    =	"New Game";
    static final int	kCmdNewGame   	    =	1;
    static final String	kStrPauseGame       =	"Pause Game";
    static final String	kStrUnPauseGame       =	"Unpause Game";
    static final int	kCmdPauseGame       =	2;
	static final String	kStrBeginner		=	"Beginner";
	static final int	kCmdBeginner		=	3;
	static final String	kStrAdvanced		=	"Advanced";
	static final int	kCmdAdvanced		=	4;
	static final String	kStrHelp    	    =	"Hints...";
	static final int	kCmdHelp    	    =	5;
	static final String kStrSound			=   "Play Sounds";
	static final int	kCmdSound			=   6;

	static final int	kCmdQuit			=   8;
	static final int	kCmdContinue		=   9;

	//*	--------------------------------	Class Variables
	static FootballWindow	mWindow;
	static football			mMe;
	static Timer			mTimer = null;
	static boolean			mSoundOn = true;

}

/**
 * Implements the main window class.
 *
 */
class FootballWindow extends ScreenWindow
{
	// --------------------	FootballWindow

	public
	FootballWindow()
	{
		mOffense = new Point(0,0);
		mDefense[0] = new Point(0,0);
		mDefense[1] = new Point(0,0);
		mDefense[2] = new Point(0,0);
		mDefense[3] = new Point(0,0);
		mDefense[4] = new Point(0,0);

		mRnd = new Random();

        mBitmapsReady = false;

		mAITicks = kAITicks_Beginner;

        if ( Screen.getColorSpace() != ColorSpace.GRAY4 )
            setBackgroundColor( Color.BLACK );

		InitGame();

		BuildActionMenu();           // mSoundOn, mAITicks, and mGameState must be init'ed
	}

	final void
	BuildActionMenu()
	{
		Menu menu = getActionMenu();

		menu.removeAllItems();

        menu.addItem(football.kStrNewGame, football.kCmdNewGame, 1, null, football.mMe).setShortcut('N');

        if ( mGameState == kPaused )
            menu.addItem(football.kStrUnPauseGame, football.kCmdPauseGame, 1, null, football.mMe).setShortcut('P');
        else
            menu.addItem(football.kStrPauseGame, football.kCmdPauseGame, 1, null, football.mMe).setShortcut('P');
		menu.addDivider();

		menu.addItem(football.kStrBeginner, football.kCmdBeginner, 2, null, football.mMe).setShortcut('B');
		menu.addItem(football.kStrAdvanced, football.kCmdAdvanced, 2, null, football.mMe).setShortcut('A');
        if ( mAITicks == kAITicks_Beginner ) {
		    menu.getItem( football.kCmdBeginner ).check();
		    menu.getItem( football.kCmdAdvanced ).uncheck();
        }
        else {
            menu.getItem( football.kCmdBeginner ).uncheck();
            menu.getItem( football.kCmdAdvanced ).check();
        }
		menu.addDivider();

        menu.addItem(football.kStrSound, football.kCmdSound, 2, null, football.mMe).setShortcut('S');
        if ( football.mSoundOn )
		    menu.getItem( football.kCmdSound ).check();
        else
            menu.getItem( football.kCmdSound ).uncheck();
		menu.addDivider();

        menu.addItem(football.kStrHelp, football.kCmdHelp, 2, null, football.mMe).setShortcut('H');
	}

    // load bitmaps
    final void LoadBitmaps()
    {
        if ( mBitmapsReady == false )
        {
            mLeftArrow      = football.mMe.getBitmap( Resources.ID_LEFT_ARROW );
            mRightArrow     = football.mMe.getBitmap( Resources.ID_RIGHT_ARROW );
            mHomeEndzone    = football.mMe.getBitmap( Resources.ID_HOME_ENDZONE );
            mVisitorEndzone = football.mMe.getBitmap( Resources.ID_VISITOR_ENDZONE );
            mBitmapsReady = true;
        }
    }

    final void FreeBitmaps()
    {
        mLeftArrow = null;
        mRightArrow = null;
        mHomeEndzone = null;
        mVisitorEndzone = null;

        mBitmapsReady = false;
    }

    final void ShowHelp()
    {
        HelpWindow hw = new HelpWindow();
        hw.show();
    }

    final void HandlePauseGame()
    {
        if ( mGameState != kPaused ) {
            mPreviousGameState = mGameState;
            mGameState = kPaused;
            SaveMessageState();
            SetMessage( "<<< PAUSED >>>" );
        }
        else {
            mGameState = mPreviousGameState;
            RestoreMessageState();
        }

        BuildActionMenu();
    }

    final void PlaySound( int soundID )
    {
        if ( football.mSoundOn )
            Sound.play( soundID );
    }

    final void ToggleSound( boolean soundOn )
    {
        Menu menu = getActionMenu();

        if ( soundOn ) {
            football.mSoundOn = false;
            menu.getItem( football.kCmdSound ).uncheck();

            Meta.muteID(Meta.FEEDBACK_KEY_ONCE);
            Meta.muteID(Meta.FEEDBACK_KEY_REPEAT);
            Meta.muteID(Meta.SCROLL_WHEEL_UP);
            Meta.muteID(Meta.SCROLL_WHEEL_DOWN);
            Meta.muteID(Meta.FEEDBACK_WHEEL_BUTTON);
        }
        else {
            football.mSoundOn = true;
            menu.getItem( football.kCmdSound ).check();

            Meta.unmuteID(Meta.FEEDBACK_KEY_ONCE);
            Meta.unmuteID(Meta.FEEDBACK_KEY_REPEAT);
            Meta.unmuteID(Meta.SCROLL_WHEEL_UP);
            Meta.unmuteID(Meta.SCROLL_WHEEL_DOWN);
            Meta.unmuteID(Meta.FEEDBACK_WHEEL_BUTTON);
        }
    }

    final void SetDifficulty( int level )
    {
        Menu menu = getActionMenu();

        if ( level == 1 )
        {
            mAITicks = kAITicks_Beginner;
            menu.getItem( football.kCmdBeginner).check();
            menu.getItem( football.kCmdAdvanced).uncheck();
        }
        else
        {
            mAITicks = kAITicks_Advanced;
            menu.getItem( football.kCmdBeginner).uncheck();
            menu.getItem( football.kCmdAdvanced).check();
        }
    }

    final void Seed()
    {
        mSeed++;
        mRnd.setSeed( mSeed );

        // mRnd.SetSeed( Hardware.GetSystemTime() );
    }

	// --------------------	GetWindowTitle

	public String
	GetWindowTitle()
    {
		return ( football.kGameName );
	}


    // -------------------------------------------------------------------------
    // Game logic, state transition routines


	// Timer gets called every 10th of a second to update game
    // - Update colors of graphics & text on screen
    // - Run Game Clock
    // - Run AI

	final void TimerTick()
	{
        mTenthSecTick++;

		if ( mMessage2_Time != 0 )
		{
			if ( --mMessage2_Time == 0 )
				mMessageDirty = true;			// need to flip back to message 1
		}

        if ( (mClock == 0) && (mGameState != kGameOver) && (mGameState != kPaused) )
            QuarterEnded();

		switch ( mGameState )
		{
            case kKickAftermath:
			case kAwaitingFirstMove:
				mClockPrescale++;
				if ( mClockPrescale >= kBlinkTicks )
				{
					if ( mHomeHasBall == true )
                            mOffenseColor = (mOffenseColor == mFieldColor) ? mHomeColor : mFieldColor;
						else
                            mOffenseColor = (mOffenseColor == mFieldColor) ? mVisitorColor : mFieldColor;

					mClockPrescale = 0;
				}

				ColorCycleMessage( 1 );
				break;

            case kGameStopped:
                ColorCycleMessage( 2 );
                break;

			case kGameRunning:
					mClockPrescale++;
					if ( mClockPrescale >= kClockTicks )
					{
						mClockPrescale = 0;
						if (mClock > 0)
                        {
						    mClock--;
                            //Sound.play( Resources.ID_TICK );
                        }
					}

					mAIPrescale++;
					if ( mAIPrescale >= mAITicks )
					{
						mAIPrescale = 0;
						RunAI();
					}
					break;

			case kOffenseTackled:
				mClockPrescale++;
				if ( mClockPrescale >= kBlinkTicks )
				{
                    if ( mHomeHasBall == true )
			            mDefenseColor[mTackler] = ( mDefenseColor[mTackler] == mTackledColor ) ? mVisitorColor : mTackledColor;
                    else
                        mDefenseColor[mTackler] = ( mDefenseColor[mTackler] == mTackledColor ) ? mHomeColor : mTackledColor;

					mClockPrescale = 0;
					ColorCycleMessage( 1 );
				}
				break;

			case kTouchdown:
					mClockPrescale++;
					if ( mClockPrescale >= kBlinkTicks ) {
                        mOffenseColor = mColorTable[mMessageColor];     // color cycle offense guy with text (Cool!!)

                        /*
                        if ( mHomeHasBall == false )    // NOTE! We've already swapped by the time we get here!
                            mOffenseColor = ( mOffenseColor == mTouchdownColor ) ? mHomeColor : mTouchdownColor;
                        else
                            mOffenseColor = ( mOffenseColor == mTouchdownColor ) ? mVisitorColor : mTouchdownColor;
                        */

						mClockPrescale = 0;
						ColorCycleMessage( 1 );
					}
					break;

            case kGameOver:
                    ColorCycleMessage( 1 );
                    break;

            case kPaused:
                    ColorCycleMessage( 1 );
                    break;
		}

		invalidate();                           // repaint everything
	}

	final void Tackled( int defenseMan )
	{
		PlaySound(sSound_Tackle);
		football.mMe.cancelEvents(Event.DEVICE_WHEEL_BUTTON, 0);

		mTackler = defenseMan;
		mGameState = kOffenseTackled;                                                           // *** STATE CHANGE

		// Pause after being tackled. Otherwise a user could keep
		// pressing a movement key, not realizing they have been
		// tackled. This would start the next play without them
		// realizing it.
		if (mPauseUntil == 0)
			mPauseUntil = danger.system.Hardware.getSystemTime() + 1000;

		if (mClock == mPreviousClock)
			mClock--;

        SetMessage( "TACKLED!" );
        SetMessage3( Math.abs(mFieldPosition - mBeginningPosition) + " Yard Gain" );

		mBeginningPosition = mFieldPosition;							//tj
		mPreviousClock = mClock;
	}


    /*
        After a Touchdown, ball possession swaps, but clock stays the same
    */

	final void Touchdown()
	{
		int YardsGained;												//tj

		mGameState = kTouchdown;                                                                // *** STATE CHANGE

		if ( mHomeHasBall )
		{
			YardsGained = 100 - mBeginningPosition;						//tj
			mHomeHasBall = false;			        // visitor's turn
			mFieldPosition = 100 - 20;		        // go to visitor 20
			mBeginningPosition = mFieldPosition;

			mHomeScore += 7;
			if ( mHomeScore > 99 )
				mHomeScore = 99;
		}
		else
		{
			YardsGained = mBeginningPosition;						//tj
			mHomeHasBall = true;			        // home's turn
			mFieldPosition = 20;			        // go to home 20
			mBeginningPosition = mFieldPosition;

			mVisitorScore += 7;
			if ( mVisitorScore > 99 )
				mVisitorScore = 99;
		}

        SetMessage( "TOUCHDOWN!  " + YardsGained + " YARD RUN!");

		if ( ( mQuarter == 4 ) && ( mClock == 0 ) ) {                                           // ### TEST STATE
            SetMessage( "GAME OVER!  GAME-ENDING TOUCHDOWN!" );
			mGameState = kGameOver;                                                             // *** STATE CHANGE
		}

        // Pause after a touchdown. Otherwise a user could keep
        // pressing a movement key, not realizing they have been
        // tackled. This would start the next play without them
        // realizing it.
        if (mPauseUntil == 0)
            mPauseUntil = danger.system.Hardware.getSystemTime() + 1000;

		PlaySound(sSound_Touchdown);

		mPreviousClock = mClock;
	}

	final void GameRunning()
	{
		String msg = "";

		mGameState = kGameRunning;			    // let the computer players go!                 // *** STATE CHANGE

		if (mHomeHasBall == true)
			mOffenseColor = mHomeColor;
		else
			mOffenseColor = mVisitorColor;

		switch( mQuarter )
		{
			case 1:	msg = "1st Quarter"; break;
			case 2: msg = "2nd Quarter"; break;
			case 3: msg = "3rd Quarter"; break;
			case 4: msg = "4th Quarter"; break;
		}

		SetMessage( msg );
	}

	final void GameOver()
	{
		String winner;

        if ( mHomeScore > mVisitorScore )
            winner = "Home Team WINS!";
        else
            if ( mHomeScore != mVisitorScore )
                winner = "Visitor Team WINS!";
            else
                winner = "It's a TIE!";

		SetMessage( "GAME OVER!  " + winner );

		// Check for touchdown at the end of the quarter
		if (mGameState != kGameOver)
			PlaySound( sSound_Cheers );

        // inverted, since we will have flipped sides
        mOffenseColor = (mHomeHasBall = true) ? mVisitorColor : mHomeColor;

		mGameState = kGameOver;

		if ( (mFieldPosition == 0) || (mFieldPosition == 100) )
			Touchdown();
	}


    /*
        QuarterEnded() is *only* called when mClock hits 0.

        From the Mattel manual:

            Q1: Home Team has the ball, "1st & 10" on its own 20 yard line

            Q2: Field position & ball possession same as at END OF FIRST QUARTER

            Q3: This time the Vistor has the ball, "1st & 10" on its own 20 yard line

            Q4: Field position & ball possesstion same as at END OF THIRD QUARTER

        "1st & 10" means 1st down, 10 yards to go (YTG)
    */

	final void QuarterEnded()
	{
		String DisplayQuarter = "";						//tj

		PlaySound(sSound_LongWhistle);

		// Check for touchdown at the end of the quarter
		if ( (mFieldPosition == 0) || (mFieldPosition == 100) )
			Touchdown();

		if ( mQuarter == 4 ) {
			GameOver();
		}
		else {
			switch (mQuarter) {
				case 1: DisplayQuarter = "1ST QUARTER!\n"; break;
				case 2:	DisplayQuarter = "1ST HALF, POSSESSION SWAPS!\n"; break;
				case 3:	DisplayQuarter = "3RD QUARTER!\n"; break;
			}

			if (mGameState != kTouchdown)
                SetMessage( "END OF " + DisplayQuarter );

            if ( mQuarter == 2 ) {                      // did 2nd Quarter just end?
                mHomeHasBall = false;                   // Visitor gets ball unconditionally
                mFieldPosition = 100 - 20;
	            mBeginningPosition = mFieldPosition;
            }

            mQuarter++;

            // Pause after Quarter End. Otherwise a user could keep
            // pressing a movement key, not realizing they have been
            // tackled. This would start the next play without them
            // realizing it.
            if (mPauseUntil == 0)
                mPauseUntil = danger.system.Hardware.getSystemTime() + 1000;

            mClock = kQuarterLength;					// another 15 minutes...
            mPreviousClock = kQuarterLength;

            InitDown( false );      // 1st & 10, but *don't* call InitField()
                                    // we'll call NextDown() when user pushes move (like for Tackled)

                                    // do this so everything will idle
            mGameState = kGameStopped;                                                          // *** STATE CHANGE
		}
	}

	final void InitField()
	{
		int xxx;

		// Init player position
		MoveOffense( 1, 2 );

		mOffenseColor = ( mHomeHasBall == true ) ? mHomeColor : mVisitorColor;

		// Init computer opponent positions
		MoveDefense( 0, 4, 1 );
		MoveDefense( 1, 4, 2 );
		MoveDefense( 2, 4, 3 );

		MoveDefense( 3, 6, 2 );
		MoveDefense( 4, 9, 2 );

		for ( xxx = 0; xxx != 5; xxx++ )
			mDefenseColor[xxx] = ( mHomeHasBall == true ) ? mVisitorColor : mHomeColor;
	}

    final void SetupFirstDown()
    {
        mYardsToGo = 10;                // assume 1st & 10

        if ( mHomeHasBall )
        {
            if ( mFieldPosition > 90 )
                mYardsToGo = (100 - mFieldPosition);
        }
        else
        {
            if ( mFieldPosition < 10 )
                mYardsToGo = mFieldPosition;
        }

        mDown = 0;						// inc below will make it 1
    }

	final void NextDown()
	{
		String DisplayQuarter="";

		if ( mDown == 4 )
		{
			if ( mYardsToGo != 0 )				// did they make it?
			{									// no, ball possession goes to the other side
				mDown = 0;
				mYardsToGo = 10;
				mHomeHasBall = !mHomeHasBall;
                if ( mJustKicked )
                    SetMessage2( "TURNOVER ON KICK", kDefaultMessage2Duration );
                else
                    SetMessage2( "TURNOVER ON DOWNS", kDefaultMessage2Duration );
                mJustKicked = false;
                PlaySound(sSound_LongWhistle);
			}
            else
                SetupFirstDown();
	    }
		else                                    // not 4th down
			if ( mYardsToGo == 0 )
                SetupFirstDown();

		mDown++;

        if ( mDown == 4 )
            SetMessage2( "4th Down, Offense may kick", kDefaultMessage2Duration );

		// Set up the players; will set up appropriately for possession
		InitField();

        // This is a bit of a hack, since InitField() will inadvertently
        // change the game state to kGameRunning.
        mGameState = kAwaitingFirstMove;                                                        // *** STATE CHANGE

		switch ( mQuarter ) {
			case 1: DisplayQuarter = "1st Quarter"; break;
			case 2: DisplayQuarter = "2nd Quarter"; break;
			case 3: DisplayQuarter = "3rd Quarter"; break;
			case 4: DisplayQuarter = "4th Quarter"; break;
		}

		SetMessage( DisplayQuarter + "  [Move to Start]" );
	}

	final void InitDown( boolean doNextDown )
	{
		mDown = 0;
        mYardsToGo = 10;
		mBeginningPosition = mFieldPosition;						//tj
		if ( doNextDown )
            NextDown();
	}

	final void InitGame()
	{
		Seed();

        // Use different color schemes for Greyscale & Color devices
        if ( Screen.getColorSpace() == ColorSpace.GRAY4 ) {
             mFieldColor     = Color.WHITE;
             mYardlineColor  = Color.BLACK;

             mHomeColor      = Color.BLACK;
             mVisitorColor   = Color.GRAY10;
             mTackledColor   = Color.WHITE;
             mTouchdownColor = Color.WHITE;

             mDisplaysColor  = Color.BLACK;

             mBackgndColor   = Color.WHITE;
             mTextColor      = Color.BLACK;

             mLEDColor       = Color.BLACK;

             mColorTable[0]  = Color.WHITE;
             mColorTable[1]  = Color.GRAY13;
             mColorTable[2]  = Color.GRAY11;
             mColorTable[3]  = Color.GRAY9;
             mColorTable[4]  = Color.GRAY7;
             mColorTable[5]  = Color.GRAY5;
             mColorTable[6]  = Color.GRAY3;
             mColorTable[7]  = Color.BLACK;
         }
         else {
             mFieldColor     = Color.pack( 0, 186, 0 );
             mYardlineColor  = Color.pack( 213, 255, 187 );

             mHomeColor      = Color.pack( 255, 255, 0 );
             mVisitorColor   = Color.pack( 102, 41, 22 );
             mTackledColor   = Color.ORANGE;
             mTouchdownColor = Color.pack( 255, 255, 255 );

             mDisplaysColor  = Color.BLACK;

             mBackgndColor   = Color.BLACK;
             mTextColor      = Color.WHITE;

             mLEDColor       = Color.pack( 255, 0, 0 );

             mColorTable[0]  = Color.BLACK;
             mColorTable[1]  = Color.RED;
             mColorTable[2]  = Color.ORANGE;
             mColorTable[3]  = Color.YELLOW;
             mColorTable[4]  = Color.GREEN;
             mColorTable[5]  = Color.BLUE;
             mColorTable[6]  = Color.PURPLE;
             mColorTable[7]  = Color.WHITE;
         }

		mMessage2_Time = 0;

        mTenthSecTick = 0;
		mClockPrescale = 0;
		mAIPrescale = 0;
        mPauseUntil = 0;
        mJustKicked = false;

		// Fresh start
        mClock = kQuarterLength;				// 15.0 "minutes" (really just 150 seconds)
        mPreviousClock = kQuarterLength;

		mHomeScore = 0;
		mVisitorScore = 0;

		mHomeHasBall = true;		            // determines which direction (L->R or R->L) game runs
		mFieldPosition = 20;		            // home's 20

        mQuarter = 1;
        InitDown( true );                       // 1st & 10

		PlaySound(sSound_NewGame);
	}


	// -------------------------------------------------------------------------
	// Computer Defense

	final boolean BlockedYard( int xxx, int yard )
	{
		int yyy;

		if ( (yard == mOffenseYard) && (mDefenseRow[xxx] == mOffenseRow) )
		{
			Tackled( xxx );
			return true;
		}

		for ( yyy = 0; yyy != 5; yyy++ )
		{
			if ( yyy != xxx )
			{
				if ( (yard == mDefenseYard[yyy])
					 && (mDefenseRow[xxx] == mDefenseRow[yyy]) )
					return true;
			}
		}

		return false;
	}

	final boolean BlockedRow( int xxx, int row )
	{
		int yyy;

		if ( (row == mOffenseRow) && (mDefenseYard[xxx] == mOffenseYard) )
		{
			Tackled( xxx );
			return true;
		}

		for ( yyy = 0; yyy != 5; yyy++ )
		{
			if ( yyy != xxx )
			{
				if ( (row == mDefenseRow[yyy])
					 && (mDefenseYard[xxx] == mDefenseYard[yyy]) )
					return true;
			}
		}

		return false;
	}

	final void RunAI()
	{
		int xxx;
		int dumb;
		int	yardOrRow;
		int desiredYard;
		int desiredRow;

		// pick a random player
		xxx = mRnd.nextInt(5);

		dumb = mRnd.nextInt(2);
		yardOrRow = mRnd.nextInt(5000);

		if ( yardOrRow < 2500 )		            // can only move 1 way per turn
		{
			// see if Offense is to left or right, and if we can, move that way
			if ( mOffenseYard > mDefenseYard[xxx] )
			{
				desiredYard = mDefenseYard[xxx] + 1;
				// Defense is not allowed to wrap. If they try, move the other way
				if ( desiredYard > 9 )
					desiredYard = 8;

				if ( !BlockedYard( xxx, desiredYard ) )
					MoveDefense( xxx, desiredYard, mDefenseRow[xxx] );
				else
					if ( dumb == 1 )
					{
						desiredYard = mDefenseYard[xxx] - 1;
						if ( desiredYard < 1 )
							desiredYard = 2;

						if ( !BlockedYard( xxx, desiredYard ) )
							MoveDefense( xxx, desiredYard, mDefenseRow[xxx] );
					}
			}
			else	                            // offenseYard <= defenseYard
			{
				desiredYard = mDefenseYard[xxx] - 1;
				if ( desiredYard < 1 )
					desiredYard = 2;

				if ( !BlockedYard( xxx, desiredYard ) )
					MoveDefense( xxx, desiredYard, mDefenseRow[xxx] );
				else
					if ( dumb == 1 )
					{
						desiredYard = mDefenseYard[xxx] + 1;
						if ( desiredYard > 9 )
							desiredYard = 8;

						if ( !BlockedYard( xxx, desiredYard ) )
							MoveDefense( xxx, desiredYard, mDefenseRow[xxx] );
					}
			}
		}
		else
		{
			// see if Offense is above or below, and if we can, move that way
			//  (slightly different, since can't wrap top-to-bottom)
			if ( (mOffenseRow > mDefenseRow[xxx]) )
			{
				desiredRow = mDefenseRow[xxx] + 1;	// know this won't wrap, since offense > defense

				if ( !BlockedRow( xxx, desiredRow ) )
					MoveDefense( xxx, mDefenseYard[xxx], desiredRow );
				else
					if ( (dumb == 1) && (mDefenseRow[xxx] != 1) )
					{
						desiredRow = mDefenseRow[xxx] - 1;
						if ( !BlockedRow( xxx, desiredRow ) )
							MoveDefense( xxx, mDefenseYard[xxx], desiredRow );
					}
			}
			else	                            // offenseRow <= defenseRow
			{
				desiredRow = mDefenseRow[xxx] - 1;

				if ( (desiredRow >= 1) && !BlockedRow( xxx, desiredRow ) )
					MoveDefense( xxx, mDefenseYard[xxx], desiredRow );
				else
					if ( dumb == 1 )
					{
						desiredRow = mDefenseRow[xxx] + 1;
						if ( (desiredRow <= 3) && !BlockedRow( xxx, desiredRow ) )
							MoveDefense( xxx, mDefenseYard[xxx], desiredRow );
					}
			}
		}

		invalidate();
	}


	// -------------------------------------------------------------------------
	// Player Position Utilities

	final boolean MoveOffense( int yard, int row )
	{
		int xxx;

		// first see if that cell has a Defense man in it
		if ( mGameState == kGameRunning )
			for ( xxx = 0; xxx != 5; xxx++ )
			{
				if ( (mDefenseYard[xxx] == yard) && (mDefenseRow[xxx] == row) )
				{
					Tackled( xxx );
					return false;
				}
			}

		// nope, so move there!
		if ( mHomeHasBall )
		{
			switch ( yard )
			{
				case 1:	mOffense.h = kYard1; break;
				case 2: mOffense.h = kYard2; break;
				case 3: mOffense.h = kYard3; break;
				case 4: mOffense.h = kYard4; break;
				case 5: mOffense.h = kYard5; break;
				case 6: mOffense.h = kYard6; break;
				case 7: mOffense.h = kYard7; break;
				case 8: mOffense.h = kYard8; break;
				case 9: mOffense.h = kYard9; break;
			}
		}
		else
		{
			switch ( yard )
			{
				case 1:	mOffense.h = kYard9; break;
				case 2: mOffense.h = kYard8; break;
				case 3: mOffense.h = kYard7; break;
				case 4: mOffense.h = kYard6; break;
				case 5: mOffense.h = kYard5; break;
				case 6: mOffense.h = kYard4; break;
				case 7: mOffense.h = kYard3; break;
				case 8: mOffense.h = kYard2; break;
				case 9: mOffense.h = kYard1; break;
			}
		}

		switch ( row )
		{
			case 1: mOffense.v = kRow1; break;
			case 2: mOffense.v = kRow2; break;
			case 3: mOffense.v = kRow3; break;
		}

		mOffenseYard = yard;
		mOffenseRow = row;

		if ( mGameState == kAwaitingFirstMove )
			if (mHomeHasBall == true)
				mOffenseColor = mHomeColor;
			else
				mOffenseColor = mVisitorColor;

        GameRunning();

		return true;
	}

	final void MoveDefense( int man, int yard, int row )
	{
		if ( mGameState == kOffenseTackled )
			return;

		if ( mHomeHasBall )
		{
			switch ( yard )
			{
				case 1:	mDefense[man].h = kYard1; break;
				case 2: mDefense[man].h = kYard2; break;
				case 3: mDefense[man].h = kYard3; break;
				case 4: mDefense[man].h = kYard4; break;
				case 5: mDefense[man].h = kYard5; break;
				case 6: mDefense[man].h = kYard6; break;
				case 7: mDefense[man].h = kYard7; break;
				case 8: mDefense[man].h = kYard8; break;
				case 9: mDefense[man].h = kYard9; break;
			}
		}
		else
		{
			switch ( yard )
			{
				case 1:	mDefense[man].h = kYard9; break;
				case 2: mDefense[man].h = kYard8; break;
				case 3: mDefense[man].h = kYard7; break;
				case 4: mDefense[man].h = kYard6; break;
				case 5: mDefense[man].h = kYard5; break;
				case 6: mDefense[man].h = kYard4; break;
				case 7: mDefense[man].h = kYard3; break;
				case 8: mDefense[man].h = kYard2; break;
				case 9: mDefense[man].h = kYard1; break;
			}
		}

		switch ( row )
		{
		case 1: mDefense[man].v = kRow1; break;
		case 2: mDefense[man].v = kRow2; break;
		case 3: mDefense[man].v = kRow3; break;
		}

		mDefenseYard[man] = yard;
		mDefenseRow[man] = row;
	}


	// -------------------------------------------------------------------------
	// User Move Handlers

	final void HandleUp()
	{
		int desiredRow = mOffenseRow;

		if ( CanMove() && (desiredRow != 1) )
		{
			if ( MoveOffense( mOffenseYard, --desiredRow ) )		// will update mOffenseRow
				invalidate();
		}
	}

	final void HandleDown()
	{
		int desiredRow = mOffenseRow;

		if ( CanMove() && (desiredRow != 3) )
		{
			if ( MoveOffense( mOffenseYard, ++desiredRow ) )		// will update mOffenseRow
				invalidate();
		}
	}

	final void HandleKick()             // regardless of KICK outcome, we'll swap ball possession
	{
		int distance;
		int needed;

        // Can *only* kick at start of 4th down!
		if ( (mGameState == kAwaitingFirstMove) && (mDown == 4) && (mYardsToGo != 0) )
		{
            PlaySound(sSound_Punt);

			Seed();
			distance = mRnd.nextInt( 65 ) + 1;			// kick goes from 1 to 65 yards

			if ( mHomeHasBall )						    // internally, field goes:
				needed = 100 - mFieldPosition;		    //		Home 0 -> 50 -> 100 Visitor
			else
				needed = mFieldPosition;

			// if distance >= needed, player gets a Field Goal (+3 points)
			// if distance < needed, just update mFieldPosition and swap ball possession

            if ( distance >= needed ) {
                SetMessage( "FIELD GOAL! " + distance + " YARD KICK!" );

				if ( mHomeHasBall ) {
				    mHomeScore += 3;
					if ( mHomeScore > 99 )
						mHomeScore = 99;

					mFieldPosition = 100 - 20;          // go to -> visitor 20
				}
				else {
					mVisitorScore += 3;
					if ( mVisitorScore > 99 )
						mVisitorScore = 99;

                        mFieldPosition = 20;            // go to -> home 20
				}

				PlaySound(sSound_Cheers);                   // Mattel Football played "CHARGE" for Field Goal
			}
            else {
                SetMessage( "PUNT! " + distance + " YARD KICK!" );
                AdvanceFieldPosition( distance );
                PlaySound(sSound_ShortWhistle);             // Mattel Football does 2 whistles for a punt
                PlaySound(sSound_ShortWhistle);
			}

            mGameState = kKickAftermath;
            mJustKicked = true;

            mBeginningPosition = mFieldPosition;

		    if (mClock != 0)
		        mClock--;                                   // take time off the clock for play
		}
		else {
			SetMessage2( "Can only kick at start of 4th Down!", kDefaultMessage2Duration );
            Meta.play(Meta.BEEP_COMMAND_REJECTED);
        }

		// kKickAftermath handler will flip possession...
	}

	final void AdvanceFieldPosition( int distance )	        // to us, field goes:  Home 0 -> 50 -> 100 Visitor
	{
		if ( mHomeHasBall )
		{
			mFieldPosition += distance;
			if ( mFieldPosition > 100 )
				mFieldPosition = 100;
		}
		else
		{
			mFieldPosition -= distance;
			if ( mFieldPosition < 0 )
				mFieldPosition = 0;
		}
	}

	final boolean TouchdownFieldPosition()
	{
		if ( mHomeHasBall )
			return ( mFieldPosition == 100 );
		else
			return ( mFieldPosition == 0 );
	}

	final void HandleMove(int direction)
	{
		int desiredYard = mOffenseYard;

		if ( CanMove() )
		{
			// You can not back up to the previous screen
			if (direction == -1 && mOffenseYard == 1)
				return;
			else if (direction == 1 && mOffenseYard == 9)
				desiredYard = 1;
			else
				desiredYard += direction;

			if ( MoveOffense( desiredYard, mOffenseRow ) )			// will update mOffenseYard
			{
				if ( mYardsToGo != 0 )
					mYardsToGo -= direction;

				AdvanceFieldPosition(direction);
				if ( TouchdownFieldPosition() )
					Touchdown();

				invalidate();
			}
		}
		else
			NextDown();		// couldn't move, so this click must be to (re)start something
	}

	final boolean CanMove()
	{
		return ( (mGameState == kAwaitingFirstMove) || (mGameState == kGameRunning) );
	}

	// -------------------------------------------------------------------------
	// Paint Routines

	final void DrawPlayfield( Pen inPen )
	{
		int xPos = kYardStart-kYardSpacing;
		int xxx;

		// erase playfield
        inPen.setColor( mFieldColor );
		inPen.fillRect( kYardStart, kFieldTop, kYardStart + (9*kYardSpacing), kFieldBottom );

		inPen.setColor( mYardlineColor );
		inPen.setSize( 1, 1 );

		// draw the vertical yard lines
		for ( xxx = 0; xxx != 10; xxx++ )
		{
			xPos += kYardSpacing;
			inPen.drawLine( xPos, kFieldTop, xPos, kFieldBottom - 1 );
		}

		// draw top & bottom edges of field
        /*
		inPen.setColor( mTextColor );
		inPen.drawLine( kYardStart, kFieldTop, xPos, kFieldTop );
		inPen.drawLine( kYardStart, kFieldBottom, xPos, kFieldBottom );
        */

		// draw the horizontal ticks on each yard line
		inPen.setColor( mYardlineColor );
		xPos = kYardStart - (kTickWidth/2);
		for ( xxx = 0; xxx != 8; xxx++ )
		{
			xPos += kYardSpacing;
			inPen.drawLine( xPos, kFieldTop+kTopTickY, xPos+kTickWidth, kFieldTop+kTopTickY );
			inPen.drawLine( xPos, kFieldTop+kBottomTickY, xPos+kTickWidth, kFieldTop+kBottomTickY );
		}

		// draw home & visitor end zones
        inPen.drawBitmap( 3, kFieldTop, mHomeEndzone );
        inPen.drawBitmap( 210, kFieldTop, mVisitorEndzone );
	}

	final void DrawPlayers( Pen inPen )
	{
		int xxx;

		// draw offense (human player)
		inPen.setColor( mOffenseColor );
		inPen.setSize( 1, 1 );

		inPen.fillRect( mOffense.h, mOffense.v,
						mOffense.h + kBlockWidth, mOffense.v + kBlockHeight );

        /*
        // bars on either side of offense player
        inPen.drawLine( mOffense.h, mOffense.v - 2,
                        mOffense.h + kBlockWidth - 1, mOffense.v - 2 );

		inPen.drawLine( mOffense.h, mOffense.v + kBlockHeight + 1,
						mOffense.h + kBlockWidth - 1, mOffense.v + kBlockHeight + 1 );
        */

		// draw defense (computer players)
		for ( xxx = 0; xxx != 5; xxx++ )
		{
			inPen.setColor( mDefenseColor[xxx] );
			inPen.fillRect( mDefense[xxx].h, mDefense[xxx].v,
							mDefense[xxx].h + kBlockWidth, mDefense[xxx].v + kBlockHeight );
		}
	}


	public void DrawDisplays( Pen inPen )
	{
		int realFieldPos;
        Font font;

		// Draw message field
		if ( mMessageDirty )		    // does the right this for Msg2, which also sets dirty
			mMessageDirty = false;      // we're handling it, so no longer dirty

        inPen.push();
        font = Font.findBoldSystemFont();
        inPen.setFont( font );

		if ( mMessage2_Time != 0 )
		{
			inPen.setColor( mMessage2_Color );
			inPen.drawText( (240/2)-(font.getWidth(mMessage2)/2), 13, mMessage2 );
		}
		else
		{
			inPen.setColor( mColorTable[mMessageColor] );

            if (mMessage3 != null )
            {
                inPen.drawText( (120/2)-(font.getWidth(mMessage)/2), 13, mMessage );
                inPen.setColor( mTextColor );
                inPen.drawText( ((240/2) + (120/2))-(font.getWidth(mMessage3)/2), 13, mMessage3 );
            }
            else
                inPen.drawText( (240/2)-(font.getWidth(mMessage)/2), 13, mMessage );
		}

        inPen.pop();

		// Draw YTG, make it blink on & off when it hits 0
        // if ( (mYardsToGo != 0) || (mGameState != kGameRunning) || ((mTenthSecTick&4) != 0) )
        if ( (mYardsToGo != 0) || ((mTenthSecTick&4) != 0) )
            DrawLEDInt( inPen, kHomeScoreLeft, kScoreTop, mYardsToGo, 2 );

        inPen.setColor( mTextColor );
		inPen.drawText( kHomeScoreLeft + 22, kScoreTop + 8, "YTG" );

		// Draw Visitor Score
		DrawLEDInt( inPen, kVisitorScoreLeft, kScoreTop, mVisitorScore, 2 );
		inPen.setColor( mTextColor );
		inPen.drawText( kVisitorScoreLeft - 32, kScoreTop + 8, "Visitor" );

		// Draw Clock
		DrawLEDInt( inPen, kClockLeft, kScoreTop, mClock/10, 2 );
		DrawLEDInt( inPen, kClockLeft + 24, kScoreTop, mClock-((mClock/10)*10), 1 );
		inPen.setColor( mLEDColor );
		inPen.drawPoint( kClockLeft + 20, kScoreTop + 8 );

		// Draw Down
		DrawLEDInt( inPen, kDownLeft, kStatusTop, mDown, 1 );
		inPen.setColor( mTextColor );
		inPen.drawText( kDownLeft + 12, kStatusTop + 8, "Down" );

		// Draw Home score
		DrawLEDInt( inPen, kYTGLeft, kStatusTop, mHomeScore, 2 );
		inPen.setColor( mTextColor );
		inPen.drawText( kYTGLeft - 28, kStatusTop + 8, "Home" );

		// Draw Field Position
		realFieldPos = ( mFieldPosition > 50 ) ? (50-(mFieldPosition-50)) : mFieldPosition;
		DrawLEDInt( inPen, kFieldPosLeft, kStatusTop, realFieldPos, 2 );

        // Draw arrow indicating which side of the 50-yard-line we're on

        if ( mFieldPosition < 50 )
            inPen.drawBitmap( kFieldPosLeft - 15, kStatusTop + 0, mLeftArrow );
        else
            if ( mFieldPosition > 50 )
                inPen.drawBitmap( kFieldPosLeft + 23, kStatusTop + 0, mRightArrow );

		/*
        // This code drew it the way the real LED display showed it on the Mattel game
		DrawLEDRaw( inPen, kFieldPosLeft + 23, kStatusTop,
					(realFieldPos==mFieldPosition)&&(!mHomeHasBall||(realFieldPos!=50))?
									kOffenseIsHomeSide:kOffenseIsVisitorSide );
        */
	}


	public void
    paint(Pen inPen)
	{
        clear( inPen );                 // this is needed to make the black background work
                                        // since screen is double-buffered, you won't see any flicker
		DrawPlayfield( inPen );
		DrawPlayers( inPen );
		DrawDisplays( inPen );
	}


	public boolean
    receiveEvent(Event e)
	{
		switch (e.type)
		{
			case Event.EVENT_HINGE_CHANGED:
				invalidate();
			break;
		}
		return (super.receiveEvent(e));
	}


	public boolean
    eventWidgetUp(int inWhichWidget, Event inEvent)
	{
		boolean	consumed = false;

        if ( mGameState != kPaused )                // ONLY HANDLE IF NOT PAUSED
		    switch (inWhichWidget)
		    {
			case Event.DEVICE_MULTIPLE_WHEEL:
			case Event.DEVICE_WHEEL:		        // wheel scroll up
		        HandleUp();                         // only do it if we're unpaused!

                break;

			case Event.DEVICE_BUTTON_BACK:
				HandleKick();
				break;
		    }

        Seed();						                // (always) seed rnd # gen with time of user move
		return (consumed || super.eventWidgetUp(inWhichWidget, inEvent));
	}

	// --------------------	EventDeviceWidgetDown
	public boolean
    eventWidgetDown(int inWhichWidget, Event inEvent)
	{
		boolean	consumed = false;

		// sleep some so next play does not start right away
		if (mPauseUntil != 0 && mPauseUntil > danger.system.Hardware.getSystemTime())
			return super.eventWidgetDown(inWhichWidget, inEvent);
		mPauseUntil = 0;

        if ( mGameState != kPaused )                // ONLY HANDLE IF NOT PAUSED
		    switch (inWhichWidget)
		    {
			case Event.DEVICE_WHEEL_BUTTON:
				HandleMove(1);
				break;

			case Event.DEVICE_ARROW_RIGHT:
				if ( mHomeHasBall )
					HandleMove(1);
				// Mattel Football I does not allow you to move backwards
				//				else
				//					HandleMove(-1);
				break;

			case Event.DEVICE_ARROW_LEFT:
				if ( mHomeHasBall )
					; //	HandleMove(-1);
				else
					HandleMove(1);
				break;

			case Event.DEVICE_MULTIPLE_WHEEL:
			case Event.DEVICE_WHEEL:                // wheel scroll down
			case Event.DEVICE_ARROW_DOWN:
				HandleDown();
				break;

			case Event.DEVICE_ARROW_UP:
				HandleUp();
				break;

            case Event.DEVICE_BUTTON_JUMP:          // user is JUMPing out
                if ( mGameState == kGameRunning )   // if game is running, help 'em out (won't pause if tackled or TD)
                    HandlePauseGame();
                break;
		    }

		Seed();						                // seed rnd # gen with time of user move

		return ( consumed || super.eventWidgetDown(inWhichWidget, inEvent) );
	}


	// --------------------	EventKeyUp
	public boolean
    eventKeyUp(char inKey, Event event)
	{
		switch (inKey)
		{
			/*
			// testing only keys
			case 'd':	mDown++; break;
			case 'f':	AdvanceFieldPosition( 1 ); break;
            case 'q':   mClock = 0; break;
            case 'c':   mClock = 2; break;
            case 'h':   mHomeScore += 7; break;
            case 'v':   mVisitorScore += 7; break;
			/**/

            case 'p':
            case 'P':
                HandlePauseGame();
                break;

			case 'k':
			case 'K':
                if ( mGameState != kPaused )
				    HandleKick();
				break;

            case '\r':
                if ( mGameState != kPaused )
                    HandleMove( 1 );
		}

        Seed();					                    // seed rnd # gen with time of user move

		return true;
	}


	// -------------------------------------------------------------------------
	// LED Display Stuff

	public void DrawLEDInt( Pen inPen, int xPos, int yPos, int val, int digits )
	{
		int divisor = 1;
		int digit;

		switch ( digits )
		{
			case 8:	divisor = 10000000; break;
			case 7:	divisor = 1000000; break;
			case 6:	divisor = 100000; break;
			case 5:	divisor = 10000; break;
			case 4:	divisor = 1000; break;
			case 3: divisor = 100; break;
			case 2: divisor = 10; break;
		}

		do
		{
			digit = val/divisor;
			val -= (digit*divisor);
			divisor /= 10;

			DrawLEDDigit( inPen, xPos, yPos, digit );

			xPos += kLEDDigitSpacing;
		}
		while ( divisor >= 1 );
	}

	final void DrawLEDDigit( Pen inPen, int xPos, int yPos, int val )
	{
		if ( val <= 9 )
		{
			switch ( val )
			{
				case 0:	val = 0x7e; break;
				case 1: val = 0x30; break;
				case 2: val = 0x6d; break;
				case 3: val = 0x79; break;
				case 4: val = 0x33; break;
				case 5: val = 0x5b; break;
				case 6: val = 0x5f; break;
				case 7: val = 0x70; break;
				case 8: val = 0x7f; break;
				case 9: val = 0x7b; break;
			}
		}

		DrawLEDRaw( inPen, xPos, yPos, val );
	}

	//   a			bits:	xxxx xxxx xxxx xxxx xxxx xxxx xabc defg
	//   -
	// f|g|b
	//   -
	// e| |c
	//   -
	//   d

	final void DrawLED_H( Pen inPen, int scale, int xPos, int yPos )
	{
		//inPen.SetColor( Color.RED);
		inPen.drawLine( xPos + 1, yPos, xPos + scale, yPos );
	}

	final void DrawLED_V( Pen inPen, int scale, int xPos, int yPos )
	{
		inPen.drawLine( xPos, yPos, xPos, yPos + scale - 1 );
	}

	final void DrawLEDRaw( Pen inPen, int xPos, int yPos, int bits )
	{
		// a
		if ( (bits & 0x40) != 0 ) inPen.setColor( mLEDColor ); else inPen.setColor( mBackgndColor );
		DrawLED_H( inPen, kLEDScale, xPos + 1, yPos + 0 );

		// b
		if ( (bits & 0x20) != 0 ) inPen.setColor( mLEDColor ); else inPen.setColor( mBackgndColor );
		DrawLED_V( inPen, kLEDScale, xPos + kLEDScale + 3, yPos + 1 );

		// c
		if ( (bits & 0x10) != 0 ) inPen.setColor( mLEDColor ); else inPen.setColor( mBackgndColor );
		DrawLED_V( inPen, kLEDScale, xPos + kLEDScale + 3, yPos + 2 + kLEDScale );

		// d
		if ( (bits & 0x08) != 0 ) inPen.setColor( mLEDColor ); else inPen.setColor( mBackgndColor );
		DrawLED_H( inPen, kLEDScale, xPos + 1, yPos + (2*kLEDScale) + 2 );

		// e
		if ( (bits & 0x04) != 0 ) inPen.setColor( mLEDColor ); else inPen.setColor( mBackgndColor );
		DrawLED_V( inPen, kLEDScale, xPos, yPos + 2 + kLEDScale );

		// f
		if ( (bits & 0x02) != 0 ) inPen.setColor( mLEDColor ); else inPen.setColor( mBackgndColor );
		DrawLED_V( inPen, kLEDScale, xPos, yPos + 1 );

		// g
		if ( (bits & 0x01) != 0 ) inPen.setColor( mLEDColor ); else inPen.setColor( mBackgndColor );
		DrawLED_H( inPen, kLEDScale, xPos + 1, yPos + kLEDScale + 1 );
	}


	// -------------------------------------------------------------------------
	// Message Window

	final void SetMessage( String msg )
	{
		mMessage = msg;

        mMessage3 = null;

		mMessageColor = 7;
		mMessageColorDir = true;

		mMessageDirty = true;
	}

    final void SaveMessageState()
    {
        mSaveMessage = mMessage;
        mSaveMessage3 = mMessage3;
    }

    final void RestoreMessageState()
    {
        mMessage = mSaveMessage;
        mMessage3 = mSaveMessage3;

        mMessageColor = 7;
        mMessageColorDir = true;

        mMessageDirty = true;
    }

	final void SetMessage2( String msg, int timeToShow )
	{
		mMessage2 = msg;

		mMessage2_Color = mTextColor;
		mMessage2_Time = timeToShow;

		mMessageDirty = true;
	}

    final void SetMessage3( String msg )
    {
        mMessage3 = msg;
    }

	final void ColorCycleMessage( int rate )
	{
		if ( mMessageColorDir )
		{
			mMessageColor += rate;
			if ( mMessageColor >= 8 )
			{
				mMessageColor = 7;
				mMessageColorDir = false;
			}
		}
		else
		{
			mMessageColor -= rate;
			if ( mMessageColor <= -1 )
			{
				mMessageColor = 0x0;
				mMessageColorDir = true;
			}
		}
	}

	// --------------------------------	Instance Variables

	Point				mDefense[] = new Point[5];
	int					mDefenseYard[] = new int[5];
	int					mDefenseRow[] = new int[5];
	int					mDefenseColor[] = new int[5];
	Point				mOffense;
	int					mOffenseColor;

	int					mTackler;

	int					mOffenseYard;
	int					mOffenseRow;

	boolean				mHomeHasBall;
	int					mPauseUntil;

	int					mHomeScore;
	int					mVisitorScore;
	int					mClock;
	int					mPreviousClock=0;
	int					mDown;
	int					mFieldPosition;			// goes 0 -> 50 -> 100 (touchdown!)
	int					mYardsToGo;

	int					mQuarter;

    int                 mTenthSecTick;
	int					mClockPrescale;			// # of 10th/sec ticks
	int					mAIPrescale;
	int					mAITicks;

	int					mGameState;
    int                 mPreviousGameState;

    String              mSaveMessage;
    String              mSaveMessage3;

    boolean             mJustKicked;

	String				mMessage;
	int					mMessageColor;
	boolean				mMessageColorDir;
	boolean				mMessageDirty = true;

	String				mMessage2;
	int					mMessage2_Color;
	int					mMessage2_Time;

    String				mMessage3;

    int                 mFieldColor;
    int                 mYardlineColor;
    int                 mDisplaysColor;

    int                 mHomeColor;
    int                 mVisitorColor;
    int                 mTackledColor;
    int                 mTouchdownColor;

    int                 mBackgndColor;
    int                 mTextColor;

    int                 mLEDColor;

    int                 mColorTable[] = new int[8];

	int					mSeed;
	Random				mRnd;

    boolean				mBitmapsReady;
    Bitmap 				mLeftArrow;
    Bitmap              mRightArrow;
    Bitmap              mHomeEndzone;
    Bitmap              mVisitorEndzone;

	int					mBeginningPosition;	// tj - field position at beginning of play
	int					mYardsRun;			// tj - the yardage gained on play

	// constants for drawing playfield
	static final int	kFieldTop = 45;						// 40
	static final int	kFieldBottom = kFieldTop + 84;		// 90
	static final int	kYardSpacing = 20;
	static final int	kYardStart = 29; //26;
	static final int	kTopTickY = 28;
	static final int	kBottomTickY = 58;
	static final int	kTickWidth = 6;

	// player size
	static final int	kBlockWidth = 9;
	static final int	kBlockHeight = 4;

	// constants for player positioning
	static final int	kYard1 = kYardStart + 6;
	static final int	kYard2 = kYard1 + kYardSpacing;
	static final int	kYard3 = kYard2 + kYardSpacing;
	static final int	kYard4 = kYard3 + kYardSpacing;
	static final int	kYard5 = kYard4 + kYardSpacing;
	static final int	kYard6 = kYard5 + kYardSpacing;
	static final int	kYard7 = kYard6 + kYardSpacing;
	static final int	kYard8 = kYard7 + kYardSpacing;
	static final int	kYard9 = kYard8 + kYardSpacing;

	static final int	kRow1 = kFieldTop + 13;
	static final int	kRow2 = kFieldTop + kTopTickY + 13;
	static final int	kRow3 = kFieldTop + kBottomTickY + 13;

	// constants for display positioning
	static final int	kLEDScale = 3;

	static final int	kLEDDigitSpacing = 10;

	static final int	kScoreTop = kFieldTop - 12;
	static final int	kStatusTop = kScoreTop - 12;

	static final int	kHomeScoreLeft = kYardStart;
	static final int	kVisitorScoreLeft = kYard9 - 5;
	static final int	kClockLeft = kYard5 - 14;

	static final int	kDownLeft = kHomeScoreLeft + 10;
	static final int	kYTGLeft = kVisitorScoreLeft;
	static final int	kFieldPosLeft = kClockLeft + 10;

    static final int    kDefaultMessage2Duration = 32;

	// funny 7-segment home/visitor 50-yard-line ball side shapes

	static final int	kOffenseIsHomeSide = 0x31;
	static final int	kOffenseIsVisitorSide = 0x07;

	// constants for timer divisor (prescaler) compares
	static final int	kClockTicks = 10;
	static final int	kAITicks_Beginner = 7;
	static final int	kAITicks_Advanced = 5;
	static final int	kBlinkTicks = 3;

	// Game States
	static final int	kAwaitingFirstMove  = 1;
    static final int    kGameStopped        = 2;
	static final int	kGameRunning        = 3;
	static final int	kOffenseTackled     = 4;
	static final int	kTouchdown          = 5;
	static final int	kKickAftermath      = 6;
	static final int	kGameOver           = 7;
    static final int    kPaused             = 8;

	static final int	kQuarterLength = 150;       // 15 "minutes" == 150 seconds


	// sounds
	static int	sSound_NewGame = Resources.ID_NEW_GAME;
	static int	sSound_Touchdown = Resources.ID_TOUCHDOWN;
	static int	sSound_Tackle = Resources.ID_TACKLE;
	static int	sSound_Punt = Resources.ID_PUNT;
	static int	sSound_Cheers = Resources.ID_CHEERS;
	static int	sSound_ShortWhistle = Resources.ID_SHORT_WHISTLE;
	static int	sSound_LongWhistle = Resources.ID_WHISTLE;
    static int  sSound_Tick = Resources.ID_TICK;
}



// Ripped off from Hints code

class HelpWindow extends DialogWindow
{
    public
    HelpWindow()
    {
        super("" /* title */, APP_DIALOG_STYLE, false /* showCancel */);

        mInstance = this;

        mDoneButton = addTopFrameButton( "Done",
                    kEvent_Done, 0, null,
                    Shortcut.BACK_BUTTON);

        mNextButton = addBottomFrameButton( "Next",
                                           kEvent_Next, 0, null, Shortcut.NONE);
        //mNextButton.setBitmap(Bitmaps.get(BitmapIDs.ID_MENU_SHORTCUT_RIGHT));
        //mNextButton.setBitmapAlignment(Button.ALIGN_RIGHT);

        mPrevButton = addFrameButton(BOTTOM_POSITION3,
                                     "Previous",
                                     kEvent_Previous, 0, null, Shortcut.NONE);
        //mPrevButton.setBitmap(Bitmaps.get(BitmapIDs.ID_MENU_SHORTCUT_LEFT));
        //mPrevButton.setBitmapAlignment(Button.ALIGN_LEFT);

        //mHintTitles = app.getStringArray(kRsrc_HintTitles);

        mHelpCount = mHelpTitles.length;

        //mTitleFormat = app.getString(kRsrc_HintTitleFormat);

        // we have to call hide() ourselves
        setHideOnButtonEvent(false);

        layout();
    }

    static void
    newInstance()
    {
        if (mInstance == null) {
            mInstance = new HelpWindow();
        }
        mInstance.show();
    }

    public void
    hide()
    {
        super.hide();
        mInstance = null;
    }

    void
    layout()
    {
        Application	app = Application.getCurrentApp();

        setTitle( mHelpTitles[mHelpIndex] );

        Bitmap helpBitmap = app.getBitmap( kHelpBitmapIDs[mHelpIndex] );

        if (mHelpImageView == null) {
            mHelpImageView = new ImageView(helpBitmap);
            mHelpImageView.setMargin(4);
            addChild(mHelpImageView);
            mHelpImageView.show();

            setFocusedChild(mNextButton);

            snapToChildren(-2, 0, -2, -2);
        } else {
            mHelpImageView.setImage(helpBitmap);
        }

        if (mHelpIndex > 0) {
            mPrevButton.show();
        } else {
            if (getFocusedChild() == mPrevButton) {
                mNextButton.show();
                setFocusedChild(mNextButton);
            }
            mPrevButton.hide();
        }

        if (mHelpIndex < (mHelpCount - 1)) {
            mNextButton.show();
        } else {
            if (getFocusedChild() == mNextButton) {
                mPrevButton.show();

                setFocusedChild(mDoneButton);
            }
            mNextButton.hide();
        }

        invalidate();
    }

    public boolean
    eventWidgetDown(int inWhichWidget, Event event)
    {
        switch (inWhichWidget) {
        case Event.DEVICE_ARROW_LEFT:
            handlePrevious();
            return true;

        case Event.DEVICE_ARROW_RIGHT:
            handleNext();
            return true;
        }

        return super.eventWidgetDown(inWhichWidget, event);
    }

    public boolean
    receiveEvent(Event inEvent)
    {
        switch (inEvent.type) {
        case kEvent_Previous:
            handlePrevious();
            return true;

        case kEvent_Next:
            handleNext();
            return true;

        case kEvent_Done:
            hide();
            return true;
        }

        return super.receiveEvent(inEvent);
    }

    public boolean
    eventWidgetUp(int widget, Event event)
    {
        // trap the jump button to make sure we get hidden properly
        if (widget == Event.DEVICE_BUTTON_JUMP) {
            hide();
            // don't consume the event, fall through
        }

        return super.eventWidgetUp (widget, event);
    }

    void
    handleNext()
    {
        if (mHelpIndex < (mHelpCount - 1)) {
            mHelpIndex++;
            if (getFocusedChild() == mPrevButton) {
                mNextButton.show();
                setFocusedChild(mNextButton);
            }
            layout();
        }
    }

    void
    handlePrevious()
    {
        if (mHelpIndex > 0) {
            mHelpIndex--;
            if (getFocusedChild() == mNextButton) {
                mPrevButton.show();
                setFocusedChild(mPrevButton);
            }
            layout();
        }
    }

    public void
    drawFrame(Pen p)
    {
        int	x, y;
        Rect r = mPaintRect;

        if (r != null) {
            getBounds(mPaintRect);
        } else {
            r = getBounds();
            mPaintRect = r;
        }

        // Draw the border
        p.setColor(getStyle().getBackgroundColor(false));
        p.fillRoundRect(r, ROUND_RECT_CORNER, ROUND_RECT_CORNER);

        //*	Draw the shadow
        p.setColor(getStyle().getColor(Style.WINDOW_SHADOW_COLOR));
        r.inset(3);
        p.setSize(3, 3);
        p.drawRoundRect(r, ROUND_RECT_CORNER, ROUND_RECT_CORNER);
        p.setSize(1, 1);

        r.left -= 2;
        r.top -= 2;

        //* fill rect with titlebar color first
        p.setColor(getStyle().getColor(Style.DIALOG_TITLE_FRAME_COLOR));
        p.fillRoundRect(r, ROUND_RECT_CORNER, ROUND_RECT_CORNER);

        // draw title
        String title = getTitle();
        if (title != null) {
            x = r.left + LEFT_MARGIN + 2;

            Font font = Font.findBoldOutlineSystemFont();

            p.setFont(font);
            p.setColor(getStyle().getColor(Style.DIALOG_TITLE_TEXT_COLOR));
            p.setTextOutlineColor(getStyle().getColor(Style.DIALOG_TITLE_OUTLINE_COLOR));
            y = r.top + getTitleBarHeight() - font.getDescent() - 4;
            p.drawText(x, y, title);
        }
    }

    //*	--------------------------------	Instance Variables

    int			mHelpIndex;
    int			mHelpCount;

    String[]	mHelpTitles = { "Reading the Display (1/3)",
                                "Reading the Display (2/3)",
                                "Reading the Display (3/3)",
                                "How to Move (1/3)",
                                "How to Move (2/3)",
                                "How to Move (3/3)",
                                "How to Play (1/4)",
                                "How to Play (2/4)",
                                "How to Play (3/4)",
                                "How to Play (4/4)",
                                "Final Tips"
                                };
    String		mTitleFormat;

    ImageView	mHelpImageView;
    Button		mPrevButton;
    Button		mNextButton;
    Button		mDoneButton;

    Rect		mPaintRect;

    //*	--------------------------------	Class Variables

    static		HelpWindow	mInstance;

    //*	--------------------------------	Constants

    static final int	kEvent_Previous		= 100;
    static final int	kEvent_Next			= 101;
    static final int	kEvent_Done			= 102;

    static final int	ROUND_RECT_CORNER	= 9;
    static final int	LEFT_MARGIN			= 5;
    static final int	RIGHT_MARGIN		= 4;

    int[] kHelpBitmapIDs = {
                               Resources.ID_HELP_01,
                               Resources.ID_HELP_02,
                               Resources.ID_HELP_03,
                               Resources.ID_HELP_04,
                               Resources.ID_HELP_05,
                               Resources.ID_HELP_06,
                               Resources.ID_HELP_07,
                               Resources.ID_HELP_08,
                               Resources.ID_HELP_09,
                               Resources.ID_HELP_10,
                               Resources.ID_HELP_11,
                           };
}
