Version of Robotron arcade game using LPC1768, a Gameduino shield, a serial EEPROM (for high scores), two microswitch joysticks and two buttons plus a box to put it in. 20 levels of mayhem.

Dependencies:   25LCxxx_SPI CommonTypes Gameduino mbed

HighScoreEntry.cpp

Committer:
RichardE
Date:
2013-06-16
Revision:
17:194789db2215
Parent:
16:d0b142ba4362
Child:
18:70190f956a24

File content as of revision 17:194789db2215:

/*
 * SOURCE FILE : HighScoreEntry.cpp
 *
 * Definition of class HighScoreEntry.
 * Routine to allow player to enter their name using joysticks.
 *
 */

#include "HighScoreEntry.h"
#include "Gameduino.h"       // Gameduino stuff
#include "GDExtra.h"         // more Gameduino stuff
#include "GDConst.h"         // Gameduino constants
#include "FrameCounter.h"    // counter updated every vertical flyback
#include "CharBlocks.h"      // blocks of characters in program memory
#include "CharFrame.h"       // for drawing frames made of characters
#include "CharCodes.h"       // character codes
#include "CharFrame.h"       // for drawing frames of characters

// Define this for debugging messages on serial port.
#undef CHATTY

#ifdef CHATTY
    extern Serial pc;
#endif

// Grid constants.
#define GRIDX 5
#define GRIDY 16
#define GRIDCOLUMNS 20
#define GRIDROWS 4                  // 3 rows for characters, 1 row for special functions
#define GRIDCOLUMNSPACING 2
#define GRIDROWSPACING 2

// Some text.
const char HighScoreEntry::textDelete[] = "DELETE";
const char HighScoreEntry::textEnter[] = "ENTER";

/***************/
/* CONSTRUCTOR */
/***************/
HighScoreEntry::HighScoreEntry() :
  charIndex( 0 ),
  cursorRow( 0 ),
  cursorColumn( 0 ),
  grid( GRIDROWS )
{
  // Initialise grid around which cursor moves.
  InitialiseGrid();
  #ifdef CHATTY
  for( UInt8 rowNum = 0; rowNum < grid.GetRowCount(); ++rowNum ) {
    FieldRow *row = grid.GetRow( rowNum );
    if( row != (FieldRow*)NULL ) {
        FieldCell *cell = row->GetFirstCell();
        UInt8 columnNum = 0;
        while( cell != (FieldCell*)NULL ) {
            pc.printf(
                "Row %d column %d -> %d,%d,%d,%d\r\n",
                (int)rowNum, (int)columnNum,
                (int)cell->Rect.X1, (int)cell->Rect.Y1, (int)cell->Rect.X2, (int)cell->Rect.Y2
            );
            cell = row->GetNextCell();
            columnNum++;
        }
    }
  }
  #endif
}

/**************/
/* DESTRUCTOR */
/**************/
HighScoreEntry::~HighScoreEntry() {
}

/*******************/
/* INITIALISE GRID */
/*******************/
void HighScoreEntry::InitialiseGrid( void ) {
    // First initialise rows used for characters.
    UInt8 code = PlayerName::MinChar;
    UInt8 x = GRIDX, y = GRIDY;
    UInt8 columnNumber = 0, rowNumber = 0;;
    FieldRow *row = grid.GetRow( rowNumber++ );
    while( code <= PlayerName::MaxChar ) {
        if( row != (FieldRow*)NULL ) {
            FieldCell *cell = new FieldCell();
            cell->Rect.X1 = x - 1;
            cell->Rect.Y1 = y - 1;
            cell->Rect.X2 = x + 1;
            cell->Rect.Y2 = y + 1;
            row->AddCell( cell );
        }
        columnNumber++;
        if( columnNumber >= GRIDCOLUMNS ) {
            columnNumber = 0;
            x = GRIDX;
            y += GRIDROWSPACING;
            row = grid.GetRow( rowNumber++ );
        }
        else {
            x += GRIDCOLUMNSPACING;
        }
        code++;
    }
    // Add another row for special functions.
    columnNumber = 0;
    x = GRIDX;
    y += GRIDROWSPACING;
    row = grid.GetRow( rowNumber++ );
    if( row != (FieldRow*)NULL ) {
        AddTextCell( x, y, row, textDelete );
        AddTextCell( x, y, row, textEnter );
    }
}

/***************************************/
/* ADD A CELL CONTAINING TEXT TO A ROW */
/***************************************/
// Pass character coordinates in x and y. These will be updated.
// Pass row to which cells should be added in row.
// Pass text which is to be contained in cell in text.
void HighScoreEntry::AddTextCell( UInt8 &x, UInt8 &y, FieldRow *row, const char *text ) {
    int len = strlen( text );
    FieldCell *cell = new FieldCell();
    cell->Rect.X1 = x - 1;
    cell->Rect.Y1 = y - 1;
    cell->Rect.X2 = x + len;
    cell->Rect.Y2 = y + 1;
    row->AddCell( cell );
    x += ( len + 1 );
 }

/*********************/
/* GET A PLAYER NAME */
/*********************/
// Pass pointer to place to store name in name.
// Pass pointer to controls to read in controls.
// Pass pointer to Gameduino to display on in gd.
void HighScoreEntry::GetName( PlayerName *name, PanelControls *controls, Gameduino *gd ) {
  UInt16 inputs;
  UInt8 countdown = 0;
  // Initialise name to all 'A' characters.
  for( UInt8 i = 0; i < PlayerName::Length; ++i ) {
    name->Name[ i ] = 'A';
  }
  // Draw screen.
  DrawScreen( gd, name );
  // Wait until player releases all controls.
  WaitControls( gd, controls, false );
  // Loop until player activates done cell.
  bool done = false;
  while( ! done ) {
    // Read panel controls.
    controls->Read();
    inputs = controls->GetInputs();
    // Only react to controls every so often to slow things down.
    if( countdown == 0 ) {
      countdown = 10;
      bool cursorMoved = false;
      // Point to current cell before changing.
      FieldCell *lastCell = grid.GetCellAt( cursorRow, cursorColumn );      
      if( inputs & PanelControls::Up1 ) {
        // Joystick up moves cursor up.
        if( cursorRow > 0 ) {
          cursorRow--;
          ValidateCursorColumn();
          cursorMoved = true;
        }
      }
      else if( inputs & PanelControls::Down1 ) {
        // Joystick down moves cursor down.
        if( cursorRow < grid.GetRowCount() - 1 ) {
          cursorRow++;
          ValidateCursorColumn();
          cursorMoved = true;
        }
      }
      if( inputs & PanelControls::Left1 ) {
        // Joystick left moves cursor left.
        if( cursorColumn > 0 ) {
          cursorColumn--;
          cursorMoved = true;
        }
      }
      else if( inputs & PanelControls::Right1 ) {
        // Joystick right moves cursor right.
        FieldRow *row = grid.GetRow( cursorRow );
        if( ( row != (FieldRow*)NULL ) && ( cursorColumn < row->GetColumnCount() - 1 ) ) {
            cursorColumn++;
            cursorMoved = true;
        }
      }
      if( inputs & PanelControls::Button1 ) {
          // Button 1 adds a character or possibly does something special.
          if( cursorRow < grid.GetRowCount() - 1 ) {
              char newChar = (char)( PlayerName::MinChar + cursorRow * GRIDCOLUMNS + cursorColumn );
              name->Name[ charIndex ] = newChar;
              if( charIndex < PlayerName::Length - 1 ) {
                  charIndex++;
              }
          }
          else {
              // Last row contains special functions.
              SpecialFunction( gd, cursorColumn, name, done );
          }
          // Draw modified name.
          gd->waitvblank();
          DrawName( gd, name );
          // Wait until player releases all controls.
          WaitControls( gd, controls, false );
      }
      // If necessary redraw the cursor.
      if( cursorMoved ) {
          WipeCursor( gd, lastCell );
          DrawCursor( gd, grid.GetCellAt( cursorRow, cursorColumn ) );
      }
    }
    else {
      countdown--;
    }
    // Wait for vertical flyback. Then do animation.
    gd->waitvblank();
    FrameCounter++;
    Animate( gd );
  }
  // Wait until player releases all controls before returning.
  WaitControls( gd, controls, false );
}

/************************************************************************/
/* PERFORM A SPECIAL FUNCTION TRIGGERED FROM A CELL ON LAST ROW OF GRID */
/************************************************************************/
// Pass pointer to Gameduino to draw on in gd.
// Pass function number in funcNum (this is the cursor column number).
// Pass player name in name.
// Pass reference to done flag in done.
void HighScoreEntry::SpecialFunction( Gameduino *gd, UInt8 funcNum, PlayerName *name, bool &done ) {
    switch( funcNum ) {
    
    case 0 :        // DELETE function
        if( charIndex > 0 ) {
            name->Name[ charIndex-- ] = ' ';
        }
        break;
        
    case 1 :        // ENTER function
        done = true;
        break;
        
    }
}

/*********************/
/* WAIT FOR CONTROLS */
/*********************/
// Pass pointer to Gameduino to display on in gd.
// Pass pointer to controls to read in controls.
// Pass true in waitActivate to wait for a control to be used.
// Pass false to wait for release.
void HighScoreEntry::WaitControls( Gameduino *gd, PanelControls *controls, bool waitActivate ) {
  bool released = false;
  UInt16 inputs;
  while( ! released ) {
    controls->Read();
    inputs = controls->GetInputs();
    released = ( waitActivate ? ( inputs != 0 ) : ( inputs == 0 ) );
    if( ! released ) {
      gd->waitvblank();
      FrameCounter++;
      Animate( gd );
    }
  }
}

/*******************/
/* DRAW THE SCREEN */
/*******************/
// Pass pointer to Gameduino to draw on in gd.
// Pass player name in name.
void HighScoreEntry::DrawScreen( Gameduino *gd, PlayerName *name ) {
  gd->waitvblank();
  // Clear the screen to zero characters.
  gd->fill( Gameduino::RAM_PIC, 0, RAM_PIC_SIZE );
  // Turn off all the sprites.
  for( UInt16 s = 0; s < SPRITE_COUNT; ++s ) {
    gd->sprite( s, 0, 400, 0, 0 );
  }
  // Draw border around screen.
  CharFrame::Draw( gd, 0, 0, VISIBLE_CHAR_WIDTH, VISIBLE_CHAR_HEIGHT );
  // Draw instructions.
  gd->putstr( 2, 2,  "   CONGRATULATIONS : YOU HAVE A HIGH SCORE!" );
  gd->putstr( 2, 4,  "PLEASE ENTER YOUR NAME USING THE LEFT JOYSTICK" );
  gd->putstr( 2, 6,  " TO MOVE THE CURSOR. PRESS THE LEFT BUTTON TO" );
  gd->putstr( 2, 8,  "  ENTER EACH LETTER. MOVE CURSOR TO \"ENTER\"" );
  gd->putstr( 2, 10, "     AND PRESS LEFT BUTTON WHEN FINISHED." );
  // Draw player's name.
  DrawName( gd, name );
  // Draw character grid.
  DrawGrid( gd );
  // Draw cursor.
  DrawCursor( gd, grid.GetCellAt( cursorRow, cursorColumn ) );
}

/***************************/
/* DRAW THE CHARACTER GRID */
/***************************/
// Pass pointer to Gameduino to draw on in gd.
void HighScoreEntry::DrawGrid( Gameduino *gd ) {
    // Draw rows containing characters.
    UInt8 code = PlayerName::MinChar;
    UInt8 x = GRIDX, y = GRIDY;
    UInt8 columnNumber = 0;
    char str [] = "X";
    while( code <= PlayerName::MaxChar ) {
        str[ 0 ] = (char)code++;
        gd->putstr( x, y, str );
        columnNumber++;
        if( columnNumber >= GRIDCOLUMNS ) {
            columnNumber = 0;
            x = GRIDX;
            y += GRIDROWSPACING;
        }
        else {
            x += GRIDCOLUMNSPACING;
        }
    }
    // Draw row containing special functions.
    x = GRIDX;
    y += GRIDROWSPACING;
    gd->putstr( x, y, textDelete );
    x += ( strlen( textDelete ) + 1 );
    gd->putstr( x, y, textEnter );
}

/********************************/
/* DRAW THE NAME AND THE CURSOR */
/********************************/
// Pass pointer to Gameduino to draw on in gd.
// Pass player name in name.
void HighScoreEntry::DrawName( Gameduino *gd, PlayerName *name ) {
  gd->putstr( 23, 13, name->Name );
  UInt16 address = Gameduino::RAM_PIC + 14 * SCREEN_CHAR_WIDTH + 23;
  for( UInt8 i = 0; i < PlayerName::Length; ++i ) {
    gd->wr( address, ( i == charIndex ) ? ArrowUp : ' ' );
    address++;
  }
}

/********************/
/* UPDATE ANIMATION */
/********************/
// Pass pointer to Gameduino to display on in gd.
void HighScoreEntry::Animate( Gameduino *gd ) {
}

/*******************/
/* WIPE THE CURSOR */
/*******************/
// Pass pointer to Gameduino to display on in gd.
// Pass cell to draw in cell.
void HighScoreEntry::WipeCursor( Gameduino *gd, FieldCell *cell ) {
    if( cell != (FieldCell*)NULL ) {
        CharFrame::Wipe( gd, cell->Rect.X1, cell->Rect.Y1, cell->Rect.GetWidth(), cell->Rect.GetHeight() );
    }
}

/*******************/
/* DRAW THE CURSOR */
/*******************/
// Pass pointer to Gameduino to display on in gd.
// Pass cell to draw in cell.
void HighScoreEntry::DrawCursor( Gameduino *gd, FieldCell *cell ) {
    if( cell != (FieldCell*)NULL ) {
        CharFrame::Draw( gd, cell->Rect.X1, cell->Rect.Y1, cell->Rect.GetWidth(), cell->Rect.GetHeight() );
    }
}

/**************************/
/* VALIDATE CURSOR COLUMN */
/**************************/
// If cursor column is beyond end of row then forces it back.
void HighScoreEntry::ValidateCursorColumn( void ) {
    FieldRow *row = grid.GetRow( cursorRow );
    if( row != (FieldRow*)NULL ) {
        UInt8 columnCount = row->GetColumnCount();
        if( cursorColumn >= columnCount ) {
            cursorColumn = columnCount - 1;
        }
    }
}